@leofcoin/peernet 0.9.3 → 0.9.6
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/dist/browser/peernet.js +651 -646
- package/dist/commonjs/codec-73adfc0f.js +205 -0
- package/dist/commonjs/codec-format-interface.js +5 -5
- package/dist/commonjs/codec.js +6 -199
- package/dist/commonjs/dht-response.js +1 -1
- package/dist/commonjs/dht.js +1 -1
- package/dist/commonjs/hash.js +4 -4
- package/dist/commonjs/peernet-message.js +1 -1
- package/dist/commonjs/peernet.js +653 -648
- package/dist/commonjs/request.js +1 -1
- package/dist/commonjs/response.js +1 -1
- package/dist/module/peernet.js +10 -4
- package/package.json +1 -1
- package/src/client.js +71 -0
- package/src/codec/codec-format-interface.js +139 -0
- package/src/codec/codec.js +114 -0
- package/src/codec/codecs.js +79 -0
- package/src/dht/dht.js +125 -0
- package/src/discovery/peer-discovery.js +75 -0
- package/src/errors/errors.js +12 -0
- package/src/handlers/message.js +52 -0
- package/src/hash/hash.js +145 -0
- package/src/http/api.js +115 -0
- package/src/http/client/api.js +41 -0
- package/src/http/client/client.js +10 -0
- package/src/http/client/http-client.js +44 -0
- package/src/http/client/storage.js +36 -0
- package/src/http/http.js +28 -0
- package/src/messages/chat-message.js +14 -0
- package/src/messages/data-response.js +14 -0
- package/src/messages/data.js +14 -0
- package/src/messages/dht-response.js +15 -0
- package/src/messages/dht.js +25 -0
- package/src/messages/peer-response.js +14 -0
- package/src/messages/peer.js +14 -0
- package/src/messages/peernet-message.js +14 -0
- package/src/messages/ps.js +14 -0
- package/src/messages/request.js +14 -0
- package/src/messages/response.js +14 -0
- package/src/peer-info.js +9 -0
- package/src/peer.js +43 -0
- package/src/peernet.js +680 -0
- package/src/proto/chat-message.proto.js +7 -0
- package/src/proto/data-response.proto.js +7 -0
- package/src/proto/data.proto.js +7 -0
- package/src/proto/dht-response.proto.js +7 -0
- package/src/proto/dht.proto.js +7 -0
- package/src/proto/peer-response.proto.js +6 -0
- package/src/proto/peer.proto.js +6 -0
- package/src/proto/peernet.proto.js +9 -0
- package/src/proto/ps.proto.js +6 -0
- package/src/proto/request.proto.js +6 -0
- package/src/proto/response.proto.js +6 -0
- package/src/server.js +25 -0
- package/src/utils/utils.js +78 -0
- package/.nyc_output/39a61420-013f-4db1-a597-7c5444da26e7.json +0 -1
- package/.nyc_output/4b387323-32a3-4eee-8f05-d13f2e0a5bf4.json +0 -1
- package/.nyc_output/ef71cf24-d9d9-45dd-814f-8d53cb6769f3.json +0 -1
- package/.nyc_output/processinfo/39a61420-013f-4db1-a597-7c5444da26e7.json +0 -1
- package/.nyc_output/processinfo/4b387323-32a3-4eee-8f05-d13f2e0a5bf4.json +0 -1
- package/.nyc_output/processinfo/ef71cf24-d9d9-45dd-814f-8d53cb6769f3.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
package/src/peernet.js
ADDED
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
import Pubsub from '@vandeurenglenn/little-pubsub'
|
|
2
|
+
import Client from './client'
|
|
3
|
+
import LeofcoinStorage from './../node_modules/@leofcoin/storage/src/level.js'
|
|
4
|
+
import http from './http/http.js'
|
|
5
|
+
import httpClient from './http/client/client.js'
|
|
6
|
+
import LeofcoinStorageClient from './http/client/storage.js'
|
|
7
|
+
import PeernetMessage from './messages/peernet-message.js'
|
|
8
|
+
import DHTMessage from './messages/dht.js'
|
|
9
|
+
import DHTMessageResponse from './messages/dht-response.js'
|
|
10
|
+
import DataMessage from './messages/data.js'
|
|
11
|
+
import PsMessage from './messages/ps.js'
|
|
12
|
+
import PeerMessage from './messages/peer.js'
|
|
13
|
+
import RequestMessage from './messages/request.js'
|
|
14
|
+
import ResponseMessage from './messages/response.js'
|
|
15
|
+
import PeerMessageResponse from './messages/peer-response.js'
|
|
16
|
+
import DataMessageResponse from './messages/data-response.js'
|
|
17
|
+
import ChatMessage from './messages/chat-message.js'
|
|
18
|
+
import PeerDiscovery from './discovery/peer-discovery'
|
|
19
|
+
import DHT from './dht/dht.js'
|
|
20
|
+
import Hash from './hash/hash'
|
|
21
|
+
import codecs from './codec/codecs'
|
|
22
|
+
import { debug, protoFor, target } from './utils/utils.js'
|
|
23
|
+
import generateAccount from
|
|
24
|
+
'./../node_modules/@leofcoin/generate-account/dist/module/generate-account.js'
|
|
25
|
+
import MessageHandler from './handlers/message.js'
|
|
26
|
+
import { encapsulatedError, dhtError,
|
|
27
|
+
nothingFoundError } from './errors/errors.js'
|
|
28
|
+
|
|
29
|
+
globalThis.leofcoin = globalThis.leofcoin || {}
|
|
30
|
+
globalThis.globalSub = globalThis.globalSub || new Pubsub({verbose: true})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @access public
|
|
34
|
+
* @example
|
|
35
|
+
* const peernet = new Peernet();
|
|
36
|
+
*/
|
|
37
|
+
export default class Peernet {
|
|
38
|
+
/**
|
|
39
|
+
* @access public
|
|
40
|
+
* @param {Object} options
|
|
41
|
+
* @param {String} options.network - desired network
|
|
42
|
+
* @param {String} options.root - path to root directory
|
|
43
|
+
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
44
|
+
*
|
|
45
|
+
* @return {Promise} instance of Peernet
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
49
|
+
*/
|
|
50
|
+
constructor(options = {}) {
|
|
51
|
+
this._discovered = []
|
|
52
|
+
/**
|
|
53
|
+
* @property {String} network - current network
|
|
54
|
+
*/
|
|
55
|
+
this.network = options.network || 'leofcoin'
|
|
56
|
+
const parts = this.network.split(':')
|
|
57
|
+
|
|
58
|
+
if (!options.storePrefix) options.storePrefix = 'lfc'
|
|
59
|
+
if (!options.port) options.port = 2000
|
|
60
|
+
if (!options.root) {
|
|
61
|
+
if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`
|
|
62
|
+
else options.root = `.${this.network}/peernet`
|
|
63
|
+
}
|
|
64
|
+
globalThis.peernet = this
|
|
65
|
+
return this._init(options)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get defaultStores() {
|
|
69
|
+
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
addProto(name, proto) {
|
|
73
|
+
if (!this.protos[name]) this.protos[name] = proto
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
addCodec(name, codec) {
|
|
77
|
+
if (!this.codecs[name]) this.codecs[name] = codec
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async addStore(name, prefix, root, isPrivate = true) {
|
|
81
|
+
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
82
|
+
name === 'data' || name === 'message') isPrivate = false
|
|
83
|
+
|
|
84
|
+
let Storage
|
|
85
|
+
if (this.hasDaemon) {
|
|
86
|
+
Storage = LeofcoinStorageClient
|
|
87
|
+
} else {
|
|
88
|
+
Storage = LeofcoinStorage
|
|
89
|
+
}
|
|
90
|
+
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
91
|
+
await new Storage(`${prefix}-${name}`, root)
|
|
92
|
+
|
|
93
|
+
globalThis[`${name}Store`].private = isPrivate
|
|
94
|
+
if (!isPrivate) this.stores.push(name)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @see MessageHandler
|
|
100
|
+
*/
|
|
101
|
+
prepareMessage(to, data) {
|
|
102
|
+
return this._messageHandler.prepareMessage(this.id, to, data)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @access public
|
|
107
|
+
*
|
|
108
|
+
* @return {Array} peerId
|
|
109
|
+
*/
|
|
110
|
+
get peers() {
|
|
111
|
+
return [...connections.values()]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @private
|
|
116
|
+
*
|
|
117
|
+
* @param {Object} options
|
|
118
|
+
* @param {String} options.root - path to root directory
|
|
119
|
+
*
|
|
120
|
+
* @return {Promise} instance of Peernet
|
|
121
|
+
*/
|
|
122
|
+
async _init(options) {
|
|
123
|
+
// peernetDHT aka closesPeer by coordinates
|
|
124
|
+
/**
|
|
125
|
+
* @type {Object}
|
|
126
|
+
* @property {Object} peer Instance of Peer
|
|
127
|
+
*/
|
|
128
|
+
this.dht = new DHT()
|
|
129
|
+
/**
|
|
130
|
+
* @type {Map}
|
|
131
|
+
* @property {Object} peer Instance of Peer
|
|
132
|
+
*/
|
|
133
|
+
this.peerMap = new Map()
|
|
134
|
+
this.stores = []
|
|
135
|
+
this.requestProtos = {}
|
|
136
|
+
this.storePrefix = options.storePrefix
|
|
137
|
+
this.root = options.root
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* proto Object containing protos
|
|
141
|
+
* @type {Object}
|
|
142
|
+
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
143
|
+
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
144
|
+
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
145
|
+
* @property {DataMessage} protos[peernet-data] messageNode
|
|
146
|
+
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
147
|
+
*/
|
|
148
|
+
globalThis.peernet.protos = {
|
|
149
|
+
'peernet-request': RequestMessage,
|
|
150
|
+
'peernet-response': ResponseMessage,
|
|
151
|
+
'peernet-peer': PeerMessage,
|
|
152
|
+
'peernet-peer-response': PeerMessageResponse,
|
|
153
|
+
'peernet-message': PeernetMessage,
|
|
154
|
+
'peernet-dht': DHTMessage,
|
|
155
|
+
'peernet-dht-response': DHTMessageResponse,
|
|
156
|
+
'peernet-data': DataMessage,
|
|
157
|
+
'peernet-data-response': DataMessageResponse,
|
|
158
|
+
'peernet-ps': PsMessage,
|
|
159
|
+
'chat-message': ChatMessage,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.protos = globalThis.peernet.protos
|
|
163
|
+
this.codecs = codecs
|
|
164
|
+
|
|
165
|
+
this._messageHandler = new MessageHandler(this.network)
|
|
166
|
+
|
|
167
|
+
const {daemon, environment} = await target()
|
|
168
|
+
this.hasDaemon = daemon
|
|
169
|
+
|
|
170
|
+
if (this.hasDaemon) {
|
|
171
|
+
globalThis.peernet.client = await httpClient({
|
|
172
|
+
protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
|
|
173
|
+
})
|
|
174
|
+
} else {
|
|
175
|
+
if (environment !== 'browser') http(options)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const store of this.defaultStores) {
|
|
179
|
+
await this.addStore(store, options.storePrefix, options.root)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const pub = await accountStore.get('public')
|
|
184
|
+
this.id = pub.walletId
|
|
185
|
+
} catch (e) {
|
|
186
|
+
if (e.code === 'ERR_NOT_FOUND') {
|
|
187
|
+
const wallet = {}
|
|
188
|
+
const {identity, accounts, config} = await generateAccount(this.network)
|
|
189
|
+
wallet.identity = identity
|
|
190
|
+
wallet.accounts = accounts
|
|
191
|
+
wallet.version = 1
|
|
192
|
+
walletStore.put(wallet)
|
|
193
|
+
await accountStore.put('config', config);
|
|
194
|
+
await accountStore.put('public', {walletId: wallet.identity.walletId});
|
|
195
|
+
|
|
196
|
+
this.id = wallet.identity.walletId
|
|
197
|
+
} else {
|
|
198
|
+
throw e
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
this._peerHandler = new PeerDiscovery(this.id)
|
|
202
|
+
// peernet id
|
|
203
|
+
const id = Buffer.from(this.id.slice(0, 32))
|
|
204
|
+
this.peerId = id
|
|
205
|
+
|
|
206
|
+
pubsub.subscribe('peer:discovered', async (peer) => {
|
|
207
|
+
this._peerHandler.discover(peer)
|
|
208
|
+
peer.on('peernet.data', async (message) => {
|
|
209
|
+
const id = message.id
|
|
210
|
+
message = new PeernetMessage(Buffer.from(message.data.data))
|
|
211
|
+
const proto = protoFor(message.decoded.data)
|
|
212
|
+
await this._protoHandler({id, proto}, peer)
|
|
213
|
+
const fulldId = this._getPeerId(peer.id)
|
|
214
|
+
if (fulldId && this._discovered.indexOf(peer.id) === -1) {
|
|
215
|
+
this._discovered.push(peer.id)
|
|
216
|
+
pubsub.publish('peer:connected', peer)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
pubsub.subscribe('peer:disconnected', async (peer) => {
|
|
221
|
+
let index = this._discovered.indexOf(peer.id)
|
|
222
|
+
if (index !== -1) this._discovered.splice(index, 1)
|
|
223
|
+
const id = this._getPeerId(peer.id)
|
|
224
|
+
let peerIds = this.peerMap.get(id)
|
|
225
|
+
|
|
226
|
+
if (peerIds) {
|
|
227
|
+
index = peerIds.indexOf(peer.id)
|
|
228
|
+
if (index !== -1) peerIds.splice(index, 1)
|
|
229
|
+
} else {
|
|
230
|
+
peerIds = []
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (peerIds.length === 0) this.peerMap.delete(id)
|
|
234
|
+
else this.peerMap.set(id, peerIds)
|
|
235
|
+
})
|
|
236
|
+
pubsub.subscribe('peer:connected', async (peer) => {
|
|
237
|
+
console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
238
|
+
// peer.on('peernet.data', async (message) => {
|
|
239
|
+
// const id = message.id
|
|
240
|
+
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
241
|
+
// const proto = protoFor(message.decoded.data)
|
|
242
|
+
// this._protoHandler({id, proto}, peer)
|
|
243
|
+
// })
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @access public
|
|
248
|
+
* @type {PeernetClient}
|
|
249
|
+
*/
|
|
250
|
+
this.client = new Client({...options, id})
|
|
251
|
+
return this
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_getPeerId(id) {
|
|
255
|
+
for (const entry of [...this.peerMap.entries()]) {
|
|
256
|
+
for (const _id of entry[1]) {
|
|
257
|
+
if (_id === id) return entry[0]
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
addRequestHandler(name, method) {
|
|
263
|
+
this.requestProtos[name] = method
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @private
|
|
268
|
+
*
|
|
269
|
+
* @param {Buffer} message - peernet message
|
|
270
|
+
* @param {PeernetPeer} peer - peernet peer
|
|
271
|
+
*/
|
|
272
|
+
async _protoHandler(message, peer) {
|
|
273
|
+
const {id, proto} = message
|
|
274
|
+
if (proto.name === 'peernet-peer') {
|
|
275
|
+
const from = proto.decoded.id
|
|
276
|
+
if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
277
|
+
else {
|
|
278
|
+
const connections = this.peerMap.get(from)
|
|
279
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
280
|
+
connections.push(peer.id)
|
|
281
|
+
this.peerMap.set(from, connections)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const data = new PeerMessageResponse({id: this.id})
|
|
285
|
+
const node = await this.prepareMessage(from, data.encoded)
|
|
286
|
+
|
|
287
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
288
|
+
} else if (proto.name === 'peernet-peer-response') {
|
|
289
|
+
const from = proto.decoded.id
|
|
290
|
+
if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
291
|
+
else {
|
|
292
|
+
const connections = this.peerMap.get(from)
|
|
293
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
294
|
+
connections.push(peer.id)
|
|
295
|
+
this.peerMap.set(from, connections)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
let from = this._getPeerId(peer.id)
|
|
300
|
+
if (!from) {
|
|
301
|
+
const data = new PeerMessage({id: this.id})
|
|
302
|
+
const node = await this.prepareMessage(peer.id, data.encoded)
|
|
303
|
+
|
|
304
|
+
let response = await peer.request(node.encoded)
|
|
305
|
+
response = protoFor(response)
|
|
306
|
+
response = new PeerMessageResponse(response.decoded.data)
|
|
307
|
+
|
|
308
|
+
from = response.decoded.id
|
|
309
|
+
if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
310
|
+
else {
|
|
311
|
+
const connections = this.peerMap.get(from)
|
|
312
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
313
|
+
connections.push(peer.id)
|
|
314
|
+
this.peerMap.set(from, connections)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (proto.name === 'peernet-dht') {
|
|
319
|
+
let { hash, store } = proto.decoded
|
|
320
|
+
let has;
|
|
321
|
+
|
|
322
|
+
if (!store) {
|
|
323
|
+
has = await this.has(hash)
|
|
324
|
+
} else {
|
|
325
|
+
store = globalThis[`${store}Store`]
|
|
326
|
+
if (store.private) has = false
|
|
327
|
+
else has = await store.has(hash)
|
|
328
|
+
}
|
|
329
|
+
const data = new DHTMessageResponse({hash, has})
|
|
330
|
+
const node = await this.prepareMessage(from, data.encoded)
|
|
331
|
+
|
|
332
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
333
|
+
} else if (proto.name === 'peernet-data') {
|
|
334
|
+
let { hash, store } = proto.decoded
|
|
335
|
+
let data
|
|
336
|
+
|
|
337
|
+
if (!store) {
|
|
338
|
+
data = await this.get(hash)
|
|
339
|
+
} else {
|
|
340
|
+
store = globalThis[`${store}Store`]
|
|
341
|
+
if (store.private) {
|
|
342
|
+
// TODO: ban
|
|
343
|
+
return
|
|
344
|
+
} else data = await store.get(hash)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (data) {
|
|
348
|
+
data = new DataMessageResponse({hash, data: Buffer.from(data)})
|
|
349
|
+
|
|
350
|
+
const node = await this.prepareMessage(from, data.encoded)
|
|
351
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
352
|
+
}
|
|
353
|
+
} else if (proto.name === 'peernet-peer') {
|
|
354
|
+
const from = proto.decoded.id
|
|
355
|
+
if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
356
|
+
else {
|
|
357
|
+
const connections = this.peerMap.get(from)
|
|
358
|
+
connections.push(peer.id)
|
|
359
|
+
this.peerMap.set(from, connections)
|
|
360
|
+
}
|
|
361
|
+
const data = new PeerMessage({id: this.id})
|
|
362
|
+
const node = await this.prepareMessage(from, data.encoded)
|
|
363
|
+
|
|
364
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
365
|
+
} else if (proto.name === 'peernet-request') {
|
|
366
|
+
// TODO: make dynamic
|
|
367
|
+
// exposeddevapi[proto.decoded.request](proto.decoded.params)
|
|
368
|
+
const method = this.requestProtos[proto.decoded.request]
|
|
369
|
+
if (method) {
|
|
370
|
+
const data = await method()
|
|
371
|
+
const node = await this.prepareMessage(from, data.encoded)
|
|
372
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
373
|
+
}
|
|
374
|
+
} else if (proto.name === 'peernet-ps' &&
|
|
375
|
+
this._getPeerId(peer.id) !== this.id.toString()) {
|
|
376
|
+
globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString())
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* performs a walk and resolves first encounter
|
|
383
|
+
*
|
|
384
|
+
* @param {String} hash
|
|
385
|
+
*/
|
|
386
|
+
async walk(hash) {
|
|
387
|
+
if (!hash) throw new Error('hash expected, received undefined')
|
|
388
|
+
const data = new DHTMessage({hash})
|
|
389
|
+
const clientId = this.client.id
|
|
390
|
+
for (const peer of this.peers) {
|
|
391
|
+
const node = await this.prepareMessage(peer.id, data.encoded)
|
|
392
|
+
|
|
393
|
+
const result = await peer.request(node.encoded)
|
|
394
|
+
|
|
395
|
+
let proto = protoFor(result.data)
|
|
396
|
+
|
|
397
|
+
if (proto.name !== 'peernet-message') throw encapsulatedError()
|
|
398
|
+
const from = proto.decoded.from
|
|
399
|
+
proto = protoFor(proto.decoded.data)
|
|
400
|
+
|
|
401
|
+
if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
|
|
402
|
+
|
|
403
|
+
// TODO: give ip and port (just used for location)
|
|
404
|
+
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
405
|
+
peer.connection.remoteFamily = 'ipv4'
|
|
406
|
+
peer.connection.remoteAddress = '127.0.0.1'
|
|
407
|
+
peer.connection.remotePort = '0000'
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const peerInfo = {
|
|
411
|
+
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
412
|
+
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
413
|
+
port: peer.connection.remotePort || peer.connection.localPort,
|
|
414
|
+
id: from,
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash)
|
|
418
|
+
}
|
|
419
|
+
return
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Override DHT behavior, try's finding the content three times
|
|
424
|
+
*
|
|
425
|
+
* @param {String} hash
|
|
426
|
+
*/
|
|
427
|
+
async providersFor(hash) {
|
|
428
|
+
let providers = await this.dht.providersFor(hash)
|
|
429
|
+
// walk the network to find a provider
|
|
430
|
+
if (!providers || providers.length === 0) {
|
|
431
|
+
await this.walk(hash)
|
|
432
|
+
providers = await this.dht.providersFor(hash)
|
|
433
|
+
// second walk the network to find a provider
|
|
434
|
+
if (!providers || providers.length === 0) {
|
|
435
|
+
await this.walk(hash)
|
|
436
|
+
providers = await this.dht.providersFor(hash)
|
|
437
|
+
}
|
|
438
|
+
// last walk
|
|
439
|
+
if (!providers || providers.length === 0) {
|
|
440
|
+
await this.walk(hash)
|
|
441
|
+
providers = await this.dht.providersFor(hash)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// undefined if no providers given
|
|
445
|
+
return providers
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
get block() {
|
|
449
|
+
return {
|
|
450
|
+
get: async (hash) => {
|
|
451
|
+
const data = await blockStore.has(hash)
|
|
452
|
+
if (data) return await blockStore.get(hash)
|
|
453
|
+
return this.requestData(hash)
|
|
454
|
+
},
|
|
455
|
+
put: async (hash, data) => {
|
|
456
|
+
if (await blockStore.has(hash)) return
|
|
457
|
+
return await blockStore.put(hash, data)
|
|
458
|
+
},
|
|
459
|
+
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
get transaction() {
|
|
464
|
+
return {
|
|
465
|
+
get: async (hash) => {
|
|
466
|
+
const data = await transactionStore.has(hash)
|
|
467
|
+
if (data) return await transactionStore.get(hash)
|
|
468
|
+
return this.requestData(hash, 'transaction')
|
|
469
|
+
},
|
|
470
|
+
put: async (hash, data) => {
|
|
471
|
+
if (await transactionStore.has(hash)) return
|
|
472
|
+
return await transactionStore.put(hash, data)
|
|
473
|
+
},
|
|
474
|
+
has: async (hash) => await transactionStore.has(hash),
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async requestData(hash, store) {
|
|
479
|
+
const providers = await this.providersFor(hash)
|
|
480
|
+
if (!providers || providers.size === 0) throw nothingFoundError(hash)
|
|
481
|
+
debug(`found ${providers.size} provider(s) for ${hash}`)
|
|
482
|
+
// get closest peer on earth
|
|
483
|
+
const closestPeer = await this.dht.closestPeer(providers)
|
|
484
|
+
// get peer instance by id
|
|
485
|
+
if (!closestPeer || !closestPeer.id) return this.requestData(hash, store)
|
|
486
|
+
|
|
487
|
+
const id = closestPeer.id.toString()
|
|
488
|
+
if (this.peers) {
|
|
489
|
+
let closest = this.peers.filter((peer) => {
|
|
490
|
+
if (this._getPeerId(peer.id) === id) return peer
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
let data = new DataMessage({hash, store})
|
|
494
|
+
|
|
495
|
+
const node = await this.prepareMessage(id, data.encoded)
|
|
496
|
+
if (closest[0]) data = await closest[0].request(node.encoded)
|
|
497
|
+
else {
|
|
498
|
+
closest = this.peers.filter((peer) => {
|
|
499
|
+
if (peer.id.toString() === id) return peer
|
|
500
|
+
})
|
|
501
|
+
if (closest[0]) data = await closest[0].request(node.encoded)
|
|
502
|
+
}
|
|
503
|
+
if (data.data) {
|
|
504
|
+
let proto = protoFor(Buffer.from(data.data))
|
|
505
|
+
proto = protoFor(proto.decoded.data)
|
|
506
|
+
return proto.decoded.data
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// this.put(hash, proto.decoded.data)
|
|
510
|
+
}
|
|
511
|
+
return null
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
get message() {
|
|
516
|
+
return {
|
|
517
|
+
/**
|
|
518
|
+
* Get content for given message hash
|
|
519
|
+
*
|
|
520
|
+
* @param {String} hash
|
|
521
|
+
*/
|
|
522
|
+
get: async (hash) => {
|
|
523
|
+
debug(`get message ${hash}`)
|
|
524
|
+
const message = await messageStore.has(hash)
|
|
525
|
+
if (message) return await messageStore.get(hash)
|
|
526
|
+
return this.requestData(hash, 'message')
|
|
527
|
+
},
|
|
528
|
+
/**
|
|
529
|
+
* put message content
|
|
530
|
+
*
|
|
531
|
+
* @param {String} hash
|
|
532
|
+
* @param {Buffer} message
|
|
533
|
+
*/
|
|
534
|
+
put: async (hash, message) => await messageStore.put(hash, message),
|
|
535
|
+
/**
|
|
536
|
+
* @param {String} hash
|
|
537
|
+
* @return {Boolean}
|
|
538
|
+
*/
|
|
539
|
+
has: async (hash) => await messageStore.has(hash),
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
get data() {
|
|
544
|
+
return {
|
|
545
|
+
/**
|
|
546
|
+
* Get content for given data hash
|
|
547
|
+
*
|
|
548
|
+
* @param {String} hash
|
|
549
|
+
*/
|
|
550
|
+
get: async (hash) => {
|
|
551
|
+
debug(`get data ${hash}`)
|
|
552
|
+
const data = await dataStore.has(hash)
|
|
553
|
+
if (data) return await dataStore.get(hash)
|
|
554
|
+
return this.requestData(hash, 'data')
|
|
555
|
+
},
|
|
556
|
+
/**
|
|
557
|
+
* put data content
|
|
558
|
+
*
|
|
559
|
+
* @param {String} hash
|
|
560
|
+
* @param {Buffer} data
|
|
561
|
+
*/
|
|
562
|
+
put: async (hash, data) => await dataStore.put(hash, data),
|
|
563
|
+
/**
|
|
564
|
+
* @param {String} hash
|
|
565
|
+
* @return {Boolean}
|
|
566
|
+
*/
|
|
567
|
+
has: async (hash) => await dataStore.has(hash),
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* goes trough given stores and tries to find data for given hash
|
|
573
|
+
* @param {Array} stores
|
|
574
|
+
* @param {string} hash
|
|
575
|
+
*/
|
|
576
|
+
async whichStore(stores, hash) {
|
|
577
|
+
let store = stores.pop()
|
|
578
|
+
const name = store
|
|
579
|
+
store = globalThis[`${store}Store`]
|
|
580
|
+
if (store) {
|
|
581
|
+
const has = await store.has(hash)
|
|
582
|
+
if (has) return store
|
|
583
|
+
if (stores.length > 0) return this.whichStore(stores, hash)
|
|
584
|
+
} else return null
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Get content for given hash
|
|
589
|
+
*
|
|
590
|
+
* @param {String} hash
|
|
591
|
+
*/
|
|
592
|
+
async get(hash, store) {
|
|
593
|
+
debug(`get ${hash}`)
|
|
594
|
+
let data
|
|
595
|
+
if (store) store = globalThis[`${store}Store`]
|
|
596
|
+
if (!store) store = await this.whichStore([...this.stores], hash)
|
|
597
|
+
if (store && await store.has(hash)) data = await store.get(hash)
|
|
598
|
+
if (data) return data
|
|
599
|
+
|
|
600
|
+
return this.requestData(hash, 'data')
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* put content
|
|
605
|
+
*
|
|
606
|
+
* @param {String} hash
|
|
607
|
+
* @param {Buffer} data
|
|
608
|
+
*/
|
|
609
|
+
async put(hash, data, store = 'data') {
|
|
610
|
+
store = globalThis[`${store}Store`]
|
|
611
|
+
return await store.put(hash, data)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* @param {String} hash
|
|
616
|
+
* @return {Boolean}
|
|
617
|
+
*/
|
|
618
|
+
async has(hash) {
|
|
619
|
+
const store = await this.whichStore([...this.stores], hash)
|
|
620
|
+
if (store) {
|
|
621
|
+
if (store.private) return false
|
|
622
|
+
else return true
|
|
623
|
+
}
|
|
624
|
+
return false
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
*
|
|
629
|
+
* @param {String} topic
|
|
630
|
+
* @param {String|Object|Array|Boolean|Buffer} data
|
|
631
|
+
*/
|
|
632
|
+
async publish(topic, data) {
|
|
633
|
+
// globalSub.publish(topic, data)
|
|
634
|
+
if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic)
|
|
635
|
+
if (!Buffer.isBuffer(data)) data = Buffer.from(data)
|
|
636
|
+
const id = Math.random().toString(36).slice(-12)
|
|
637
|
+
data = new PsMessage({data, topic})
|
|
638
|
+
for (const peer of this.peers) {
|
|
639
|
+
if (peer.connection._connected) {
|
|
640
|
+
if (peer.id.toString() !== this.peerId.toString()) {
|
|
641
|
+
const node = await this.prepareMessage(peer.id, data.encoded)
|
|
642
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
this.removePeer(peer)
|
|
646
|
+
}
|
|
647
|
+
// TODO: if peer subscribed
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
createHash(data, name) {
|
|
652
|
+
return new Hash(data, {name})
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
*
|
|
657
|
+
* @param {String} topic
|
|
658
|
+
* @param {Method} cb
|
|
659
|
+
*/
|
|
660
|
+
async subscribe(topic, cb) {
|
|
661
|
+
// TODO: if peer subscribed
|
|
662
|
+
globalSub.subscribe(topic, cb)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
async removePeer(peer) {
|
|
666
|
+
connections.delete(peer.id)
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
get Buffer() {
|
|
670
|
+
return Buffer
|
|
671
|
+
}
|
|
672
|
+
// async block(index) {
|
|
673
|
+
// const _values = []
|
|
674
|
+
// for (const peer of this.peers) {
|
|
675
|
+
// const value = await peer.request({type: 'block', index})
|
|
676
|
+
// console.log(value);
|
|
677
|
+
// }
|
|
678
|
+
//
|
|
679
|
+
// }
|
|
680
|
+
}
|