@libp2p/peer-store 7.0.2 → 8.1.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.
Files changed (76) hide show
  1. package/README.md +0 -170
  2. package/dist/index.min.js +12 -12
  3. package/dist/src/errors.d.ts +0 -1
  4. package/dist/src/errors.d.ts.map +1 -1
  5. package/dist/src/errors.js +1 -2
  6. package/dist/src/errors.js.map +1 -1
  7. package/dist/src/index.d.ts +24 -25
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/index.js +120 -87
  10. package/dist/src/index.js.map +1 -1
  11. package/dist/src/pb/peer.d.ts +28 -9
  12. package/dist/src/pb/peer.d.ts.map +1 -1
  13. package/dist/src/pb/peer.js +147 -31
  14. package/dist/src/pb/peer.js.map +1 -1
  15. package/dist/src/store.d.ts +18 -28
  16. package/dist/src/store.d.ts.map +1 -1
  17. package/dist/src/store.js +77 -147
  18. package/dist/src/store.js.map +1 -1
  19. package/dist/src/utils/bytes-to-peer.d.ts +4 -0
  20. package/dist/src/utils/bytes-to-peer.d.ts.map +1 -0
  21. package/dist/src/utils/bytes-to-peer.js +33 -0
  22. package/dist/src/utils/bytes-to-peer.js.map +1 -0
  23. package/dist/src/utils/dedupe-addresses.d.ts +6 -0
  24. package/dist/src/utils/dedupe-addresses.d.ts.map +1 -0
  25. package/dist/src/utils/dedupe-addresses.js +41 -0
  26. package/dist/src/utils/dedupe-addresses.js.map +1 -0
  27. package/dist/src/utils/peer-data-to-datastore-peer.d.ts +5 -0
  28. package/dist/src/utils/peer-data-to-datastore-peer.d.ts.map +1 -0
  29. package/dist/src/utils/peer-data-to-datastore-peer.js +92 -0
  30. package/dist/src/utils/peer-data-to-datastore-peer.js.map +1 -0
  31. package/dist/src/utils/peer-id-to-datastore-key.d.ts +5 -0
  32. package/dist/src/utils/peer-id-to-datastore-key.d.ts.map +1 -0
  33. package/dist/src/utils/peer-id-to-datastore-key.js +13 -0
  34. package/dist/src/utils/peer-id-to-datastore-key.js.map +1 -0
  35. package/dist/src/utils/to-peer-pb.d.ts +10 -0
  36. package/dist/src/utils/to-peer-pb.d.ts.map +1 -0
  37. package/dist/src/utils/to-peer-pb.js +182 -0
  38. package/dist/src/utils/to-peer-pb.js.map +1 -0
  39. package/dist/typedoc-urls.json +13 -2
  40. package/package.json +9 -10
  41. package/src/errors.ts +1 -2
  42. package/src/index.ts +133 -97
  43. package/src/pb/peer.proto +10 -7
  44. package/src/pb/peer.ts +187 -37
  45. package/src/store.ts +102 -189
  46. package/src/utils/bytes-to-peer.ts +41 -0
  47. package/src/utils/dedupe-addresses.ts +51 -0
  48. package/src/utils/peer-data-to-datastore-peer.ts +116 -0
  49. package/src/utils/peer-id-to-datastore-key.ts +15 -0
  50. package/src/utils/to-peer-pb.ts +237 -0
  51. package/dist/src/address-book.d.ts +0 -29
  52. package/dist/src/address-book.d.ts.map +0 -1
  53. package/dist/src/address-book.js +0 -304
  54. package/dist/src/address-book.js.map +0 -1
  55. package/dist/src/key-book.d.ts +0 -21
  56. package/dist/src/key-book.d.ts.map +0 -1
  57. package/dist/src/key-book.js +0 -121
  58. package/dist/src/key-book.js.map +0 -1
  59. package/dist/src/metadata-book.d.ts +0 -28
  60. package/dist/src/metadata-book.d.ts.map +0 -1
  61. package/dist/src/metadata-book.js +0 -211
  62. package/dist/src/metadata-book.js.map +0 -1
  63. package/dist/src/pb/tags.d.ts +0 -21
  64. package/dist/src/pb/tags.d.ts.map +0 -1
  65. package/dist/src/pb/tags.js +0 -111
  66. package/dist/src/pb/tags.js.map +0 -1
  67. package/dist/src/proto-book.d.ts +0 -18
  68. package/dist/src/proto-book.d.ts.map +0 -1
  69. package/dist/src/proto-book.js +0 -199
  70. package/dist/src/proto-book.js.map +0 -1
  71. package/src/address-book.ts +0 -367
  72. package/src/key-book.ts +0 -140
  73. package/src/metadata-book.ts +0 -244
  74. package/src/pb/tags.proto +0 -11
  75. package/src/pb/tags.ts +0 -145
  76. package/src/proto-book.ts +0 -234
