@helia/ipns 10.0.1 → 10.0.2-ef258e7e
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 +3 -7
- package/dist/index.min.js +16 -5
- package/dist/index.min.js.map +4 -4
- package/dist/src/errors.d.ts +0 -8
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +0 -8
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +58 -20
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/ipns/publisher.d.ts.map +1 -1
- package/dist/src/ipns/publisher.js +27 -14
- package/dist/src/ipns/publisher.js.map +1 -1
- package/dist/src/ipns/republisher.d.ts.map +1 -1
- package/dist/src/ipns/republisher.js +22 -9
- package/dist/src/ipns/republisher.js.map +1 -1
- package/dist/src/ipns/resolver.d.ts.map +1 -1
- package/dist/src/ipns/resolver.js +39 -17
- package/dist/src/ipns/resolver.js.map +1 -1
- package/dist/src/ipns.d.ts.map +1 -1
- package/dist/src/ipns.js +8 -8
- package/dist/src/ipns.js.map +1 -1
- package/dist/src/local-store.d.ts +4 -4
- package/dist/src/local-store.d.ts.map +1 -1
- package/dist/src/local-store.js +2 -1
- package/dist/src/local-store.js.map +1 -1
- package/dist/src/pb/ipns.d.ts +29 -29
- package/dist/src/pb/ipns.d.ts.map +1 -1
- package/dist/src/pb/ipns.js +15 -15
- package/dist/src/records.d.ts +27 -142
- package/dist/src/records.d.ts.map +1 -1
- package/dist/src/records.js +39 -46
- package/dist/src/records.js.map +1 -1
- package/dist/src/routing/helia.d.ts +5 -5
- package/dist/src/routing/helia.d.ts.map +1 -1
- package/dist/src/routing/helia.js +3 -3
- package/dist/src/routing/helia.js.map +1 -1
- package/dist/src/routing/index.d.ts +7 -7
- package/dist/src/routing/index.d.ts.map +1 -1
- package/dist/src/routing/index.js +2 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/local-store.d.ts +1 -1
- package/dist/src/routing/local-store.d.ts.map +1 -1
- package/dist/src/routing/local-store.js +3 -3
- package/dist/src/routing/local-store.js.map +1 -1
- package/dist/src/routing/pubsub.d.ts +11 -11
- package/dist/src/routing/pubsub.d.ts.map +1 -1
- package/dist/src/routing/pubsub.js +11 -12
- package/dist/src/routing/pubsub.js.map +1 -1
- package/dist/src/selector.d.ts +2 -2
- package/dist/src/selector.d.ts.map +1 -1
- package/dist/src/selector.js +13 -9
- package/dist/src/selector.js.map +1 -1
- package/dist/src/utils.d.ts +9 -10
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +20 -128
- package/dist/src/utils.js.map +1 -1
- package/dist/src/validator.d.ts +9 -3
- package/dist/src/validator.d.ts.map +1 -1
- package/dist/src/validator.js +57 -14
- package/dist/src/validator.js.map +1 -1
- package/package.json +6 -31
- package/src/errors.ts +0 -10
- package/src/index.ts +75 -25
- package/src/ipns/publisher.ts +27 -14
- package/src/ipns/republisher.ts +31 -13
- package/src/ipns/resolver.ts +39 -21
- package/src/ipns.ts +8 -8
- package/src/local-store.ts +8 -7
- package/src/pb/ipns.proto +1 -1
- package/src/pb/ipns.ts +37 -37
- package/src/records.ts +57 -210
- package/src/routing/helia.ts +6 -6
- package/src/routing/index.ts +7 -7
- package/src/routing/local-store.ts +6 -6
- package/src/routing/pubsub.ts +24 -25
- package/src/selector.ts +14 -11
- package/src/utils.ts +25 -147
- package/src/validator.ts +73 -16
- package/dist/typedoc-urls.json +0 -50
package/src/index.ts
CHANGED
|
@@ -37,7 +37,6 @@
|
|
|
37
37
|
* import { createHelia } from 'helia'
|
|
38
38
|
* import { ipns } from '@helia/ipns'
|
|
39
39
|
* import { unixfs } from '@helia/unixfs'
|
|
40
|
-
* import { generateKeyPair } from '@libp2p/crypto/keys'
|
|
41
40
|
*
|
|
42
41
|
* const helia = await createHelia()
|
|
43
42
|
* const name = ipns(helia)
|
|
@@ -66,7 +65,6 @@
|
|
|
66
65
|
* import { createHelia } from 'helia'
|
|
67
66
|
* import { ipns } from '@helia/ipns'
|
|
68
67
|
* import { unixfs } from '@helia/unixfs'
|
|
69
|
-
* import { generateKeyPair } from '@libp2p/crypto/keys'
|
|
70
68
|
*
|
|
71
69
|
* const helia = await createHelia()
|
|
72
70
|
* const name = ipns(helia)
|
|
@@ -105,15 +103,13 @@
|
|
|
105
103
|
* may fail to be published with "Insufficient peers" errors.
|
|
106
104
|
*
|
|
107
105
|
* ```TypeScript
|
|
108
|
-
* import { ipns } from '@helia/ipns'
|
|
109
|
-
* import { pubsub } from '@helia/ipns/routing'
|
|
106
|
+
* import { ipns, pubSubIPNSRouting } from '@helia/ipns'
|
|
110
107
|
* import { withLibp2p, libp2pDefaults } from '@helia/libp2p'
|
|
111
108
|
* import { unixfs } from '@helia/unixfs'
|
|
112
|
-
* import { generateKeyPair } from '@libp2p/crypto/keys'
|
|
113
109
|
* import { floodsub } from '@libp2p/floodsub'
|
|
114
110
|
* import { createHelia } from 'helia'
|
|
115
111
|
* import type { Helia } from '@helia/interface'
|
|
116
|
-
* import type { PubSub } from '@helia/ipns
|
|
112
|
+
* import type { PubSub } from '@helia/ipns'
|
|
117
113
|
* import type { DefaultLibp2pServices } from '@helia/libp2p'
|
|
118
114
|
* import type { FloodSub } from '@libp2p/floodsub'
|
|
119
115
|
* import type { Libp2p } from '@libp2p/interface'
|
|
@@ -125,7 +121,7 @@
|
|
|
125
121
|
*
|
|
126
122
|
* const name = ipns(helia, {
|
|
127
123
|
* routers: [
|
|
128
|
-
*
|
|
124
|
+
* pubSubIPNSRouting(helia)
|
|
129
125
|
* ]
|
|
130
126
|
* })
|
|
131
127
|
*
|
|
@@ -147,10 +143,10 @@ import { CID } from 'multiformats/cid'
|
|
|
147
143
|
import { IPNSResolver as IPNSResolverClass } from './ipns/resolver.ts'
|
|
148
144
|
import { IPNS as IPNSClass } from './ipns.ts'
|
|
149
145
|
import { localStore } from './local-store.ts'
|
|
150
|
-
import {
|
|
151
|
-
import {
|
|
146
|
+
import { heliaIPNSRouting } from './routing/index.ts'
|
|
147
|
+
import { localStoreIPNSRouting } from './routing/local-store.ts'
|
|
152
148
|
import type { IPNSResolverComponents } from './ipns/resolver.ts'
|
|
153
|
-
import type {
|
|
149
|
+
import type { IPNSEntry } from './pb/ipns.ts'
|
|
154
150
|
import type { IPNSRouting, IPNSRoutingProgressEvents } from './routing/index.ts'
|
|
155
151
|
import type { Routing, HeliaEvents, Keychain, PublicKey } from '@helia/interface'
|
|
156
152
|
import type { ComponentLogger, TypedEventEmitter } from '@libp2p/interface'
|
|
@@ -159,14 +155,29 @@ import type { Datastore } from 'interface-datastore'
|
|
|
159
155
|
import type { MultihashDigest } from 'multiformats/hashes/interface'
|
|
160
156
|
import type { ProgressEvent, ProgressOptions } from 'progress-events'
|
|
161
157
|
|
|
158
|
+
export * from './routing/index.ts'
|
|
159
|
+
export * from './pb/ipns.ts'
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
multihashFromIPNSRoutingKey,
|
|
163
|
+
multihashToIPNSRoutingKey
|
|
164
|
+
} from './utils.ts'
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
createIPNSRecord
|
|
168
|
+
} from './records.ts'
|
|
169
|
+
export type {
|
|
170
|
+
CreateIPNSRecordOptions
|
|
171
|
+
} from './records.ts'
|
|
172
|
+
|
|
162
173
|
export type PublishProgressEvents =
|
|
163
174
|
ProgressEvent<'ipns:publish:start'> |
|
|
164
|
-
ProgressEvent<'ipns:publish:success',
|
|
175
|
+
ProgressEvent<'ipns:publish:success', IPNSEntry> |
|
|
165
176
|
ProgressEvent<'ipns:publish:error', Error>
|
|
166
177
|
|
|
167
178
|
export type ResolveProgressEvents =
|
|
168
179
|
ProgressEvent<'ipns:resolve:start', unknown> |
|
|
169
|
-
ProgressEvent<'ipns:resolve:success',
|
|
180
|
+
ProgressEvent<'ipns:resolve:success', IPNSEntry> |
|
|
170
181
|
ProgressEvent<'ipns:resolve:error', Error>
|
|
171
182
|
|
|
172
183
|
export type DatastoreProgressEvents =
|
|
@@ -177,7 +188,8 @@ export type DatastoreProgressEvents =
|
|
|
177
188
|
|
|
178
189
|
export interface PublishOptions extends AbortOptions, ProgressOptions<PublishProgressEvents | IPNSRoutingProgressEvents> {
|
|
179
190
|
/**
|
|
180
|
-
* Time duration of the signature validity in ms
|
|
191
|
+
* Time duration of the signature validity in ms - after this many ms have
|
|
192
|
+
* expired the record will be invalidated.
|
|
181
193
|
*
|
|
182
194
|
* @default 172_800_000
|
|
183
195
|
*/
|
|
@@ -199,16 +211,44 @@ export interface PublishOptions extends AbortOptions, ProgressOptions<PublishPro
|
|
|
199
211
|
v1Compatible?: boolean
|
|
200
212
|
|
|
201
213
|
/**
|
|
202
|
-
* The TTL of the record in ms
|
|
214
|
+
* The TTL of the record in ms - after this many ms have expired, resolving
|
|
215
|
+
* the record will query the routing for an updated version.
|
|
216
|
+
*
|
|
217
|
+
* Before this many ms have expired any locally stored copy will be treated as
|
|
218
|
+
* the latest version and the routing will not be queried.
|
|
203
219
|
*
|
|
204
220
|
* @default 300_000
|
|
205
221
|
*/
|
|
206
222
|
ttl?: number
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Extensible data that will be added to the IPNS record data and signed to
|
|
226
|
+
* verify it's integrity.
|
|
227
|
+
*
|
|
228
|
+
* Note that this data will be encoded as DAG-CBOR so it must be valid.
|
|
229
|
+
*
|
|
230
|
+
* It is recommended that any custom fields set here are prefixed with `_` to
|
|
231
|
+
* avoid collision with any mandatory fields added to future versions of the
|
|
232
|
+
* IPNS specification.
|
|
233
|
+
*
|
|
234
|
+
* @see https://specs.ipfs.tech/ipns/ipns-record/#extensible-data-dag-cbor
|
|
235
|
+
*/
|
|
236
|
+
data?: Record<string, any>
|
|
207
237
|
}
|
|
208
238
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Extensible data from the record `data` field.
|
|
241
|
+
*
|
|
242
|
+
* The wire format of this data is DAG-CBOR.
|
|
243
|
+
*
|
|
244
|
+
* @see https://specs.ipfs.tech/ipns/ipns-record/#extensible-data-dag-cbor
|
|
245
|
+
*/
|
|
246
|
+
export interface IPNSRecordData extends Record<string, any> {
|
|
247
|
+
Value: Uint8Array<ArrayBuffer>
|
|
248
|
+
Validity: Uint8Array<ArrayBuffer>
|
|
249
|
+
ValidityType: IPNSEntry.ValidityType
|
|
250
|
+
Sequence: bigint
|
|
251
|
+
TTL: bigint
|
|
212
252
|
}
|
|
213
253
|
|
|
214
254
|
export interface IPNSResolveOptions extends AbortOptions, ProgressOptions<ResolveProgressEvents | IPNSRoutingProgressEvents> {
|
|
@@ -225,20 +265,33 @@ export interface IPNSResolveOptions extends AbortOptions, ProgressOptions<Resolv
|
|
|
225
265
|
* @default false
|
|
226
266
|
*/
|
|
227
267
|
nocache?: boolean
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* If true, ensure the record fields and signature are valid. If false, just
|
|
271
|
+
* return the record.
|
|
272
|
+
*
|
|
273
|
+
* @default true
|
|
274
|
+
*/
|
|
275
|
+
validate?: boolean
|
|
228
276
|
}
|
|
229
277
|
|
|
230
278
|
export interface IPNSResolveResult {
|
|
231
279
|
/**
|
|
232
280
|
* The resolved record
|
|
233
281
|
*/
|
|
234
|
-
record:
|
|
282
|
+
record: IPNSEntry
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The value contained within the IPNS record
|
|
286
|
+
*/
|
|
287
|
+
value: string
|
|
235
288
|
}
|
|
236
289
|
|
|
237
290
|
export interface IPNSPublishResult {
|
|
238
291
|
/**
|
|
239
292
|
* The published record
|
|
240
293
|
*/
|
|
241
|
-
record:
|
|
294
|
+
record: IPNSEntry
|
|
242
295
|
|
|
243
296
|
/**
|
|
244
297
|
* The IPNS name that can be used to resolve this record
|
|
@@ -309,7 +362,7 @@ export interface IPNS {
|
|
|
309
362
|
resolve(name: CID | PublicKey | MultihashDigest | string, options?: IPNSResolveOptions): AsyncGenerator<IPNSResolveResult>
|
|
310
363
|
|
|
311
364
|
/**
|
|
312
|
-
* Stop republishing of an IPNS record
|
|
365
|
+
* Stop republishing of an IPNS record.
|
|
313
366
|
*
|
|
314
367
|
* This will delete the last signed IPNS record from the datastore.
|
|
315
368
|
*
|
|
@@ -319,9 +372,6 @@ export interface IPNS {
|
|
|
319
372
|
unpublish(keyName: string, options?: AbortOptions): Promise<void>
|
|
320
373
|
}
|
|
321
374
|
|
|
322
|
-
export type { IPNSRouting } from './routing/index.ts'
|
|
323
|
-
export type { IPNSRecord } from './records.ts'
|
|
324
|
-
|
|
325
375
|
export interface IPNSComponents {
|
|
326
376
|
datastore: Datastore
|
|
327
377
|
routing: Routing
|
|
@@ -366,8 +416,8 @@ export function ipns (components: IPNSComponents, options: IPNSOptions = {}): IP
|
|
|
366
416
|
export function ipnsResolver (components: IPNSResolverComponents, options: IPNSResolverOptions = {}): IPNSResolver {
|
|
367
417
|
const store = localStore(components.datastore, components.logger.forComponent('helia:ipns:local-store'))
|
|
368
418
|
const routers = [
|
|
369
|
-
|
|
370
|
-
|
|
419
|
+
localStoreIPNSRouting(store),
|
|
420
|
+
heliaIPNSRouting(components.routing),
|
|
371
421
|
...(options.routers ?? [])
|
|
372
422
|
]
|
|
373
423
|
|
package/src/ipns/publisher.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { base36 } from 'multiformats/bases/base36'
|
|
2
2
|
import { CustomProgressEvent } from 'progress-events'
|
|
3
|
-
import { DEFAULT_LIFETIME_MS
|
|
3
|
+
import { DEFAULT_LIFETIME_MS } from '../constants.ts'
|
|
4
|
+
import { IPNSEntry } from '../pb/ipns.ts'
|
|
4
5
|
import { createIPNSRecord } from '../records.ts'
|
|
5
|
-
import {
|
|
6
|
+
import { decodeExtensibleData, multihashToIPNSRoutingKey } from '../utils.ts'
|
|
6
7
|
import type { IPNSPublishResult, PublishOptions } from '../index.ts'
|
|
7
8
|
import type { LocalStore } from '../local-store.ts'
|
|
8
9
|
import type { IPNSRouting } from '../routing/index.ts'
|
|
@@ -42,31 +43,43 @@ export class IPNSPublisher {
|
|
|
42
43
|
if (await this.localStore.has(routingKey, options)) {
|
|
43
44
|
// if we have published under this key before, increment the sequence number
|
|
44
45
|
const { record } = await this.localStore.get(routingKey, options)
|
|
45
|
-
const existingRecord =
|
|
46
|
-
|
|
46
|
+
const existingRecord = IPNSEntry.decode(record)
|
|
47
|
+
const data = decodeExtensibleData(existingRecord.data)
|
|
48
|
+
sequenceNumber = (data.Sequence ?? 0n) + 1n
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
// convert ttl from milliseconds to nanoseconds as createIPNSRecord expects
|
|
50
|
-
const ttlNs = options.ttl != null ? BigInt(options.ttl) * 1_000_000n : DEFAULT_TTL_NS
|
|
51
51
|
const lifetime = options.lifetime ?? DEFAULT_LIFETIME_MS
|
|
52
|
-
const record = await createIPNSRecord(key, value, sequenceNumber, lifetime, {
|
|
53
|
-
|
|
52
|
+
const record = await createIPNSRecord(key, value, sequenceNumber, lifetime, {
|
|
53
|
+
...options,
|
|
54
|
+
// convert ttl from milliseconds to nanoseconds as createIPNSRecord expects
|
|
55
|
+
ttlNs: options.ttl != null ? BigInt(options.ttl) * 1_000_000n : undefined
|
|
56
|
+
})
|
|
57
|
+
const marshaledRecord = IPNSEntry.encode(record)
|
|
54
58
|
|
|
55
59
|
if (options.offline === true) {
|
|
56
60
|
// only store record locally
|
|
57
|
-
await this.localStore.put(routingKey, marshaledRecord, {
|
|
61
|
+
await this.localStore.put(routingKey, marshaledRecord, {
|
|
62
|
+
...options,
|
|
63
|
+
metadata: {
|
|
64
|
+
keyName,
|
|
65
|
+
lifetime
|
|
66
|
+
}
|
|
67
|
+
})
|
|
58
68
|
} else {
|
|
59
69
|
// publish record to routing (including the local store)
|
|
60
70
|
await Promise.all(this.routers.map(async r => {
|
|
61
|
-
await r.put(routingKey, marshaledRecord, {
|
|
71
|
+
await r.put(routingKey, marshaledRecord, {
|
|
72
|
+
...options,
|
|
73
|
+
metadata: {
|
|
74
|
+
keyName,
|
|
75
|
+
lifetime
|
|
76
|
+
}
|
|
77
|
+
})
|
|
62
78
|
}))
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
return {
|
|
66
|
-
record
|
|
67
|
-
...record,
|
|
68
|
-
publicKey: key.publicKey
|
|
69
|
-
},
|
|
82
|
+
record,
|
|
70
83
|
name: `/ipns/${key.publicKey.toCID().toString(base36)}`,
|
|
71
84
|
publicKey: key.publicKey
|
|
72
85
|
}
|
package/src/ipns/republisher.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Queue, repeatingTask } from '@libp2p/utils'
|
|
2
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
2
3
|
import { DEFAULT_REPUBLISH_CONCURRENCY, DEFAULT_REPUBLISH_INTERVAL_MS, DEFAULT_TTL_NS } from '../constants.ts'
|
|
4
|
+
import { IPNSEntry } from '../pb/ipns.ts'
|
|
3
5
|
import { createIPNSRecord } from '../records.ts'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import type { IPNSRecord } from '../index.ts'
|
|
6
|
+
import { decodeExtensibleData, shouldRepublish } from '../utils.ts'
|
|
7
|
+
import type { IPNSRecordData } from '../index.ts'
|
|
7
8
|
import type { LocalStore } from '../local-store.ts'
|
|
8
9
|
import type { IPNSRouting } from '../routing/index.ts'
|
|
9
10
|
import type { Keychain, PrivateKey } from '@helia/interface'
|
|
@@ -78,7 +79,7 @@ export class IPNSRepublisher {
|
|
|
78
79
|
})
|
|
79
80
|
|
|
80
81
|
try {
|
|
81
|
-
const recordsToRepublish: Array<{ routingKey: Uint8Array, record:
|
|
82
|
+
const recordsToRepublish: Array<{ routingKey: Uint8Array, record: IPNSEntry }> = []
|
|
82
83
|
let listed = 0
|
|
83
84
|
|
|
84
85
|
// Find all records using the localStore.list method
|
|
@@ -91,21 +92,38 @@ export class IPNSRepublisher {
|
|
|
91
92
|
this.log('no metadata found for record %b, skipping', routingKey)
|
|
92
93
|
continue
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
|
|
96
|
+
let ipnsRecord: IPNSEntry
|
|
97
|
+
|
|
95
98
|
try {
|
|
96
|
-
ipnsRecord =
|
|
97
|
-
} catch (err
|
|
98
|
-
this.log.
|
|
99
|
+
ipnsRecord = IPNSEntry.decode(record)
|
|
100
|
+
} catch (err) {
|
|
101
|
+
this.log.trace('skipping invalid record %b because could not decode', routingKey)
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (ipnsRecord.data == null) {
|
|
106
|
+
this.log.trace('skipping record %b because data was missing', routingKey)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let data: IPNSRecordData
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
data = decodeExtensibleData(ipnsRecord.data)
|
|
114
|
+
} catch {
|
|
115
|
+
this.log.trace('skipping record %b because could not decode data', routingKey)
|
|
99
116
|
continue
|
|
100
117
|
}
|
|
101
118
|
|
|
102
119
|
// Only republish records that are within the DHT or record expiry threshold
|
|
103
|
-
if (!shouldRepublish(
|
|
120
|
+
if (!shouldRepublish(created, new Date(uint8ArrayToString(data.Validity)))) {
|
|
104
121
|
this.log.trace('skipping record %b within republish threshold', routingKey)
|
|
105
122
|
continue
|
|
106
123
|
}
|
|
107
|
-
|
|
108
|
-
const
|
|
124
|
+
|
|
125
|
+
const sequenceNumber = data.Sequence + 1n
|
|
126
|
+
const ttlNs = data.TTL ?? DEFAULT_TTL_NS
|
|
109
127
|
let privKey: PrivateKey
|
|
110
128
|
|
|
111
129
|
try {
|
|
@@ -116,7 +134,7 @@ export class IPNSRepublisher {
|
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
try {
|
|
119
|
-
const updatedRecord = await createIPNSRecord(privKey,
|
|
137
|
+
const updatedRecord = await createIPNSRecord(privKey, uint8ArrayToString(data.Value), sequenceNumber, metadata.lifetime, {
|
|
120
138
|
...options,
|
|
121
139
|
ttlNs
|
|
122
140
|
})
|
|
@@ -137,7 +155,7 @@ export class IPNSRepublisher {
|
|
|
137
155
|
// Add job to queue to republish the record to all routers
|
|
138
156
|
queue.add(async () => {
|
|
139
157
|
try {
|
|
140
|
-
const marshaledRecord =
|
|
158
|
+
const marshaledRecord = IPNSEntry.encode(record)
|
|
141
159
|
await Promise.all(
|
|
142
160
|
this.routers.map(r => r.put(routingKey, marshaledRecord, options))
|
|
143
161
|
)
|
package/src/ipns/resolver.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { base36 } from 'multiformats/bases/base36'
|
|
1
2
|
import { DEFAULT_TTL_NS } from '../constants.ts'
|
|
2
|
-
import { RecordNotFoundError, RecordsFailedValidationError } from '../errors.ts'
|
|
3
|
+
import { InvalidValueError, RecordNotFoundError, RecordsFailedValidationError } from '../errors.ts'
|
|
4
|
+
import { IPNSEntry } from '../pb/ipns.ts'
|
|
3
5
|
import { ipnsSelector } from '../selector.ts'
|
|
4
|
-
import { multihashToIPNSRoutingKey,
|
|
6
|
+
import { multihashToIPNSRoutingKey, normalizeKey, IPNS_STRING_PREFIX, ipnsRecordValueToString, decodeExtensibleData } from '../utils.ts'
|
|
5
7
|
import { ipnsValidator } from '../validator.ts'
|
|
6
|
-
import type {
|
|
8
|
+
import type { IPNSResolveOptions, IPNSResolveResult } from '../index.ts'
|
|
7
9
|
import type { LocalStore } from '../local-store.ts'
|
|
8
10
|
import type { IPNSRouting } from '../routing/index.ts'
|
|
9
11
|
import type { Routing, Keychain } from '@helia/interface'
|
|
@@ -42,22 +44,33 @@ export class IPNSResolver {
|
|
|
42
44
|
while (true) {
|
|
43
45
|
const routingKey = multihashToIPNSRoutingKey(digest)
|
|
44
46
|
const record = await this.#findIpnsRecord(routingKey, options)
|
|
47
|
+
let value = ''
|
|
48
|
+
|
|
49
|
+
if (record.data != null) {
|
|
50
|
+
const data = decodeExtensibleData(record.data)
|
|
51
|
+
value = ipnsRecordValueToString(data.Value)
|
|
52
|
+
} else if (record.value != null) {
|
|
53
|
+
value = ipnsRecordValueToString(record.value)
|
|
54
|
+
} else {
|
|
55
|
+
throw new InvalidValueError(`Record for key ${base36.encode(digest.bytes)} contained no value`)
|
|
56
|
+
}
|
|
45
57
|
|
|
46
58
|
yield {
|
|
47
|
-
record
|
|
59
|
+
record,
|
|
60
|
+
value
|
|
48
61
|
}
|
|
49
62
|
|
|
50
|
-
if (!
|
|
63
|
+
if (!value.startsWith(IPNS_STRING_PREFIX)) {
|
|
51
64
|
// not a recursive record
|
|
52
65
|
break
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
({ digest } = normalizeKey(
|
|
68
|
+
({ digest } = normalizeKey(value))
|
|
56
69
|
}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
async #findIpnsRecord (routingKey: Uint8Array, options: IPNSResolveOptions = {}): Promise<
|
|
60
|
-
const records:
|
|
72
|
+
async #findIpnsRecord (routingKey: Uint8Array, options: IPNSResolveOptions = {}): Promise<IPNSEntry> {
|
|
73
|
+
const records: IPNSEntry[] = []
|
|
61
74
|
const cached = await this.localStore.has(routingKey, options)
|
|
62
75
|
|
|
63
76
|
if (cached) {
|
|
@@ -70,19 +83,23 @@ export class IPNSResolver {
|
|
|
70
83
|
|
|
71
84
|
this.log('record retrieved from cache')
|
|
72
85
|
|
|
73
|
-
// unmarshal the record
|
|
74
|
-
|
|
86
|
+
// unmarshal and validate the record
|
|
87
|
+
let ipnsRecord: IPNSEntry
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
if (options.validate === false) {
|
|
90
|
+
ipnsRecord = IPNSEntry.decode(marshaledIPNSRecord)
|
|
91
|
+
this.log('skipped validation of record')
|
|
92
|
+
} else {
|
|
93
|
+
ipnsRecord = await ipnsValidator(routingKey, marshaledIPNSRecord, this.keychain, options)
|
|
94
|
+
this.log('record was valid')
|
|
95
|
+
}
|
|
80
96
|
|
|
81
97
|
// check the TTL
|
|
98
|
+
const data = decodeExtensibleData(ipnsRecord.data)
|
|
82
99
|
|
|
83
100
|
// IPNS TTL is in nanoseconds, convert to milliseconds, default to one
|
|
84
101
|
// hour
|
|
85
|
-
const ttlMs = Number((
|
|
102
|
+
const ttlMs = Number((data.TTL ?? DEFAULT_TTL_NS) / 1_000_000n)
|
|
86
103
|
const ttlExpires = created.getTime() + ttlMs
|
|
87
104
|
|
|
88
105
|
if (ttlExpires > Date.now()) {
|
|
@@ -138,12 +155,13 @@ export class IPNSResolver {
|
|
|
138
155
|
}
|
|
139
156
|
|
|
140
157
|
try {
|
|
141
|
-
|
|
142
|
-
// and Validity are of valid types and have a value, (3) that CBOR data matches protobuf
|
|
143
|
-
// if it's a V1+V2 record
|
|
144
|
-
const record = await unmarshalIPNSRecord(routingKey, marshaledIPNSRecord, this.keychain, options)
|
|
158
|
+
let record: IPNSEntry
|
|
145
159
|
|
|
146
|
-
|
|
160
|
+
if (options.validate === false) {
|
|
161
|
+
record = IPNSEntry.decode(marshaledIPNSRecord)
|
|
162
|
+
} else {
|
|
163
|
+
record = await ipnsValidator(routingKey, marshaledIPNSRecord, this.keychain, options)
|
|
164
|
+
}
|
|
147
165
|
|
|
148
166
|
records.push(record)
|
|
149
167
|
} catch (err) {
|
|
@@ -164,7 +182,7 @@ export class IPNSResolver {
|
|
|
164
182
|
|
|
165
183
|
const record = records[ipnsSelector(routingKey, records)]
|
|
166
184
|
|
|
167
|
-
await this.localStore.put(routingKey, record
|
|
185
|
+
await this.localStore.put(routingKey, IPNSEntry.encode(record), options)
|
|
168
186
|
|
|
169
187
|
return record
|
|
170
188
|
}
|
package/src/ipns.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { IPNSEntry } from './index.ts'
|
|
2
3
|
import { IPNSPublisher } from './ipns/publisher.ts'
|
|
3
4
|
import { IPNSRepublisher } from './ipns/republisher.ts'
|
|
4
5
|
import { IPNSResolver } from './ipns/resolver.ts'
|
|
5
6
|
import { localStore } from './local-store.ts'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { heliaIPNSRouting } from './routing/helia.ts'
|
|
8
|
+
import { localStoreIPNSRouting } from './routing/local-store.ts'
|
|
8
9
|
import { ipnsSelector } from './selector.ts'
|
|
9
|
-
import { normalizeKey, normalizeValue
|
|
10
|
+
import { normalizeKey, normalizeValue } from './utils.ts'
|
|
10
11
|
import { ipnsValidator } from './validator.ts'
|
|
11
12
|
import type { IPNSComponents, IPNS as IPNSInterface, IPNSOptions, IPNSPublishResult, PublishOptions, IPNSResolveOptions, IPNSResolveResult } from './index.ts'
|
|
12
13
|
import type { LocalStore } from './local-store.ts'
|
|
@@ -31,8 +32,8 @@ export class IPNS implements IPNSInterface, Startable {
|
|
|
31
32
|
this.started = false
|
|
32
33
|
|
|
33
34
|
this.routers = [
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
localStoreIPNSRouting(this.localStore),
|
|
36
|
+
heliaIPNSRouting(components.routing),
|
|
36
37
|
...(init.routers ?? [])
|
|
37
38
|
]
|
|
38
39
|
|
|
@@ -67,14 +68,13 @@ export class IPNS implements IPNSInterface, Startable {
|
|
|
67
68
|
if (isKadDHT(service)) {
|
|
68
69
|
// @ ts-expect-error https://github.com/libp2p/js-libp2p/pull/3506
|
|
69
70
|
service.selectors.ipns = async (key: Uint8Array, values: Uint8Array[]): Promise<number> => {
|
|
70
|
-
const records =
|
|
71
|
+
const records = values.map(buf => IPNSEntry.decode(buf))
|
|
71
72
|
|
|
72
73
|
return ipnsSelector(key, records)
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
service.validators.ipns = async (key: Uint8Array, value: Uint8Array): Promise<void> => {
|
|
76
|
-
|
|
77
|
-
await ipnsValidator(record)
|
|
77
|
+
await ipnsValidator(key, value, this.components.keychain)
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
package/src/local-store.ts
CHANGED
|
@@ -2,14 +2,15 @@ import { Record } from '@libp2p/kad-dht'
|
|
|
2
2
|
import { CustomProgressEvent } from 'progress-events'
|
|
3
3
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
4
4
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
5
|
+
import { withArrayBuffer } from 'uint8arrays/with-array-buffer'
|
|
5
6
|
import { IPNSPublishMetadata } from './pb/metadata.ts'
|
|
6
7
|
import { dhtRoutingKey, DHT_RECORD_PREFIX, ipnsMetadataKey } from './utils.ts'
|
|
7
|
-
import type { DatastoreProgressEvents,
|
|
8
|
+
import type { DatastoreProgressEvents, IPNSRoutingGetOptions, IPNSRoutingPutOptions } from './routing/index.ts'
|
|
8
9
|
import type { AbortOptions, Logger } from '@libp2p/interface'
|
|
9
10
|
import type { Datastore } from 'interface-datastore'
|
|
10
11
|
|
|
11
12
|
export interface GetResult {
|
|
12
|
-
record: Uint8Array
|
|
13
|
+
record: Uint8Array<ArrayBuffer>
|
|
13
14
|
created: Date
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -32,8 +33,8 @@ export interface LocalStore {
|
|
|
32
33
|
* @param marshaledRecord - The marshaled IPNS record
|
|
33
34
|
* @param options - options for the put operation including metadata
|
|
34
35
|
*/
|
|
35
|
-
put(routingKey: Uint8Array, marshaledRecord: Uint8Array, options?:
|
|
36
|
-
get(routingKey: Uint8Array, options?:
|
|
36
|
+
put(routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: IPNSRoutingPutOptions): Promise<void>
|
|
37
|
+
get(routingKey: Uint8Array, options?: IPNSRoutingGetOptions): Promise<GetResult>
|
|
37
38
|
has(routingKey: Uint8Array, options?: AbortOptions): Promise<boolean>
|
|
38
39
|
delete(routingKey: Uint8Array, options?: AbortOptions): Promise<void>
|
|
39
40
|
/**
|
|
@@ -50,7 +51,7 @@ export interface LocalStore {
|
|
|
50
51
|
*/
|
|
51
52
|
export function localStore (datastore: Datastore, log: Logger): LocalStore {
|
|
52
53
|
return {
|
|
53
|
-
async put (routingKey: Uint8Array, marshalledRecord: Uint8Array, options:
|
|
54
|
+
async put (routingKey: Uint8Array, marshalledRecord: Uint8Array, options: IPNSRoutingPutOptions = {}) {
|
|
54
55
|
try {
|
|
55
56
|
const key = dhtRoutingKey(routingKey)
|
|
56
57
|
|
|
@@ -86,7 +87,7 @@ export function localStore (datastore: Datastore, log: Logger): LocalStore {
|
|
|
86
87
|
throw err
|
|
87
88
|
}
|
|
88
89
|
},
|
|
89
|
-
async get (routingKey: Uint8Array, options:
|
|
90
|
+
async get (routingKey: Uint8Array, options: IPNSRoutingGetOptions = {}): Promise<GetResult> {
|
|
90
91
|
try {
|
|
91
92
|
const key = dhtRoutingKey(routingKey)
|
|
92
93
|
|
|
@@ -97,7 +98,7 @@ export function localStore (datastore: Datastore, log: Logger): LocalStore {
|
|
|
97
98
|
const record = Record.deserialize(buf)
|
|
98
99
|
|
|
99
100
|
return {
|
|
100
|
-
record: record.value,
|
|
101
|
+
record: withArrayBuffer(record.value),
|
|
101
102
|
created: record.timeReceived
|
|
102
103
|
}
|
|
103
104
|
} catch (err: any) {
|