@helia/ipns 9.2.1-f6cc7640 → 10.0.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 +21 -57
- package/dist/index.min.js +5 -16
- package/dist/index.min.js.map +4 -4
- package/dist/src/errors.d.ts +33 -5
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +33 -20
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +62 -99
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +21 -60
- package/dist/src/index.js.map +1 -1
- package/dist/src/ipns/publisher.d.ts +5 -9
- package/dist/src/ipns/publisher.d.ts.map +1 -1
- package/dist/src/ipns/publisher.js +30 -26
- package/dist/src/ipns/publisher.js.map +1 -1
- package/dist/src/ipns/republisher.d.ts +3 -5
- package/dist/src/ipns/republisher.d.ts.map +1 -1
- package/dist/src/ipns/republisher.js +18 -9
- package/dist/src/ipns/republisher.js.map +1 -1
- package/dist/src/ipns/resolver.d.ts +6 -5
- package/dist/src/ipns/resolver.d.ts.map +1 -1
- package/dist/src/ipns/resolver.js +32 -79
- package/dist/src/ipns/resolver.js.map +1 -1
- package/dist/src/ipns.d.ts +6 -4
- package/dist/src/ipns.d.ts.map +1 -1
- package/dist/src/ipns.js +33 -4
- package/dist/src/ipns.js.map +1 -1
- package/dist/src/pb/ipns.d.ts +62 -0
- package/dist/src/pb/ipns.d.ts.map +1 -0
- package/dist/src/pb/ipns.js +203 -0
- package/dist/src/pb/ipns.js.map +1 -0
- package/dist/src/records.d.ts +155 -0
- package/dist/src/records.d.ts.map +1 -0
- package/dist/src/records.js +88 -0
- package/dist/src/records.js.map +1 -0
- package/dist/src/routing/pubsub.d.ts +3 -0
- package/dist/src/routing/pubsub.d.ts.map +1 -1
- package/dist/src/routing/pubsub.js +15 -11
- package/dist/src/routing/pubsub.js.map +1 -1
- package/dist/src/selector.d.ts +14 -0
- package/dist/src/selector.d.ts.map +1 -0
- package/dist/src/selector.js +47 -0
- package/dist/src/selector.js.map +1 -0
- package/dist/src/utils.d.ts +29 -2
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +308 -0
- package/dist/src/utils.js.map +1 -1
- package/dist/src/validator.d.ts +18 -0
- package/dist/src/validator.d.ts.map +1 -0
- package/dist/src/validator.js +58 -0
- package/dist/src/validator.js.map +1 -0
- package/dist/typedoc-urls.json +49 -0
- package/package.json +16 -15
- package/src/errors.ts +40 -25
- package/src/index.ts +63 -100
- package/src/ipns/publisher.ts +34 -33
- package/src/ipns/republisher.ts +24 -13
- package/src/ipns/resolver.ts +40 -89
- package/src/ipns.ts +44 -7
- package/src/pb/ipns.proto +39 -0
- package/src/pb/ipns.ts +280 -0
- package/src/records.ts +273 -0
- package/src/routing/pubsub.ts +17 -11
- package/src/selector.ts +55 -0
- package/src/utils.ts +371 -2
- package/src/validator.ts +67 -0
package/src/ipns/republisher.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { Queue, repeatingTask } from '@libp2p/utils'
|
|
2
|
-
import { createIPNSRecord, marshalIPNSRecord, unmarshalIPNSRecord } from 'ipns'
|
|
3
2
|
import { DEFAULT_REPUBLISH_CONCURRENCY, DEFAULT_REPUBLISH_INTERVAL_MS, DEFAULT_TTL_NS } from '../constants.ts'
|
|
3
|
+
import { createIPNSRecord } from '../records.ts'
|
|
4
|
+
import { marshalIPNSRecord, unmarshalIPNSRecord } from '../utils.ts'
|
|
4
5
|
import { shouldRepublish } from '../utils.ts'
|
|
6
|
+
import type { IPNSRecord } from '../index.ts'
|
|
5
7
|
import type { LocalStore } from '../local-store.ts'
|
|
6
8
|
import type { IPNSRouting } from '../routing/index.ts'
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
9
|
+
import type { Keychain, PrivateKey } from '@helia/interface'
|
|
10
|
+
import type { AbortOptions, ComponentLogger, Logger } from '@libp2p/interface'
|
|
9
11
|
import type { RepeatingTask } from '@libp2p/utils'
|
|
10
|
-
import type { IPNSRecord } from 'ipns'
|
|
11
12
|
|
|
12
13
|
export interface IPNSRepublisherComponents {
|
|
13
14
|
logger: ComponentLogger
|
|
14
|
-
|
|
15
|
+
keychain: Keychain
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface IPNSRepublisherInit {
|
|
@@ -33,9 +34,9 @@ export class IPNSRepublisher {
|
|
|
33
34
|
constructor (components: IPNSRepublisherComponents, init: IPNSRepublisherInit) {
|
|
34
35
|
this.log = components.logger.forComponent('helia:ipns')
|
|
35
36
|
this.localStore = init.localStore
|
|
36
|
-
this.keychain = components.
|
|
37
|
+
this.keychain = components.keychain
|
|
37
38
|
this.republishConcurrency = init.republishConcurrency || DEFAULT_REPUBLISH_CONCURRENCY
|
|
38
|
-
this.started =
|
|
39
|
+
this.started = false
|
|
39
40
|
this.routers = init.routers ?? []
|
|
40
41
|
|
|
41
42
|
this.republishTask = repeatingTask(this.#republish.bind(this), init.republishInterval ?? DEFAULT_REPUBLISH_INTERVAL_MS, {
|
|
@@ -78,18 +79,21 @@ export class IPNSRepublisher {
|
|
|
78
79
|
|
|
79
80
|
try {
|
|
80
81
|
const recordsToRepublish: Array<{ routingKey: Uint8Array, record: IPNSRecord }> = []
|
|
82
|
+
let listed = 0
|
|
81
83
|
|
|
82
84
|
// Find all records using the localStore.list method
|
|
83
85
|
for await (const { routingKey, record, metadata, created } of this.localStore.list(options)) {
|
|
86
|
+
listed++
|
|
87
|
+
|
|
84
88
|
if (metadata == null) {
|
|
85
89
|
// Skip if no metadata is found from before we started
|
|
86
90
|
// storing metadata or for records republished without a key
|
|
87
|
-
this.log(
|
|
91
|
+
this.log('no metadata found for record %b, skipping', routingKey)
|
|
88
92
|
continue
|
|
89
93
|
}
|
|
90
94
|
let ipnsRecord: IPNSRecord
|
|
91
95
|
try {
|
|
92
|
-
ipnsRecord = unmarshalIPNSRecord(record)
|
|
96
|
+
ipnsRecord = await unmarshalIPNSRecord(routingKey, record, this.keychain, options)
|
|
93
97
|
} catch (err: any) {
|
|
94
98
|
this.log.error('error unmarshaling record - %e', err)
|
|
95
99
|
continue
|
|
@@ -97,7 +101,7 @@ export class IPNSRepublisher {
|
|
|
97
101
|
|
|
98
102
|
// Only republish records that are within the DHT or record expiry threshold
|
|
99
103
|
if (!shouldRepublish(ipnsRecord, created)) {
|
|
100
|
-
this.log.trace(
|
|
104
|
+
this.log.trace('skipping record %b within republish threshold', routingKey)
|
|
101
105
|
continue
|
|
102
106
|
}
|
|
103
107
|
const sequenceNumber = ipnsRecord.sequence + 1n
|
|
@@ -110,16 +114,23 @@ export class IPNSRepublisher {
|
|
|
110
114
|
this.log.error('missing key %s, skipping republishing record - %e', metadata.keyName, err)
|
|
111
115
|
continue
|
|
112
116
|
}
|
|
117
|
+
|
|
113
118
|
try {
|
|
114
|
-
const updatedRecord = await createIPNSRecord(privKey, ipnsRecord.value, sequenceNumber, metadata.lifetime, {
|
|
115
|
-
|
|
119
|
+
const updatedRecord = await createIPNSRecord(privKey, ipnsRecord.value, sequenceNumber, metadata.lifetime, {
|
|
120
|
+
...options,
|
|
121
|
+
ttlNs
|
|
122
|
+
})
|
|
123
|
+
recordsToRepublish.push({
|
|
124
|
+
routingKey,
|
|
125
|
+
record: updatedRecord
|
|
126
|
+
})
|
|
116
127
|
} catch (err: any) {
|
|
117
128
|
this.log.error('error creating updated IPNS record for %s - %e', routingKey, err)
|
|
118
129
|
continue
|
|
119
130
|
}
|
|
120
131
|
}
|
|
121
132
|
|
|
122
|
-
this.log(`found ${recordsToRepublish.length} records to republish`)
|
|
133
|
+
this.log(`found ${recordsToRepublish.length}/${listed} records to republish`)
|
|
123
134
|
|
|
124
135
|
// Republish each record
|
|
125
136
|
for (const { routingKey, record } of recordsToRepublish) {
|
package/src/ipns/resolver.ts
CHANGED
|
@@ -1,33 +1,21 @@
|
|
|
1
|
-
import { isPeerId, isPublicKey } from '@libp2p/interface'
|
|
2
|
-
import { multihashToIPNSRoutingKey, unmarshalIPNSRecord } from 'ipns'
|
|
3
|
-
import { ipnsSelector } from 'ipns/selector'
|
|
4
|
-
import { ipnsValidator } from 'ipns/validator'
|
|
5
|
-
import { base36 } from 'multiformats/bases/base36'
|
|
6
|
-
import { base58btc } from 'multiformats/bases/base58'
|
|
7
|
-
import { CID } from 'multiformats/cid'
|
|
8
|
-
import * as Digest from 'multiformats/hashes/digest'
|
|
9
1
|
import { DEFAULT_TTL_NS } from '../constants.ts'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
2
|
+
import { RecordNotFoundError, RecordsFailedValidationError } from '../errors.ts'
|
|
3
|
+
import { ipnsSelector } from '../selector.ts'
|
|
4
|
+
import { multihashToIPNSRoutingKey, unmarshalIPNSRecord, normalizeKey, IPNS_STRING_PREFIX } from '../utils.ts'
|
|
5
|
+
import { ipnsValidator } from '../validator.ts'
|
|
6
|
+
import type { IPNSRecord, ResolveOptions, ResolveResult } from '../index.ts'
|
|
13
7
|
import type { LocalStore } from '../local-store.ts'
|
|
14
8
|
import type { IPNSRouting } from '../routing/index.ts'
|
|
15
|
-
import type { Routing } from '@helia/interface'
|
|
16
|
-
import type { ComponentLogger, Logger
|
|
9
|
+
import type { Routing, Keychain } from '@helia/interface'
|
|
10
|
+
import type { ComponentLogger, Logger } from '@libp2p/interface'
|
|
17
11
|
import type { Datastore } from 'interface-datastore'
|
|
18
|
-
import type { IPNSRecord } from 'ipns'
|
|
19
|
-
import type { MultibaseDecoder } from 'multiformats/bases/interface'
|
|
20
12
|
import type { MultihashDigest } from 'multiformats/hashes/interface'
|
|
21
13
|
|
|
22
|
-
const bases: Record<string, MultibaseDecoder<string>> = {
|
|
23
|
-
[base36.prefix]: base36,
|
|
24
|
-
[base58btc.prefix]: base58btc
|
|
25
|
-
}
|
|
26
|
-
|
|
27
14
|
export interface IPNSResolverComponents {
|
|
28
15
|
datastore: Datastore
|
|
29
16
|
routing: Routing
|
|
30
17
|
logger: ComponentLogger
|
|
18
|
+
keychain: Keychain
|
|
31
19
|
}
|
|
32
20
|
|
|
33
21
|
export interface IPNResolverInit {
|
|
@@ -39,81 +27,37 @@ export class IPNSResolver {
|
|
|
39
27
|
public readonly routers: IPNSRouting[]
|
|
40
28
|
private readonly localStore: LocalStore
|
|
41
29
|
private readonly log: Logger
|
|
30
|
+
private keychain: Keychain
|
|
42
31
|
|
|
43
32
|
constructor (components: IPNSResolverComponents, init: IPNResolverInit) {
|
|
44
33
|
this.log = components.logger.forComponent('helia:ipns')
|
|
45
34
|
this.localStore = init.localStore
|
|
46
35
|
this.routers = init.routers
|
|
36
|
+
this.keychain = components.keychain
|
|
47
37
|
}
|
|
48
38
|
|
|
49
|
-
async resolve (key:
|
|
50
|
-
|
|
51
|
-
// @ts-expect-error @libp2p/peer-id needs new multiformats
|
|
52
|
-
const routingKey = multihashToIPNSRoutingKey(digest)
|
|
53
|
-
const record = await this.#findIpnsRecord(routingKey, options)
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
...(await this.#resolve(record.value, options)),
|
|
57
|
-
record
|
|
58
|
-
}
|
|
59
|
-
}
|
|
39
|
+
async * resolve (key: MultihashDigest, options: ResolveOptions = {}): AsyncGenerator<ResolveResult> {
|
|
40
|
+
let { digest } = normalizeKey(key)
|
|
60
41
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const scheme = parts[1]
|
|
65
|
-
|
|
66
|
-
if (scheme === 'ipns') {
|
|
67
|
-
const str = parts[2]
|
|
68
|
-
const prefix = str.substring(0, 1)
|
|
69
|
-
let buf: Uint8Array | undefined
|
|
70
|
-
|
|
71
|
-
if (prefix === '1' || prefix === 'Q') {
|
|
72
|
-
buf = base58btc.decode(`z${str}`)
|
|
73
|
-
} else if (bases[prefix] != null) {
|
|
74
|
-
buf = bases[prefix].decode(str)
|
|
75
|
-
} else {
|
|
76
|
-
throw new UnsupportedMultibasePrefixError(`Unsupported multibase prefix "${prefix}"`)
|
|
77
|
-
}
|
|
42
|
+
while (true) {
|
|
43
|
+
const routingKey = multihashToIPNSRoutingKey(digest)
|
|
44
|
+
const record = await this.#findIpnsRecord(routingKey, options)
|
|
78
45
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
digest = Digest.decode(buf)
|
|
83
|
-
} catch {
|
|
84
|
-
digest = CID.decode(buf).multihash
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!isCodec(digest, IDENTITY_CODEC) && !isCodec(digest, SHA2_256_CODEC)) {
|
|
88
|
-
throw new UnsupportedMultihashCodecError(`Unsupported multihash codec "${digest.code}"`)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const { cid } = await this.resolve(digest, options)
|
|
92
|
-
const path = parts.slice(3).join('/')
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
cid,
|
|
96
|
-
path: path === '' ? undefined : path
|
|
97
|
-
}
|
|
98
|
-
} else if (scheme === 'ipfs') {
|
|
99
|
-
const cid = CID.parse(parts[2])
|
|
100
|
-
const path = parts.slice(3).join('/')
|
|
46
|
+
yield {
|
|
47
|
+
record
|
|
48
|
+
}
|
|
101
49
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
50
|
+
if (!record.value.startsWith(IPNS_STRING_PREFIX)) {
|
|
51
|
+
// not a recursive record
|
|
52
|
+
break
|
|
106
53
|
}
|
|
107
|
-
} catch (err) {
|
|
108
|
-
this.log.error('error parsing ipfs path - %e', err)
|
|
109
|
-
}
|
|
110
54
|
|
|
111
|
-
|
|
112
|
-
|
|
55
|
+
({ digest } = normalizeKey(record.value))
|
|
56
|
+
}
|
|
113
57
|
}
|
|
114
58
|
|
|
115
59
|
async #findIpnsRecord (routingKey: Uint8Array, options: ResolveOptions = {}): Promise<IPNSRecord> {
|
|
116
|
-
const records:
|
|
60
|
+
const records: IPNSRecord[] = []
|
|
117
61
|
const cached = await this.localStore.has(routingKey, options)
|
|
118
62
|
|
|
119
63
|
if (cached) {
|
|
@@ -122,17 +66,19 @@ export class IPNSResolver {
|
|
|
122
66
|
if (options.nocache !== true) {
|
|
123
67
|
try {
|
|
124
68
|
// check the local cache first
|
|
125
|
-
const { record, created } = await this.localStore.get(routingKey, options)
|
|
69
|
+
const { record: marshaledIPNSRecord, created } = await this.localStore.get(routingKey, options)
|
|
126
70
|
|
|
127
71
|
this.log('record retrieved from cache')
|
|
128
72
|
|
|
73
|
+
// unmarshal the record
|
|
74
|
+
const ipnsRecord = await unmarshalIPNSRecord(routingKey, marshaledIPNSRecord, this.keychain, options)
|
|
75
|
+
|
|
129
76
|
// validate the record
|
|
130
|
-
await ipnsValidator(
|
|
77
|
+
await ipnsValidator(ipnsRecord, options)
|
|
131
78
|
|
|
132
79
|
this.log('record was valid')
|
|
133
80
|
|
|
134
81
|
// check the TTL
|
|
135
|
-
const ipnsRecord = unmarshalIPNSRecord(record)
|
|
136
82
|
|
|
137
83
|
// IPNS TTL is in nanoseconds, convert to milliseconds, default to one
|
|
138
84
|
// hour
|
|
@@ -156,7 +102,7 @@ export class IPNSResolver {
|
|
|
156
102
|
// add the local record to our list of resolved record, and also
|
|
157
103
|
// search the routing for updates - the most up to date record will be
|
|
158
104
|
// returned
|
|
159
|
-
records.push(
|
|
105
|
+
records.push(ipnsRecord)
|
|
160
106
|
} catch (err) {
|
|
161
107
|
this.log('cached record was invalid - %e', err)
|
|
162
108
|
await this.localStore.delete(routingKey, options)
|
|
@@ -177,10 +123,10 @@ export class IPNSResolver {
|
|
|
177
123
|
|
|
178
124
|
await Promise.all(
|
|
179
125
|
this.routers.map(async (router) => {
|
|
180
|
-
let
|
|
126
|
+
let marshaledIPNSRecord: Uint8Array
|
|
181
127
|
|
|
182
128
|
try {
|
|
183
|
-
|
|
129
|
+
marshaledIPNSRecord = await router.get(routingKey, {
|
|
184
130
|
...options,
|
|
185
131
|
validate: false
|
|
186
132
|
})
|
|
@@ -192,7 +138,12 @@ export class IPNSResolver {
|
|
|
192
138
|
}
|
|
193
139
|
|
|
194
140
|
try {
|
|
195
|
-
|
|
141
|
+
// unmarshal ensures that (1) SignatureV2 and Data are present, (2) that ValidityType
|
|
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)
|
|
145
|
+
|
|
146
|
+
await ipnsValidator(record, options)
|
|
196
147
|
|
|
197
148
|
records.push(record)
|
|
198
149
|
} catch (err) {
|
|
@@ -213,8 +164,8 @@ export class IPNSResolver {
|
|
|
213
164
|
|
|
214
165
|
const record = records[ipnsSelector(routingKey, records)]
|
|
215
166
|
|
|
216
|
-
await this.localStore.put(routingKey, record, options)
|
|
167
|
+
await this.localStore.put(routingKey, record.bytes, options)
|
|
217
168
|
|
|
218
|
-
return
|
|
169
|
+
return record
|
|
219
170
|
}
|
|
220
171
|
}
|
package/src/ipns.ts
CHANGED
|
@@ -5,10 +5,15 @@ import { IPNSResolver } from './ipns/resolver.ts'
|
|
|
5
5
|
import { localStore } from './local-store.ts'
|
|
6
6
|
import { helia } from './routing/helia.ts'
|
|
7
7
|
import { localStoreRouting } from './routing/local-store.ts'
|
|
8
|
-
import
|
|
8
|
+
import { ipnsSelector } from './selector.ts'
|
|
9
|
+
import { normalizeKey, normalizeValue, unmarshalIPNSRecord } from './utils.ts'
|
|
10
|
+
import { ipnsValidator } from './validator.ts'
|
|
11
|
+
import type { IPNSComponents, IPNS as IPNSInterface, IPNSOptions, PublishResult, PublishOptions, ResolveOptions, ResolveResult } from './index.ts'
|
|
9
12
|
import type { LocalStore } from './local-store.ts'
|
|
10
13
|
import type { IPNSRouting } from './routing/index.ts'
|
|
11
|
-
import type {
|
|
14
|
+
import type { PublicKey } from '@helia/interface'
|
|
15
|
+
import type { AbortOptions, Libp2p, Startable } from '@libp2p/interface'
|
|
16
|
+
import type { ValidateFn, SelectFn } from '@libp2p/kad-dht'
|
|
12
17
|
import type { MultihashDigest } from 'multiformats/hashes/interface'
|
|
13
18
|
|
|
14
19
|
export class IPNS implements IPNSInterface, Startable {
|
|
@@ -17,11 +22,13 @@ export class IPNS implements IPNSInterface, Startable {
|
|
|
17
22
|
private readonly republisher: IPNSRepublisher
|
|
18
23
|
private readonly resolver: IPNSResolver
|
|
19
24
|
private readonly localStore: LocalStore
|
|
25
|
+
private readonly components: IPNSComponents
|
|
20
26
|
private started: boolean
|
|
21
27
|
|
|
22
28
|
constructor (components: IPNSComponents, init: IPNSOptions = {}) {
|
|
23
29
|
this.localStore = localStore(components.datastore, components.logger.forComponent('helia:ipns:local-store'))
|
|
24
|
-
this.
|
|
30
|
+
this.components = components
|
|
31
|
+
this.started = false
|
|
25
32
|
|
|
26
33
|
this.routers = [
|
|
27
34
|
localStoreRouting(this.localStore),
|
|
@@ -53,6 +60,26 @@ export class IPNS implements IPNSInterface, Startable {
|
|
|
53
60
|
if (this.started) {
|
|
54
61
|
this.republisher.start()
|
|
55
62
|
}
|
|
63
|
+
|
|
64
|
+
for (const component of Object.values(this.components)) {
|
|
65
|
+
if (isLibp2p(component)) {
|
|
66
|
+
for (const service of Object.values(component.services)) {
|
|
67
|
+
if (isKadDHT(service)) {
|
|
68
|
+
// @ ts-expect-error https://github.com/libp2p/js-libp2p/pull/3506
|
|
69
|
+
service.selectors.ipns = async (key: Uint8Array, values: Uint8Array[]): Promise<number> => {
|
|
70
|
+
const records = await Promise.all(values.map(buf => unmarshalIPNSRecord(key, buf, this.components.keychain)))
|
|
71
|
+
|
|
72
|
+
return ipnsSelector(key, records)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
service.validators.ipns = async (key: Uint8Array, value: Uint8Array): Promise<void> => {
|
|
76
|
+
const record = await unmarshalIPNSRecord(key, value, this.components.keychain)
|
|
77
|
+
await ipnsValidator(record)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
56
83
|
}
|
|
57
84
|
|
|
58
85
|
start (): void {
|
|
@@ -73,15 +100,25 @@ export class IPNS implements IPNSInterface, Startable {
|
|
|
73
100
|
this.republisher.stop()
|
|
74
101
|
}
|
|
75
102
|
|
|
76
|
-
async publish (keyName: string, value:
|
|
77
|
-
return this.publisher.publish(keyName, value, options)
|
|
103
|
+
async publish (keyName: string, value: PublicKey | CID | MultihashDigest | string, options: PublishOptions = {}): Promise<PublishResult> {
|
|
104
|
+
return this.publisher.publish(keyName, normalizeValue(value), options)
|
|
78
105
|
}
|
|
79
106
|
|
|
80
|
-
async resolve (key: CID<unknown, 0x72
|
|
81
|
-
|
|
107
|
+
async * resolve (key: PublicKey | CID<unknown, 0x72> | MultihashDigest | string, options: ResolveOptions = {}): AsyncGenerator<ResolveResult> {
|
|
108
|
+
const { digest } = normalizeKey(key)
|
|
109
|
+
|
|
110
|
+
yield * this.resolver.resolve(digest, options)
|
|
82
111
|
}
|
|
83
112
|
|
|
84
113
|
async unpublish (keyName: string, options?: AbortOptions): Promise<void> {
|
|
85
114
|
return this.publisher.unpublish(keyName, options)
|
|
86
115
|
}
|
|
87
116
|
}
|
|
117
|
+
|
|
118
|
+
function isLibp2p (obj?: any): obj is Libp2p {
|
|
119
|
+
return obj?.services != null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isKadDHT (obj?: any): obj is { validators: Record<string, ValidateFn>, selectors: Record<string, SelectFn> } {
|
|
123
|
+
return obj?.validators != null && obj?.selectors != null
|
|
124
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// https://github.com/ipfs/boxo/blob/main/ipns/pb/record.proto
|
|
2
|
+
|
|
3
|
+
syntax = "proto3";
|
|
4
|
+
|
|
5
|
+
message IpnsEntry {
|
|
6
|
+
enum ValidityType {
|
|
7
|
+
// setting an EOL says "this record is valid until..."
|
|
8
|
+
EOL = 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// legacy V1 copy of data[Value]
|
|
12
|
+
optional bytes value = 1;
|
|
13
|
+
|
|
14
|
+
// legacy V1 field, verify 'signatureV2' instead
|
|
15
|
+
optional bytes signatureV1 = 2;
|
|
16
|
+
|
|
17
|
+
// legacy V1 copies of data[ValidityType] and data[Validity]
|
|
18
|
+
optional ValidityType validityType = 3;
|
|
19
|
+
optional bytes validity = 4;
|
|
20
|
+
|
|
21
|
+
// legacy V1 copy of data[Sequence]
|
|
22
|
+
optional uint64 sequence = 5;
|
|
23
|
+
|
|
24
|
+
// legacy V1 copy copy of data[TTL]
|
|
25
|
+
optional uint64 ttl = 6;
|
|
26
|
+
|
|
27
|
+
// Optional Public Key to be used for signature verification.
|
|
28
|
+
// Used for big keys such as old RSA keys. Including the public key as part of
|
|
29
|
+
// the record itself makes it verifiable in offline mode, without any additional lookup.
|
|
30
|
+
// For newer Ed25519 keys, the public key is small enough that it can be embedded in the
|
|
31
|
+
// IPNS Name itself, making this field unnecessary.
|
|
32
|
+
optional bytes publicKey = 7;
|
|
33
|
+
|
|
34
|
+
// (mandatory V2) signature of the IPNS record
|
|
35
|
+
optional bytes signatureV2 = 8;
|
|
36
|
+
|
|
37
|
+
// (mandatory V2) extensible record data in DAG-CBOR format
|
|
38
|
+
optional bytes data = 9;
|
|
39
|
+
}
|