package/src/store.ts CHANGED
@@ -1,241 +1,154 @@
1
- import { logger } from '@libp2p/logger'
2
1
  import { peerIdFromBytes } from '@libp2p/peer-id'
3
- import { CodeError } from '@libp2p/interfaces/errors'
4
- import { codes } from './errors.js'
5
- import { Key } from 'interface-datastore/key'
6
2
  import { base32 } from 'multiformats/bases/base32'
7
- import { multiaddr } from '@multiformats/multiaddr'
8
- import { Metadata, Peer as PeerPB } from './pb/peer.js'
9
- import mortice from 'mortice'
10
- import { equals as uint8arrayEquals } from 'uint8arrays/equals'
11
- import type { Peer } from '@libp2p/interface-peer-store'
3
+ import { Peer as PeerPB } from './pb/peer.js'
4
+ import type { Peer, PeerData } from '@libp2p/interface-peer-store'
12
5
  import type { PeerId } from '@libp2p/interface-peer-id'
13
- import type { PersistentPeerStoreComponents } from './index.js'
14
-
15
- const log = logger('libp2p:peer-store:store')
16
-
17
- const NAMESPACE_COMMON = '/peers/'
18
-
19
- export interface Store {
20
- has: (peerId: PeerId) => Promise<boolean>
21
- save: (peer: Peer) => Promise<Peer>
22
- load: (peerId: PeerId) => Promise<Peer>
23
- delete: (peerId: PeerId) => Promise<void>
24
- merge: (peerId: PeerId, data: Partial<Peer>) => Promise<Peer>
25
- mergeOrCreate: (peerId: PeerId, data: Partial<Peer>) => Promise<Peer>
26
- patch: (peerId: PeerId, data: Partial<Peer>) => Promise<Peer>
27
- patchOrCreate: (peerId: PeerId, data: Partial<Peer>) => Promise<Peer>
28
- all: () => AsyncIterable<Peer>
29
-
30
- lock: {
31
- readLock: () => Promise<() => void>
32
- writeLock: () => Promise<() => void>
33
- }
6
+ import type { AddressFilter, PersistentPeerStoreComponents, PersistentPeerStoreInit } from './index.js'
7
+ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
8
+ import { NAMESPACE_COMMON, peerIdToDatastoreKey } from './utils/peer-id-to-datastore-key.js'
9
+ import { bytesToPeer } from './utils/bytes-to-peer.js'
10
+ import { CodeError } from '@libp2p/interfaces/errors'
11
+ import { codes } from './errors.js'
12
+ import type { Datastore } from 'interface-datastore'
13
+ import type { PeerUpdate as PeerUpdateExternal } from '@libp2p/interface-libp2p'
14
+ import mortice, { Mortice } from 'mortice'
15
+ import { toPeerPB } from './utils/to-peer-pb.js'
16
+
17
+ /**
18
+ * Event detail emitted when peer data changes
19
+ */
20
+ export interface PeerUpdate extends PeerUpdateExternal {
21
+ updated: boolean
34
22
  }
35
23
 
36
24
  export class PersistentStore {
37
- private readonly components: PersistentPeerStoreComponents
38
- public lock: any
39
-
40
- constructor (components: PersistentPeerStoreComponents) {
41
- this.components = components
25
+ private readonly peerId: PeerId
26
+ private readonly datastore: Datastore
27
+ public readonly lock: Mortice
28
+ private readonly addressFilter?: AddressFilter
29
+
30
+ constructor (components: PersistentPeerStoreComponents, init: PersistentPeerStoreInit = {}) {
31
+ this.peerId = components.peerId
32
+ this.datastore = components.datastore
33
+ this.addressFilter = init.addressFilter
42
34
  this.lock = mortice({
43
35
  name: 'peer-store',
44
36
  singleProcess: true
45
37
  })
46
38
  }
47
39
 
48
- _peerIdToDatastoreKey (peerId: PeerId): Key {
49
- if (peerId.type == null) {
50
- log.error('peerId must be an instance of peer-id to store data')
51
- throw new CodeError('peerId must be an instance of peer-id', codes.ERR_INVALID_PARAMETERS)
52
- }
53
-
54
- const b32key = peerId.toCID().toString()
55
- return new Key(`${NAMESPACE_COMMON}${b32key}`)
56
- }
57
-
58
40
  async has (peerId: PeerId): Promise<boolean> {
59
- return await this.components.datastore.has(this._peerIdToDatastoreKey(peerId))
41
+ return await this.datastore.has(peerIdToDatastoreKey(peerId))
60
42
  }
61
43
 
62
44
  async delete (peerId: PeerId): Promise<void> {
63
- await this.components.datastore.delete(this._peerIdToDatastoreKey(peerId))
45
+ if (this.peerId.equals(peerId)) {
46
+ throw new CodeError('Cannot delete self peer', codes.ERR_INVALID_PARAMETERS)
47
+ }
48
+
49
+ await this.datastore.delete(peerIdToDatastoreKey(peerId))
64
50
  }
65
51
 
66
52
  async load (peerId: PeerId): Promise<Peer> {
67
- const buf = await this.components.datastore.get(this._peerIdToDatastoreKey(peerId))
68
- const peer = PeerPB.decode(buf)
69
- const metadata = new Map()
53
+ const buf = await this.datastore.get(peerIdToDatastoreKey(peerId))
70
54
 
71
- for (const meta of peer.metadata) {
72
- metadata.set(meta.key, meta.value)
73
- }
74
-
75
- return {
76
- ...peer,
77
- id: peerId,
78
- addresses: peer.addresses.map(({ multiaddr: ma, isCertified }) => {
79
- return {
80
- multiaddr: multiaddr(ma),
81
- isCertified: isCertified ?? false
82
- }
83
- }),
84
- metadata,
85
- pubKey: peer.pubKey ?? undefined,
86
- peerRecordEnvelope: peer.peerRecordEnvelope ?? undefined
87
- }
55
+ return await bytesToPeer(peerId, buf)
88
56
  }
89
57
 
90
- async save (peer: Peer): Promise<Peer> {
91
- if (peer.pubKey != null && peer.id.publicKey != null && !uint8arrayEquals(peer.pubKey, peer.id.publicKey)) {
92
- log.error('peer publicKey bytes do not match peer id publicKey bytes')
93
- throw new CodeError('publicKey bytes do not match peer id publicKey bytes', codes.ERR_INVALID_PARAMETERS)
94
- }
95
-
96
- // dedupe addresses
97
- const addressSet = new Set()
98
- const addresses = peer.addresses
99
- .filter(address => {
100
- if (addressSet.has(address.multiaddr.toString())) {
101
- return false
102
- }
103
-
104
- addressSet.add(address.multiaddr.toString())
105
- return true
106
- })
107
- .sort((a, b) => {
108
- return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
109
- })
110
- .map(({ multiaddr, isCertified }) => ({
111
- multiaddr: multiaddr.bytes,
112
- isCertified
113
- }))
114
-
115
- const metadata: Metadata[] = []
116
-
117
- ;[...peer.metadata.keys()].sort().forEach(key => {
118
- const value = peer.metadata.get(key)
119
-
120
- if (value != null) {
121
- metadata.push({ key, value })
122
- }
123
- })
58
+ async save (peerId: PeerId, data: PeerData): Promise<PeerUpdate> {
59
+ const {
60
+ existingBuf,
61
+ existingPeer
62
+ } = await this.#findExistingPeer(peerId)
124
63
 
125
- const buf = PeerPB.encode({
126
- addresses,
127
- protocols: peer.protocols.sort(),
128
- pubKey: peer.pubKey,
129
- metadata,
130
- peerRecordEnvelope: peer.peerRecordEnvelope
64
+ const peerPb: PeerPB = await toPeerPB(peerId, data, 'patch', {
65
+ addressFilter: this.addressFilter
131
66
  })
132
67
 
133
- await this.components.datastore.put(this._peerIdToDatastoreKey(peer.id), buf.subarray())
134
-
135
- return await this.load(peer.id)
68
+ return await this.#saveIfDifferent(peerId, peerPb, existingBuf, existingPeer)
136
69
  }
137
70
 
138
- async patch (peerId: PeerId, data: Partial<Peer>): Promise<Peer> {
139
- const peer = await this.load(peerId)
71
+ async patch (peerId: PeerId, data: Partial<PeerData>): Promise<PeerUpdate> {
72
+ const {
73
+ existingBuf,
74
+ existingPeer
75
+ } = await this.#findExistingPeer(peerId)
76
+
77
+ const peerPb: PeerPB = await toPeerPB(peerId, data, 'patch', {
78
+ addressFilter: this.addressFilter,
79
+ existingPeer
80
+ })
140
81
 
141
- return await this._patch(peerId, data, peer)
82
+ return await this.#saveIfDifferent(peerId, peerPb, existingBuf, existingPeer)
142
83
  }
143
84
 
144
- async patchOrCreate (peerId: PeerId, data: Partial<Peer>): Promise<Peer> {
145
- let peer: Peer
85
+ async merge (peerId: PeerId, data: PeerData): Promise<PeerUpdate> {
86
+ const {
87
+ existingBuf,
88
+ existingPeer
89
+ } = await this.#findExistingPeer(peerId)
146
90
 
147
- try {
148
- peer = await this.load(peerId)
149
- } catch (err: any) {
150
- if (err.code !== codes.ERR_NOT_FOUND) {
151
- throw err
152
- }
153
-
154
- peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
155
- }
91
+ const peerPb: PeerPB = await toPeerPB(peerId, data, 'merge', {
92
+ addressFilter: this.addressFilter,
93
+ existingPeer
94
+ })
156
95
 
157
- return await this._patch(peerId, data, peer)
96
+ return await this.#saveIfDifferent(peerId, peerPb, existingBuf, existingPeer)
158
97
  }
159
98
 
160
- async _patch (peerId: PeerId, data: Partial<Peer>, peer: Peer): Promise<Peer> {
161
- return await this.save({
162
- ...peer,
163
- ...data,
164
- id: peerId
165
- })
166
- }
99
+ async * all (): AsyncGenerator<Peer, void, unknown> {
100
+ for await (const { key, value } of this.datastore.query({
101
+ prefix: NAMESPACE_COMMON
102
+ })) {
103
+ // /peers/${peer-id-as-libp2p-key-cid-string-in-base-32}
104
+ const base32Str = key.toString().split('/')[2]
105
+ const buf = base32.decode(base32Str)
106
+ const peerId = peerIdFromBytes(buf)
167
107
 
168
- async merge (peerId: PeerId, data: Partial<Peer>): Promise<Peer> {
169
- const peer = await this.load(peerId)
108
+ if (peerId.equals(this.peerId)) {
109
+ // Skip self peer if present
110
+ continue
111
+ }
170
112
 
171
- return await this._merge(peerId, data, peer)
113
+ yield bytesToPeer(peerId, value)
114
+ }
172
115
  }
173
116
 
174
- async mergeOrCreate (peerId: PeerId, data: Partial<Peer>): Promise<Peer> {
175
- /** @type {Peer} */
176
- let peer
177
-
117
+ async #findExistingPeer (peerId: PeerId): Promise<{ existingBuf?: Uint8Array, existingPeer?: Peer }> {
178
118
  try {
179
- peer = await this.load(peerId)
119
+ const existingBuf = await this.datastore.get(peerIdToDatastoreKey(peerId))
120
+ const existingPeer = await bytesToPeer(peerId, existingBuf)
121
+
122
+ return {
123
+ existingBuf,
124
+ existingPeer
125
+ }
180
126
  } catch (err: any) {
181
- if (err.code !== codes.ERR_NOT_FOUND) {
127
+ if (err.code !== 'ERR_NOT_FOUND') {
182
128
  throw err
183
129
  }
184
-
185
- peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
186
130
  }
187
131
 
188
- return await this._merge(peerId, data, peer)
132
+ return {}
189
133
  }
190
134
 
191
- async _merge (peerId: PeerId, data: Partial<Peer>, peer: Peer): Promise<Peer> {
192
- // if the peer has certified addresses, use those in
193
- // favour of the supplied versions
194
- const addresses = new Map<string, boolean>()
195
-
196
- peer.addresses.forEach((addr) => {
197
- addresses.set(addr.multiaddr.toString(), addr.isCertified)
198
- })
199
-
200
- ;(data.addresses ?? []).forEach(addr => {
201
- const addrString = addr.multiaddr.toString()
202
- const isAlreadyCertified = Boolean(addresses.get(addrString))
203
-
204
- const isCertified = isAlreadyCertified || addr.isCertified
135
+ async #saveIfDifferent (peerId: PeerId, peer: PeerPB, existingBuf?: Uint8Array, existingPeer?: Peer): Promise<PeerUpdate> {
136
+ const buf = PeerPB.encode(peer)
205
137
 
206
- addresses.set(addrString, isCertified)
207
- })
138
+ if (existingBuf != null && uint8ArrayEquals(buf, existingBuf)) {
139
+ return {
140
+ peer: await bytesToPeer(peerId, buf),
141
+ previous: existingPeer,
142
+ updated: false
143
+ }
144
+ }
208
145
 
209
- return await this.save({
210
- id: peerId,
211
- addresses: Array.from(addresses.entries()).map(([addrStr, isCertified]) => {
212
- return {
213
- multiaddr: multiaddr(addrStr),
214
- isCertified
215
- }
216
- }),
217
- protocols: Array.from(new Set([
218
- ...(peer.protocols ?? []),
219
- ...(data.protocols ?? [])
220
- ])),
221
- metadata: new Map([
222
- ...(peer.metadata?.entries() ?? []),
223
- ...(data.metadata?.entries() ?? [])
224
- ]),
225
- pubKey: data.pubKey ?? (peer != null ? peer.pubKey : undefined),
226
- peerRecordEnvelope: data.peerRecordEnvelope ?? (peer != null ? peer.peerRecordEnvelope : undefined)
227
- })
228
- }
146
+ await this.datastore.put(peerIdToDatastoreKey(peerId), buf)
229
147
 
230
- async * all (): AsyncGenerator<Peer, void, unknown> {
231
- for await (const key of this.components.datastore.queryKeys({
232
- prefix: NAMESPACE_COMMON
233
- })) {
234
- // /peers/${peer-id-as-libp2p-key-cid-string-in-base-32}
235
- const base32Str = key.toString().split('/')[2]
236
- const buf = base32.decode(base32Str)
237
-
238
- yield this.load(peerIdFromBytes(buf))
148
+ return {
149
+ peer: await bytesToPeer(peerId, buf),
150
+ previous: existingPeer,
151
+ updated: true
239
152
  }
240
153
  }
241
154
  }
@@ -0,0 +1,41 @@
1
+ import { multiaddr } from '@multiformats/multiaddr'
2
+ import { Peer as PeerPB } from '../pb/peer.js'
3
+ import type { Peer, Tag } from '@libp2p/interface-peer-store'
4
+ import { createFromPubKey } from '@libp2p/peer-id-factory'
5
+ import { unmarshalPublicKey } from '@libp2p/crypto/keys'
6
+ import type { PeerId } from '@libp2p/interface-peer-id'
7
+
8
+ export async function bytesToPeer (peerId: PeerId, buf: Uint8Array): Promise<Peer> {
9
+ const peer = PeerPB.decode(buf)
10
+
11
+ if (peer.publicKey != null && peerId.publicKey == null) {
12
+ peerId = await createFromPubKey(unmarshalPublicKey(peer.publicKey))
13
+ }
14
+
15
+ const tags = new Map<string, Tag>()
16
+
17
+ // remove any expired tags
18
+ const now = BigInt(Date.now())
19
+
20
+ for (const [key, tag] of peer.tags.entries()) {
21
+ if (tag.expiry != null && tag.expiry < now) {
22
+ continue
23
+ }
24
+
25
+ tags.set(key, tag)
26
+ }
27
+
28
+ return {
29
+ ...peer,
30
+ id: peerId,
31
+ addresses: peer.addresses.map(({ multiaddr: ma, isCertified }) => {
32
+ return {
33
+ multiaddr: multiaddr(ma),
34
+ isCertified: isCertified ?? false
35
+ }
36
+ }),
37
+ metadata: peer.metadata,
38
+ peerRecordEnvelope: peer.peerRecordEnvelope ?? undefined,
39
+ tags
40
+ }
41
+ }
@@ -0,0 +1,51 @@
1
+ import { isMultiaddr, multiaddr } from '@multiformats/multiaddr'
2
+ import type { Address as AddressPB } from '../pb/peer.js'
3
+ import type { Address } from '@libp2p/interface-peer-store'
4
+ import type { AddressFilter } from '../index.js'
5
+ import type { PeerId } from '@libp2p/interface-peer-id'
6
+ import { CodeError } from '@libp2p/interfaces/errors'
7
+ import { codes } from '../errors.js'
8
+
9
+ export async function dedupeFilterAndSortAddresses (peerId: PeerId, filter: AddressFilter, addresses: Array<Address | AddressPB | undefined>): Promise<AddressPB[]> {
10
+ const addressMap = new Map<string, Address>()
11
+
12
+ for (const addr of addresses) {
13
+ if (addr == null) {
14
+ continue
15
+ }
16
+
17
+ if (addr.multiaddr instanceof Uint8Array) {
18
+ addr.multiaddr = multiaddr(addr.multiaddr)
19
+ }
20
+
21
+ if (!isMultiaddr(addr.multiaddr)) {
22
+ throw new CodeError('Multiaddr was invalid', codes.ERR_INVALID_PARAMETERS)
23
+ }
24
+
25
+ if (!(await filter(peerId, addr.multiaddr))) {
26
+ continue
27
+ }
28
+
29
+ const isCertified = addr.isCertified ?? false
30
+ const maStr = addr.multiaddr.toString()
31
+ const existingAddr = addressMap.get(maStr)
32
+
33
+ if (existingAddr != null) {
34
+ addr.isCertified = existingAddr.isCertified || isCertified
35
+ } else {
36
+ addressMap.set(maStr, {
37
+ multiaddr: addr.multiaddr,
38
+ isCertified
39
+ })
40
+ }
41
+ }
42
+
43
+ return [...addressMap.values()]
44
+ .sort((a, b) => {
45
+ return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
46
+ })
47
+ .map(({ isCertified, multiaddr }) => ({
48
+ isCertified,
49
+ multiaddr: multiaddr.bytes
50
+ }))
51
+ }
@@ -0,0 +1,116 @@
1
+
2
+ import { CodeError } from '@libp2p/interfaces/errors'
3
+ import { codes } from '../errors.js'
4
+ import { isMultiaddr } from '@multiformats/multiaddr'
5
+ import type { Peer as PeerPB } from '../pb/peer.js'
6
+ import { equals as uint8arrayEquals } from 'uint8arrays/equals'
7
+ import type { PeerData } from '@libp2p/interface-peer-store'
8
+ import type { PeerId } from '@libp2p/interface-peer-id'
9
+
10
+ export function toDatastorePeer (peerId: PeerId, data: PeerData): PeerPB {
11
+ if (data == null) {
12
+ throw new CodeError('Invalid PeerData', codes.ERR_INVALID_PARAMETERS)
13
+ }
14
+
15
+ if (data.publicKey != null && peerId.publicKey != null && !uint8arrayEquals(data.publicKey, peerId.publicKey)) {
16
+ throw new CodeError('publicKey bytes do not match peer id publicKey bytes', codes.ERR_INVALID_PARAMETERS)
17
+ }
18
+
19
+ // merge addresses and multiaddrs, and dedupe
20
+ const addressSet = new Set()
21
+
22
+ const output: PeerPB = {
23
+ addresses: (data.addresses ?? [])
24
+ .concat((data.multiaddrs ?? []).map(multiaddr => ({ multiaddr, isCertified: false })))
25
+ .filter(address => {
26
+ if (!isMultiaddr(address.multiaddr)) {
27
+ throw new CodeError('Invalid mulitaddr', codes.ERR_INVALID_PARAMETERS)
28
+ }
29
+
30
+ if (addressSet.has(address.multiaddr.toString())) {
31
+ return false
32
+ }
33
+
34
+ addressSet.add(address.multiaddr.toString())
35
+ return true
36
+ })
37
+ .sort((a, b) => {
38
+ return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
39
+ })
40
+ .map(({ multiaddr, isCertified }) => ({
41
+ multiaddr: multiaddr.bytes,
42
+ isCertified
43
+ })),
44
+ protocols: (data.protocols ?? []).sort(),
45
+ metadata: new Map(),
46
+ tags: new Map(),
47
+ publicKey: data.publicKey,
48
+ peerRecordEnvelope: data.peerRecordEnvelope
49
+ }
50
+
51
+ // remove invalid metadata
52
+ if (data.metadata != null) {
53
+ const metadataEntries = data.metadata instanceof Map ? data.metadata.entries() : Object.entries(data.metadata)
54
+
55
+ for (const [key, value] of metadataEntries) {
56
+ if (typeof key !== 'string') {
57
+ throw new CodeError('Peer metadata keys must be strings', codes.ERR_INVALID_PARAMETERS)
58
+ }
59
+
60
+ if (value == null) {
61
+ continue
62
+ }
63
+
64
+ if (!(value instanceof Uint8Array)) {
65
+ throw new CodeError('Peer metadata values must be Uint8Arrays', codes.ERR_INVALID_PARAMETERS)
66
+ }
67
+
68
+ output.metadata.set(key, value)
69
+ }
70
+ }
71
+
72
+ if (data.tags != null) {
73
+ const tagsEntries = data.tags instanceof Map ? data.tags.entries() : Object.entries(data.tags)
74
+
75
+ for (const [key, value] of tagsEntries) {
76
+ if (typeof key !== 'string') {
77
+ throw new CodeError('Peer tag keys must be strings', codes.ERR_INVALID_PARAMETERS)
78
+ }
79
+
80
+ if (value == null) {
81
+ continue
82
+ }
83
+
84
+ const tag = {
85
+ name: key,
86
+ ttl: value.ttl,
87
+ value: value.value ?? 0
88
+ }
89
+
90
+ if (tag.value < 0 || tag.value > 100) {
91
+ throw new CodeError('Tag value must be between 0-100', codes.ERR_INVALID_PARAMETERS)
92
+ }
93
+
94
+ if (parseInt(`${tag.value}`, 10) !== tag.value) {
95
+ throw new CodeError('Tag value must be an integer', codes.ERR_INVALID_PARAMETERS)
96
+ }
97
+
98
+ if (tag.ttl != null) {
99
+ if (tag.ttl < 0) {
100
+ throw new CodeError('Tag ttl must be between greater than 0', codes.ERR_INVALID_PARAMETERS)
101
+ }
102
+
103
+ if (parseInt(`${tag.ttl}`, 10) !== tag.ttl) {
104
+ throw new CodeError('Tag ttl must be an integer', codes.ERR_INVALID_PARAMETERS)
105
+ }
106
+ }
107
+
108
+ output.tags.set(tag.name, {
109
+ value: tag.value,
110
+ expiry: tag.ttl == null ? undefined : BigInt(Date.now() + tag.ttl)
111
+ })
112
+ }
113
+ }
114
+
115
+ return output
116
+ }
@@ -0,0 +1,15 @@
1
+ import { CodeError } from '@libp2p/interfaces/errors'
2
+ import { codes } from '../errors.js'
3
+ import { Key } from 'interface-datastore/key'
4
+ import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
5
+
6
+ export const NAMESPACE_COMMON = '/peers/'
7
+
8
+ export function peerIdToDatastoreKey (peerId: PeerId): Key {
9
+ if (!isPeerId(peerId) || peerId.type == null) {
10
+ throw new CodeError('Invalid PeerId', codes.ERR_INVALID_PARAMETERS)
11
+ }
12
+
13
+ const b32key = peerId.toCID().toString()
14
+ return new Key(`${NAMESPACE_COMMON}${b32key}`)
15
+ }