@leofcoin/peernet 0.10.8 → 0.11.2
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/LICENSE +1 -1
- package/README.md +49 -49
- package/coverage/lcov-report/block-navigation.js +8 -0
- package/coverage/lcov-report/codec-format-interface.js.html +224 -120
- package/coverage/lcov-report/dht-response.js.html +44 -39
- package/coverage/lcov-report/index.html +39 -64
- package/coverage/lcov-report/sorter.js +26 -0
- package/coverage/lcov.info +164 -424
- package/dist/browser/326.peernet.js +29 -0
- package/dist/browser/peernet.js +84258 -95769
- package/dist/commonjs/client-1a1f75e6.js +324 -0
- package/dist/commonjs/{codec-6367213c.js → codec-8c8c652f.js} +198 -188
- package/dist/commonjs/codec-format-interface.js +169 -152
- package/dist/commonjs/codec.js +4 -4
- package/dist/commonjs/dht-response.js +13 -13
- package/dist/commonjs/dht.js +24 -24
- package/dist/commonjs/hash.js +151 -141
- package/dist/commonjs/{http-a94c5a81.js → http-4bc6caeb.js} +19 -15
- package/dist/commonjs/peernet-message.js +15 -15
- package/dist/commonjs/peernet.js +1901 -1794
- package/dist/commonjs/request.js +13 -13
- package/dist/commonjs/response.js +13 -13
- package/dist/module/peernet.js +2462 -2348
- package/index.html +5 -7
- package/package.json +22 -14
- package/rollup.config.js +33 -5
- package/rollup0.config.js +7 -0
- package/src/client.js +75 -75
- package/src/codec/codec-format-interface.js +172 -155
- package/src/codec/codec.js +124 -114
- package/src/codec/codecs.js +79 -79
- package/src/dht/dht.js +121 -121
- package/src/discovery/peer-discovery.js +75 -75
- package/src/handlers/message.js +50 -52
- package/src/hash/hash.js +155 -145
- package/src/http/client/http-client.js +44 -44
- package/src/messages/chat-message.js +14 -14
- package/src/messages/data-response.js +14 -14
- package/src/messages/data.js +18 -18
- package/src/messages/dht-response.js +14 -15
- package/src/messages/dht.js +25 -25
- package/src/messages/peer-response.js +14 -14
- package/src/messages/peer.js +14 -14
- package/src/messages/peernet-message.js +14 -14
- package/src/messages/ps.js +14 -14
- package/src/messages/request.js +14 -14
- package/src/messages/response.js +14 -14
- package/src/peer.js +67 -67
- package/src/peernet.js +614 -697
- package/src/proto/chat-message.proto.js +7 -7
- package/src/proto/peernet.proto.js +2 -2
- package/src/utils/utils.js +78 -78
- package/test/codec.js +3 -2
- package/test/messages.js +7 -4
- package/test.js +11 -4
- package/webpack.config.js +41 -0
- package/coverage/lcov-report/codec.js.html +0 -677
- package/coverage/lcov-report/hash.js.html +0 -551
- package/debug.log +0 -3
- package/dist/browser/peernet.js.tmp-browserify-14074318104595318069 +0 -0
- package/dist/browser/peernet.js.tmp-browserify-45407634493269122267 +0 -0
- package/dist/browser/peernet.js.tmp-browserify-53722389064799025427 +0 -0
- package/dist/browser/peernet.js.tmp-browserify-96323030449218949300 +0 -0
- package/dist/codec/codec-format-interface.js +0 -433
- package/dist/codec/codec.js +0 -199
- package/dist/commonjs/codec-73adfc0f.js +0 -205
- package/dist/commonjs/http-2c603501.js +0 -324
- package/dist/commonjs/http-42a6e555.js +0 -324
- package/dist/commonjs/http-43f4fafe.js +0 -324
- package/dist/commonjs/peernet-message-b6925673.js +0 -32
- package/dist/hash/hash.js +0 -340
- package/dist/messages/dht-response.js +0 -454
- package/dist/messages/dht.js +0 -453
- package/dist/messages/peernet.js +0 -456
- package/dist/module/http-273664bd.js +0 -317
- package/dist/module/http-8fe3c0d7.js +0 -317
- package/dist/module/http-c780c991.js +0 -317
- package/dist/module/http-f13e0d77.js +0 -317
package/dist/module/peernet.js
CHANGED
|
@@ -1,779 +1,589 @@
|
|
|
1
|
-
import
|
|
2
|
-
import sha256 from 'crypto-js/sha256';
|
|
3
|
-
import P2P from 'p2pt';
|
|
4
|
-
import LeofcoinStorage$1 from '@leofcoin/storage';
|
|
5
|
-
import { server } from 'websocket';
|
|
6
|
-
import { createServer } from 'http';
|
|
7
|
-
import Koa from 'koa';
|
|
1
|
+
import LeofcoinStorage from '@leofcoin/storage';
|
|
8
2
|
import protons from 'protons';
|
|
9
|
-
import bs32 from '
|
|
10
|
-
import bs58 from '
|
|
11
|
-
import isHex from 'is-hex';
|
|
3
|
+
import bs32 from '@vandeurenglenn/base32';
|
|
4
|
+
import bs58 from '@vandeurenglenn/base58';
|
|
5
|
+
import isHex from '@vandeurenglenn/is-hex';
|
|
12
6
|
import varint from 'varint';
|
|
13
7
|
import createKeccakHash from 'keccak';
|
|
14
|
-
import fetch
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
resolve(message.data);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
pubsub.subscribe('peernet.data', _onData);
|
|
44
|
-
|
|
45
|
-
// cleanup subscriptions
|
|
46
|
-
setTimeout(() => {
|
|
47
|
-
pubsub.unsubscribe('peernet.data', _onData);
|
|
48
|
-
}, 5000);
|
|
49
|
-
|
|
50
|
-
this.write(data);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
write(data) {
|
|
55
|
-
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
56
|
-
|
|
57
|
-
this.bw.up += data.length;
|
|
58
|
-
this.connection.write(data);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
on(event = 'peernet.data', cb) {
|
|
62
|
-
this._events[event] = cb;
|
|
63
|
-
pubsub.subscribe(event, cb);
|
|
64
|
-
// this.connection.on(event, cb)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
removeListener(event = 'data', cb) {
|
|
68
|
-
delete this._events[event];
|
|
69
|
-
pubsub.unsubscribe(event, cb);
|
|
8
|
+
import fetch from 'node-fetch';
|
|
9
|
+
import generateAccount from '@leofcoin/generate-account';
|
|
10
|
+
import * as bs58check from 'bs58check';
|
|
11
|
+
import bs58check__default from 'bs58check';
|
|
12
|
+
import { generateMnemonic, mnemonicToSeed } from 'bip39';
|
|
13
|
+
import * as bip32 from 'bip32';
|
|
14
|
+
import { publicKeyConvert } from 'secp256k1';
|
|
15
|
+
import ecc from 'tiny-secp256k1';
|
|
16
|
+
import MultiSignature from 'multi-signature';
|
|
17
|
+
import AES from 'crypto-js/aes.js';
|
|
18
|
+
import 'crypto-js/sha512.js';
|
|
19
|
+
import ENC from 'crypto-js/enc-utf8.js';
|
|
20
|
+
|
|
21
|
+
/* socket-request-client version 1.6.1 */
|
|
22
|
+
|
|
23
|
+
class LittlePubSub {
|
|
24
|
+
constructor(verbose = true) {
|
|
25
|
+
this.subscribers = {};
|
|
26
|
+
this.verbose = verbose;
|
|
27
|
+
}
|
|
28
|
+
subscribe(event, handler, context) {
|
|
29
|
+
if (typeof context === 'undefined') {
|
|
30
|
+
context = handler;
|
|
31
|
+
}
|
|
32
|
+
this.subscribers[event] = this.subscribers[event] || { handlers: [], value: null};
|
|
33
|
+
this.subscribers[event].handlers.push(handler.bind(context));
|
|
70
34
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
pubsub.unsubscribe(event, this._events[event]);
|
|
35
|
+
unsubscribe(event, handler, context) {
|
|
36
|
+
if (typeof context === 'undefined') {
|
|
37
|
+
context = handler;
|
|
75
38
|
}
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.
|
|
39
|
+
if (this.subscribers[event]) {
|
|
40
|
+
const index = this.subscribers[event].handlers.indexOf(handler.bind(context));
|
|
41
|
+
this.subscribers[event].handlers.splice(index);
|
|
42
|
+
if (this.subscribers[event].handlers.length === 0) delete this.subscribers[event];
|
|
80
43
|
}
|
|
81
|
-
this.connection.destroy();
|
|
82
44
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
globalThis.connections = new Map();
|
|
91
|
-
globalThis.recentConnections = new Map();
|
|
92
|
-
globalThis.pubsub = globalThis.pubsub || new PubSub({verbose: false});
|
|
93
|
-
|
|
94
|
-
class PeernetClient {
|
|
95
|
-
constructor(options = {}) {
|
|
96
|
-
if (!options.id) options.id = Buffer.from('00000000000000000000000000000000');
|
|
97
|
-
if (!options.networkVersion) options.networkVersion = 'v0.1.0';
|
|
98
|
-
if (!options.networkName) options.networkName = 'peernet';
|
|
99
|
-
this.id = options.id;
|
|
100
|
-
|
|
101
|
-
this.topic = Buffer.from(sha256(`${options.networkName}-${options.networkVersion}`).toString());
|
|
102
|
-
|
|
103
|
-
const trackers = [
|
|
104
|
-
'wss://star.leofcoin.org',
|
|
105
|
-
'wss://tracker.openwebtorrent.com',
|
|
106
|
-
// 'wss://tracker.sloppyta.co:443/announce',
|
|
107
|
-
];
|
|
108
|
-
this.p2p = new P2P(trackers, this.topic.slice(0, 20));
|
|
109
|
-
this.p2p.on('peerconnect', (peer) => {
|
|
110
|
-
peer = new PeernetPeer(peer.id, peer);
|
|
111
|
-
connections.set(peer.id, peer);
|
|
112
|
-
pubsub.publish('peer:discovered', peer);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
this.p2p.on('peerclose', (peer) => {
|
|
116
|
-
// TODO: close peernetPeer
|
|
117
|
-
const peernetPeer = connections.get(peer.id);
|
|
118
|
-
if (peernetPeer) {
|
|
119
|
-
peernetPeer.close();
|
|
45
|
+
publish(event, change) {
|
|
46
|
+
if (this.subscribers[event]) {
|
|
47
|
+
if (this.verbose || this.subscribers[event].value !== change) {
|
|
48
|
+
this.subscribers[event].value = change;
|
|
49
|
+
this.subscribers[event].handlers.forEach(handler => {
|
|
50
|
+
handler(change, this.subscribers[event].value);
|
|
51
|
+
});
|
|
120
52
|
}
|
|
121
|
-
connections.delete(peer.id);
|
|
122
|
-
pubsub.publish('peer:disconnected', peer);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
this.p2p.start();
|
|
126
|
-
|
|
127
|
-
if (globalThis.process) {
|
|
128
|
-
process.on('SIGINT', async () => {
|
|
129
|
-
console.log('Caught interrupt signal');
|
|
130
|
-
this.close();
|
|
131
|
-
setTimeout(async () => {
|
|
132
|
-
process.exit();
|
|
133
|
-
}, 100);
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
globalThis.onbeforeunload = () => {
|
|
137
|
-
this.close();
|
|
138
|
-
};
|
|
139
53
|
}
|
|
140
|
-
//
|
|
141
|
-
// this.sw.on('close', () => {
|
|
142
|
-
// })
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
close() {
|
|
146
|
-
return this.p2p.destroy()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
_peers() {
|
|
150
|
-
return this.p2p.getPeers()
|
|
151
54
|
}
|
|
152
55
|
}
|
|
153
56
|
|
|
154
|
-
var
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
if (method === 'remove') {
|
|
176
|
-
await globalThis[name].remove(key, value);
|
|
177
|
-
return send('ok')
|
|
178
|
-
}
|
|
179
|
-
value = await globalThis[name].get(key);
|
|
180
|
-
return send(value)
|
|
181
|
-
} catch (e) {
|
|
182
|
-
return error(e)
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
getConfig: async (params, {send, error}) => {
|
|
186
|
-
try {
|
|
187
|
-
const config = await api.getConfig(params);
|
|
188
|
-
send(config);
|
|
189
|
-
} catch (e) {
|
|
190
|
-
error(e);
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
setMinerConfig: async (params, {send, error}) => {
|
|
194
|
-
try {
|
|
195
|
-
await api.setMinerConfig(params);
|
|
196
|
-
send('ok');
|
|
197
|
-
} catch (e) {
|
|
198
|
-
error(e);
|
|
57
|
+
var clientApi = _pubsub => {
|
|
58
|
+
const subscribe = (topic, cb) => {
|
|
59
|
+
_pubsub.subscribe(topic, cb);
|
|
60
|
+
};
|
|
61
|
+
const unsubscribe = (topic, cb) => {
|
|
62
|
+
_pubsub.unsubscribe(topic, cb);
|
|
63
|
+
};
|
|
64
|
+
const publish = (topic, value) => {
|
|
65
|
+
_pubsub.publish(topic, value);
|
|
66
|
+
};
|
|
67
|
+
const _connectionState = (state) => {
|
|
68
|
+
switch (state) {
|
|
69
|
+
case 0:
|
|
70
|
+
return 'connecting'
|
|
71
|
+
case 1:
|
|
72
|
+
return 'open'
|
|
73
|
+
case 2:
|
|
74
|
+
return 'closing'
|
|
75
|
+
case 3:
|
|
76
|
+
return 'closed'
|
|
199
77
|
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
78
|
+
};
|
|
79
|
+
const request = (client, request) => {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const state = _connectionState(client.readyState);
|
|
82
|
+
if (state !== 'open') return reject(`coudn't send request to ${client.id}, no open connection found.`)
|
|
83
|
+
request.id = Math.random().toString(36).slice(-12);
|
|
84
|
+
const handler = result => {
|
|
85
|
+
if (result && result.error) return reject(result.error)
|
|
86
|
+
resolve({result, id: request.id, handler});
|
|
87
|
+
unsubscribe(request.id, handler);
|
|
88
|
+
};
|
|
89
|
+
subscribe(request.id, handler);
|
|
90
|
+
send(client, request);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const send = async (client, request) => {
|
|
94
|
+
return client.send(JSON.stringify(request))
|
|
95
|
+
};
|
|
96
|
+
const pubsub = client => {
|
|
97
|
+
return {
|
|
98
|
+
publish: (topic = 'pubsub', value) => {
|
|
99
|
+
return send(client, {url: 'pubsub', params: { topic, value }})
|
|
100
|
+
},
|
|
101
|
+
subscribe: (topic = 'pubsub', cb) => {
|
|
102
|
+
subscribe(topic, cb);
|
|
103
|
+
return send(client, {url: 'pubsub', params: { topic, subscribe: true }})
|
|
104
|
+
},
|
|
105
|
+
unsubscribe: (topic = 'pubsub', cb) => {
|
|
106
|
+
unsubscribe(topic, cb);
|
|
107
|
+
return send(client, {url: 'pubsub', params: { topic, unsubscribe: true }})
|
|
108
|
+
},
|
|
109
|
+
subscribers: _pubsub.subscribers
|
|
207
110
|
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
111
|
+
};
|
|
112
|
+
const server = (client) => {
|
|
113
|
+
return {
|
|
114
|
+
uptime: async () => {
|
|
115
|
+
try {
|
|
116
|
+
const { result, id, handler } = await request(client, {url: 'uptime'});
|
|
117
|
+
unsubscribe(id, handler);
|
|
118
|
+
return result
|
|
119
|
+
} catch (e) {
|
|
120
|
+
throw e
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
ping: async () => {
|
|
124
|
+
try {
|
|
125
|
+
const now = new Date().getTime();
|
|
126
|
+
const { result, id, handler } = await request(client, {url: 'ping'});
|
|
127
|
+
unsubscribe(id, handler);
|
|
128
|
+
return (Number(result) - now)
|
|
129
|
+
} catch (e) {
|
|
130
|
+
throw e
|
|
131
|
+
}
|
|
132
|
+
}
|
|
219
133
|
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
134
|
+
};
|
|
135
|
+
const peernet = (client) => {
|
|
136
|
+
return {
|
|
137
|
+
join: async (params) => {
|
|
138
|
+
try {
|
|
139
|
+
params.join = true;
|
|
140
|
+
const requested = { url: 'peernet', params };
|
|
141
|
+
const { result, id, handler } = await request(client, requested);
|
|
142
|
+
unsubscribe(id, handler);
|
|
143
|
+
return result
|
|
144
|
+
} catch (e) {
|
|
145
|
+
throw e
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
leave: async (params) => {
|
|
149
|
+
try {
|
|
150
|
+
params.join = false;
|
|
151
|
+
const requested = { url: 'peernet', params };
|
|
152
|
+
const { result, id, handler } = await request(client, requested);
|
|
153
|
+
unsubscribe(id, handler);
|
|
154
|
+
return result
|
|
155
|
+
} catch (e) {
|
|
156
|
+
throw e
|
|
157
|
+
}
|
|
158
|
+
}
|
|
227
159
|
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} catch (e) {
|
|
244
|
-
console.log(e);
|
|
245
|
-
error(e);
|
|
160
|
+
};
|
|
161
|
+
return { send, request, pubsub, server, subscribe, unsubscribe, publish, peernet }
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
if (!globalThis.PubSub) globalThis.PubSub = LittlePubSub;
|
|
165
|
+
if (!globalThis.pubsub) globalThis.pubsub = new LittlePubSub({verbose: false});
|
|
166
|
+
const socketRequestClient = (url, protocols = 'echo-protocol', options = { retry: false }) => {
|
|
167
|
+
const { retry } = options;
|
|
168
|
+
const api = clientApi(pubsub);
|
|
169
|
+
let tries = 0;
|
|
170
|
+
const onerror = error => {
|
|
171
|
+
if (pubsub.subscribers['error']) {
|
|
172
|
+
pubsub.publish('error', error);
|
|
173
|
+
} else {
|
|
174
|
+
console.error(error);
|
|
246
175
|
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
176
|
+
};
|
|
177
|
+
const onmessage = message => {
|
|
178
|
+
const {value, url, status, id} = JSON.parse(message.data.toString());
|
|
179
|
+
const publisher = id ? id : url;
|
|
180
|
+
if (status === 200) {
|
|
181
|
+
pubsub.publish(publisher, value);
|
|
182
|
+
} else {
|
|
183
|
+
pubsub.publish(publisher, {error: value});
|
|
254
184
|
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
185
|
+
};
|
|
186
|
+
const clientConnection = client => {
|
|
187
|
+
const startTime = new Date().getTime();
|
|
188
|
+
return {
|
|
189
|
+
client,
|
|
190
|
+
request: async req => {
|
|
191
|
+
const { result, id, handler } = await api.request(client, req);
|
|
192
|
+
pubsub.unsubscribe(id, handler);
|
|
193
|
+
return result
|
|
194
|
+
},
|
|
195
|
+
send: req => api.send(client, req),
|
|
196
|
+
subscribe: api.subscribe,
|
|
197
|
+
unsubscribe: api.unsubscribe,
|
|
198
|
+
subscribers: api.subscribers,
|
|
199
|
+
publish: api.publish,
|
|
200
|
+
pubsub: api.pubsub(client),
|
|
201
|
+
uptime: () => {
|
|
202
|
+
const now = new Date().getTime();
|
|
203
|
+
return (now - startTime)
|
|
204
|
+
},
|
|
205
|
+
peernet: api.peernet(client),
|
|
206
|
+
server: api.server(client),
|
|
207
|
+
close: exit => {
|
|
208
|
+
client.onclose = message => {
|
|
209
|
+
if (exit) process.exit();
|
|
210
|
+
};
|
|
211
|
+
client.close();
|
|
212
|
+
}
|
|
266
213
|
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
214
|
+
};
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
const init = () => {
|
|
217
|
+
let ws;
|
|
218
|
+
if (typeof process === 'object' && !globalThis.WebSocket) {
|
|
219
|
+
ws = require('websocket').w3cwebsocket;
|
|
220
|
+
} else {
|
|
221
|
+
ws = WebSocket;
|
|
222
|
+
}
|
|
223
|
+
const client = new ws(url, protocols);
|
|
224
|
+
client.onmessage = onmessage;
|
|
225
|
+
client.onerror = onerror;
|
|
226
|
+
client.onopen = () => {
|
|
227
|
+
tries = 0;
|
|
228
|
+
resolve(clientConnection(client));
|
|
229
|
+
};
|
|
230
|
+
client.onclose = message => {
|
|
231
|
+
tries++;
|
|
232
|
+
if (!retry) return reject(options)
|
|
233
|
+
if (tries > 5) {
|
|
234
|
+
console.log(`${protocols} Client Closed`);
|
|
235
|
+
console.error(`could not connect to - ${url}/`);
|
|
236
|
+
return resolve(clientConnection(client))
|
|
237
|
+
}
|
|
238
|
+
if (message.code === 1006) {
|
|
239
|
+
console.log('Retrying in 10 seconds');
|
|
240
|
+
setTimeout(() => {
|
|
241
|
+
return init();
|
|
242
|
+
}, retry);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
return init();
|
|
247
|
+
});
|
|
272
248
|
};
|
|
273
249
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
250
|
+
class Peer {
|
|
251
|
+
#connection
|
|
252
|
+
#ready = false
|
|
253
|
+
#connecting = false
|
|
254
|
+
#connected = false
|
|
255
|
+
#channelReady = false
|
|
256
|
+
#destroying = false
|
|
257
|
+
#destroyed = false
|
|
258
|
+
#isNegotiating = false
|
|
259
|
+
#firstNegotiation = true
|
|
260
|
+
#iceComplete = false
|
|
261
|
+
#remoteTracks = []
|
|
262
|
+
#remoteStreams = []
|
|
263
|
+
#pendingCandidates = []
|
|
264
|
+
#senderMap = new Map()
|
|
265
|
+
#iceCompleteTimer
|
|
266
|
+
#channel
|
|
267
|
+
|
|
268
|
+
get connection() {
|
|
269
|
+
return this.#connection
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
get connected() {
|
|
273
|
+
return this.#connected
|
|
274
|
+
}
|
|
279
275
|
|
|
280
276
|
/**
|
|
281
|
-
* @
|
|
282
|
-
*
|
|
283
|
-
* @param {object} connection socket connection
|
|
284
|
-
* @param {string} route the route to handle
|
|
285
|
-
*/
|
|
286
|
-
var socketConnection = (request, protocol, origin) => {
|
|
287
|
-
if (origin && !originIsAllowed(request.origin, origin)) {
|
|
288
|
-
// Make sure we only accept requests from an allowed origin
|
|
289
|
-
request.reject();
|
|
290
|
-
fullLog(`Connection from origin ${request.origin} rejected.`);
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
// console.log(request);
|
|
294
|
-
const connection = request.accept(protocol, request.origin);
|
|
295
|
-
fullLog(`Connection accepted @${protocol}`);
|
|
296
|
-
return connection;
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* @module socketResponse
|
|
301
|
-
*
|
|
302
|
-
* @param {object} connection socket connection
|
|
303
|
-
* @param {string} url the request url
|
|
277
|
+
* @params {Object} options
|
|
278
|
+
* @params {string} options.channelName - this peerid : otherpeer id
|
|
304
279
|
*/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const socketRequestServer = (options, routes = {}) => {
|
|
318
|
-
// if (!routes && !routes.port && options) routes = options;
|
|
319
|
-
// else if (!options && !routes) return console.error('no routes defined');
|
|
320
|
-
|
|
321
|
-
let {httpServer, httpsServer, port, protocol, credentials, origin, pubsub } = options;
|
|
322
|
-
if (!pubsub) pubsub = new PubSub({verbose: false});
|
|
323
|
-
if (!port) port = 6000;
|
|
324
|
-
const connections = [];
|
|
325
|
-
let connection;
|
|
326
|
-
const startTime = new Date().getTime();
|
|
327
|
-
// default routes
|
|
328
|
-
if (!routes.ping) routes.ping = (response) => response.send(new Date().getTime());
|
|
329
|
-
if (!routes.uptime) routes.uptime = (response) => {
|
|
330
|
-
const now = new Date().getTime();
|
|
331
|
-
response.send(now - startTime);
|
|
280
|
+
constructor(options = {}) {
|
|
281
|
+
this._in = this._in.bind(this);
|
|
282
|
+
this.offerOptions = options.offerOptions;
|
|
283
|
+
this.initiator = options.initiator;
|
|
284
|
+
this.streams = options.streams;
|
|
285
|
+
this.socketClient = options.socketClient;
|
|
286
|
+
this.id = options.id;
|
|
287
|
+
this.to = options.to;
|
|
288
|
+
this.bw = {
|
|
289
|
+
up: 0,
|
|
290
|
+
down: 0
|
|
332
291
|
};
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
// }));
|
|
365
|
-
// }
|
|
366
|
-
pubsub.publish(topic, params.value);
|
|
367
|
-
response.send('ok', 200);
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
globalThis.peerMap = new Map();
|
|
372
|
-
if (!routes.peernet) {
|
|
373
|
-
routes.peernet = (params, response) => {
|
|
374
|
-
if (params.join) {
|
|
375
|
-
peerMap.set(params.peerId, params.address);
|
|
376
|
-
response.send([...peerMap.values()]);
|
|
377
|
-
for (const connection of connections) {
|
|
378
|
-
if (connection !== response.connection)
|
|
379
|
-
socketResponse(connection, 'peernet', 'peernet').send({discovered: params.address});
|
|
380
|
-
}
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
if (!params.join) {
|
|
384
|
-
peerMap.delete(params.peerId);
|
|
385
|
-
return response.send()
|
|
292
|
+
|
|
293
|
+
this.channelName = options.channelName || Buffer.from(Math.random().toString(36).slice(-12)).toString('hex');
|
|
294
|
+
console.log(this.channelName);
|
|
295
|
+
this.options = options;
|
|
296
|
+
this.#init();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
set socketClient(value) {
|
|
300
|
+
// this.socketClient?.pubsub.unsubscribe('signal', this._in)
|
|
301
|
+
this._socketClient = value;
|
|
302
|
+
this._socketClient.pubsub.subscribe('signal', this._in);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
get socketClient() {
|
|
306
|
+
return this._socketClient
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
send(message) {
|
|
310
|
+
this.bw.up += message.length;
|
|
311
|
+
this.channel.send(message);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
request(data) {
|
|
315
|
+
return new Promise((resolve, reject) => {
|
|
316
|
+
const id = Math.random().toString(36).slice(-12);
|
|
317
|
+
data = new TextEncoder().encode(JSON.stringify({id, data}));
|
|
318
|
+
const _onData = message => {
|
|
319
|
+
message = JSON.parse(new TextDecoder().decode(message.data));
|
|
320
|
+
if (message.id === id) {
|
|
321
|
+
resolve(message.data);
|
|
322
|
+
pubsub.unsubscribe('peer:data', _onData);
|
|
386
323
|
}
|
|
387
324
|
};
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
325
|
+
|
|
326
|
+
pubsub.subscribe('peer:data', _onData);
|
|
327
|
+
|
|
328
|
+
// cleanup subscriptions
|
|
329
|
+
setTimeout(() => {
|
|
330
|
+
pubsub.unsubscribe('peer:data', _onData);
|
|
331
|
+
}, 5000);
|
|
332
|
+
|
|
333
|
+
this.send(data);
|
|
397
334
|
});
|
|
398
335
|
}
|
|
399
336
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
337
|
+
async #init() {
|
|
338
|
+
try {
|
|
339
|
+
const iceServers = [{
|
|
340
|
+
urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
|
|
341
|
+
}];
|
|
404
342
|
|
|
405
|
-
|
|
343
|
+
this.#connection = new wrtc.RTCPeerConnection();
|
|
344
|
+
this.#connection.onicecandidate = ({ candidate }) => {
|
|
345
|
+
if (candidate) {
|
|
346
|
+
this.address = candidate.address;
|
|
347
|
+
this.port = candidate.port;
|
|
348
|
+
this.protocol = candidate.protocol;
|
|
349
|
+
this.ipFamily = this.address.includes('::') ? 'ipv6': 'ipv4';
|
|
350
|
+
this._sendMessage({candidate});
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
// if (this.initiator) this.#connection.onnegotiationneeded = () => {
|
|
354
|
+
// console.log('create offer');
|
|
355
|
+
this.#connection.ondatachannel = (message) => {
|
|
356
|
+
message.channel.onopen = () => {
|
|
357
|
+
this.#connected = true;
|
|
358
|
+
pubsub.publish('peer:connected', this);
|
|
359
|
+
};
|
|
360
|
+
message.channel.onclose = () => console.log('close');
|
|
361
|
+
message.channel.onmessage = (message) => {
|
|
362
|
+
if (message.to) {
|
|
363
|
+
if (message.to === this.id) pubsub.publish('peer:data', message);
|
|
364
|
+
} else pubsub.publish('peer:data', message);
|
|
365
|
+
this.bw.down += message.length;
|
|
366
|
+
};
|
|
367
|
+
this.channel = message.channel;
|
|
368
|
+
};
|
|
369
|
+
if (this.initiator) {
|
|
406
370
|
|
|
407
|
-
|
|
408
|
-
|
|
371
|
+
this.channel = this.#connection.createDataChannel('messageChannel');
|
|
372
|
+
this.channel.onopen = () => {
|
|
373
|
+
this.#connected = true;
|
|
374
|
+
pubsub.publish('peer:connected', this);
|
|
375
|
+
// this.channel.send('hi')
|
|
376
|
+
};
|
|
377
|
+
this.channel.onclose = () => this.close.bind(this);
|
|
409
378
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
const { route, params, url, id, customEvent } = JSON.parse(data);
|
|
423
|
-
// ignore api when customEvent is defined
|
|
424
|
-
if (customEvent) return;
|
|
425
|
-
if (routes[url]) {
|
|
426
|
-
if (!params) return routes[url](socketResponse(connection, url, id));
|
|
427
|
-
return routes[url](params, socketResponse(connection, url, id));
|
|
379
|
+
this.channel.onmessage = (message) => {
|
|
380
|
+
if (message.to) {
|
|
381
|
+
if (message.to === this.id) pubsub.publish('peer:data', message);
|
|
382
|
+
} else pubsub.publish('peer:data', message);
|
|
383
|
+
this.bw.down += message.length;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const offer = await this.#connection.createOffer();
|
|
387
|
+
await this.#connection.setLocalDescription(offer);
|
|
388
|
+
|
|
389
|
+
this._sendMessage({'sdp': this.#connection.localDescription});
|
|
428
390
|
}
|
|
429
|
-
|
|
430
|
-
};
|
|
391
|
+
// }
|
|
431
392
|
|
|
432
|
-
|
|
433
|
-
|
|
393
|
+
} catch (e) {
|
|
394
|
+
console.log(e);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
434
397
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (!config.protocol) config.protocol = 'peernet-v0.1.0';
|
|
444
|
-
if (!config.port) config.port = 2000;
|
|
445
|
-
if (!config.host) config.host = '127.0.0.1';
|
|
446
|
-
|
|
447
|
-
const app = new Koa();
|
|
448
|
-
|
|
449
|
-
app.use(async (ctx) => {
|
|
450
|
-
const url = ctx.url.split('/api/')[1];
|
|
451
|
-
if (url === 'version') ctx.body = {client: '@peernet/api/http', version};
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
const httpServer = createServer(app.callback());
|
|
455
|
-
|
|
456
|
-
config.httpServer = httpServer;
|
|
457
|
-
|
|
458
|
-
httpServer.listen(config.port, () => {
|
|
459
|
-
console.log(`listening on ${config.port}`);
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
return socketRequestServer(config, api$1)
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
var clientApi = _pubsub => {
|
|
466
|
-
|
|
467
|
-
const subscribe = (topic, cb) => {
|
|
468
|
-
_pubsub.subscribe(topic, cb);
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
const unsubscribe = (topic, cb) => {
|
|
472
|
-
_pubsub.unsubscribe(topic, cb);
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
const publish = (topic, value) => {
|
|
476
|
-
_pubsub.publish(topic, value);
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
const _connectionState = (state) => {
|
|
480
|
-
switch (state) {
|
|
481
|
-
case 0:
|
|
482
|
-
return 'connecting'
|
|
483
|
-
case 1:
|
|
484
|
-
return 'open'
|
|
485
|
-
case 2:
|
|
486
|
-
return 'closing'
|
|
487
|
-
case 3:
|
|
488
|
-
return 'closed'
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
/**
|
|
492
|
-
* @param {string} type
|
|
493
|
-
* @param {string} name
|
|
494
|
-
* @param {object} params
|
|
495
|
-
*/
|
|
496
|
-
const request = (client, request) => {
|
|
497
|
-
return new Promise((resolve, reject) => {
|
|
498
|
-
|
|
499
|
-
const state = _connectionState(client.readyState);
|
|
500
|
-
if (state !== 'open') return reject(`coudn't send request to ${client.id}, no open connection found.`)
|
|
501
|
-
|
|
502
|
-
request.id = Math.random().toString(36).slice(-12);
|
|
503
|
-
const handler = result => {
|
|
504
|
-
if (result && result.error) return reject(result.error)
|
|
505
|
-
resolve({result, id: request.id, handler});
|
|
506
|
-
};
|
|
507
|
-
subscribe(request.id, handler);
|
|
508
|
-
send(client, request);
|
|
509
|
-
});
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
const send = async (client, request) => {
|
|
513
|
-
return client.send(JSON.stringify(request))
|
|
514
|
-
};
|
|
398
|
+
_sendMessage(message) {
|
|
399
|
+
this.socketClient.send({url: 'signal', params: {
|
|
400
|
+
to: this.to,
|
|
401
|
+
from: this.id,
|
|
402
|
+
channelName: this.options.channelName,
|
|
403
|
+
...message
|
|
404
|
+
}});
|
|
405
|
+
}
|
|
515
406
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return send(client, {url: 'pubsub', params: { topic, unsubscribe: true }})
|
|
528
|
-
},
|
|
529
|
-
subscribers: _pubsub.subscribers
|
|
407
|
+
async _in(message, data) {
|
|
408
|
+
// message = JSON.parse(message);
|
|
409
|
+
if (message.to !== this.id) return
|
|
410
|
+
// if (data.videocall) return this._startStream(true, false); // start video and audio stream
|
|
411
|
+
// if (data.call) return this._startStream(true, true); // start audio stream
|
|
412
|
+
if (message.candidate) {
|
|
413
|
+
this.remoteAddress = message.candidate.address;
|
|
414
|
+
this.remotePort = message.candidate.port;
|
|
415
|
+
this.remoteProtocol = message.candidate.protocol;
|
|
416
|
+
this.remoteIpFamily = this.remoteAddress?.includes('::') ? 'ipv6': 'ipv4';
|
|
417
|
+
return this.#connection.addIceCandidate(new wrtc.RTCIceCandidate(message.candidate));
|
|
530
418
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
unsubscribe(id, handler);
|
|
539
|
-
return result
|
|
540
|
-
} catch (e) {
|
|
541
|
-
throw e
|
|
419
|
+
try {
|
|
420
|
+
if (message.sdp) {
|
|
421
|
+
if (message.sdp.type === 'offer') {
|
|
422
|
+
await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
|
|
423
|
+
const answer = await this.#connection.createAnswer();
|
|
424
|
+
await this.#connection.setLocalDescription(answer);
|
|
425
|
+
this._sendMessage({'sdp': this.#connection.localDescription});
|
|
542
426
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
try {
|
|
546
|
-
const now = new Date().getTime();
|
|
547
|
-
const { result, id, handler } = await request(client, {url: 'ping'});
|
|
548
|
-
unsubscribe(id, handler);
|
|
549
|
-
return (Number(result) - now)
|
|
550
|
-
} catch (e) {
|
|
551
|
-
throw e
|
|
427
|
+
if (message.sdp.type === 'answer') {
|
|
428
|
+
await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
|
|
552
429
|
}
|
|
430
|
+
}
|
|
431
|
+
} catch (e) {
|
|
432
|
+
console.log(e);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
close() {
|
|
437
|
+
this.channel?.close();
|
|
438
|
+
this.#connection?.close();
|
|
439
|
+
|
|
440
|
+
this.socketClient.pubsub.unsubscribe('signal', this._in);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
class Client {
|
|
445
|
+
#peerConnection
|
|
446
|
+
#connections = {}
|
|
447
|
+
#stars = {}
|
|
448
|
+
|
|
449
|
+
get connections() {
|
|
450
|
+
return { ...this.#connections }
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
constructor(id, identifiers = ['peernet-v0.1.0'], stars = []) {
|
|
454
|
+
this.id = id || Math.random().toString(36).slice(-12);
|
|
455
|
+
if (!Array.isArray(identifiers)) identifiers = [identifiers];
|
|
456
|
+
this.peerJoined = this.peerJoined.bind(this);
|
|
457
|
+
this.peerLeft = this.peerLeft.bind(this);
|
|
458
|
+
this.starLeft = this.starLeft.bind(this);
|
|
459
|
+
this.starJoined = this.starJoined.bind(this);
|
|
460
|
+
|
|
461
|
+
this._init(identifiers, stars);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async _init(identifiers, stars = []) {
|
|
465
|
+
if (stars.length === 0) {
|
|
466
|
+
stars.push('wss://star.leofcoin.org');
|
|
467
|
+
stars.push('ws://localhost:44444');
|
|
468
|
+
}
|
|
469
|
+
this.identifiers = identifiers;
|
|
470
|
+
this.starsConfig = stars;
|
|
471
|
+
// reconnectJob()
|
|
472
|
+
|
|
473
|
+
globalThis.wrtc = await import('wrtc');
|
|
474
|
+
for (const star of stars) {
|
|
475
|
+
try {
|
|
476
|
+
this.socketClient = await socketRequestClient(star, identifiers[0]);
|
|
477
|
+
const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
|
|
478
|
+
this.socketClient.peerId = id;
|
|
479
|
+
this.#stars[id] = this.socketClient;
|
|
480
|
+
} catch (e) {
|
|
481
|
+
if (stars.indexOf(star) === stars.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
|
|
553
482
|
}
|
|
554
483
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
484
|
+
const peers = await this.socketClient.peernet.join({id: this.id});
|
|
485
|
+
for (const id of peers) {
|
|
486
|
+
if (id !== this.id && !this.#connections[id]) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id});
|
|
487
|
+
}
|
|
488
|
+
this.setupListeners();
|
|
489
|
+
|
|
490
|
+
pubsub.subscribe('peer:connected', (peer) => {
|
|
491
|
+
// peer.send(JSON.stringify({data: 'hello', from: this.id, to: peer.to}))
|
|
492
|
+
console.log({peer: peer.to});
|
|
493
|
+
console.log({id: peer.id});
|
|
494
|
+
console.log({id: this.id});
|
|
495
|
+
});
|
|
496
|
+
// pubsub.subscribe('peer:data', (data) => console.log({data}))
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
setupListeners() {
|
|
500
|
+
this.socketClient.subscribe('peer:joined', this.peerJoined);
|
|
501
|
+
this.socketClient.subscribe('peer:left', this.peerLeft);
|
|
502
|
+
this.socketClient.subscribe('star:left', this.starLeft);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
starJoined(id) {
|
|
506
|
+
if (this.#stars[id]) {
|
|
507
|
+
this.#stars[id].close();
|
|
508
|
+
delete this.#stars[id];
|
|
509
|
+
}
|
|
510
|
+
console.log(`star ${id} joined`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
async starLeft(id) {
|
|
514
|
+
if (this.#stars[id]) {
|
|
515
|
+
this.#stars[id].close();
|
|
516
|
+
delete this.#stars[id];
|
|
517
|
+
}
|
|
518
|
+
if (this.socketClient?.peerId === id) {
|
|
519
|
+
|
|
520
|
+
this.socketClient.unsubscribe('peer:joined', this.peerJoined);
|
|
521
|
+
this.socketClient.unsubscribe('peer:left', this.peerLeft);
|
|
522
|
+
this.socketClient.unsubscribe('star:left', this.starLeft);
|
|
523
|
+
this.socketClient.close();
|
|
524
|
+
this.socketClient = undefined;
|
|
525
|
+
|
|
526
|
+
for (const star of this.starsConfig) {
|
|
571
527
|
try {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
528
|
+
this.socketClient = await socketRequestClient(star, this.identifiers[0]);
|
|
529
|
+
if (!this.socketClient?.client?._connection.connected) return
|
|
530
|
+
const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
|
|
531
|
+
this.#stars[id] = this.socketClient;
|
|
532
|
+
|
|
533
|
+
this.socketClient.peerId = id;
|
|
534
|
+
|
|
535
|
+
const peers = await this.socketClient.peernet.join({id: this.id});
|
|
536
|
+
this.setupListeners();
|
|
537
|
+
for (const id of peers) {
|
|
538
|
+
if (id !== this.id) {
|
|
539
|
+
// close connection
|
|
540
|
+
if (this.#connections[id]) {
|
|
541
|
+
if (this.#connections[id].connected) await this.#connections[id].close();
|
|
542
|
+
delete this.#connections[id];
|
|
543
|
+
}
|
|
544
|
+
// reconnect
|
|
545
|
+
if (id !== this.id) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
}
|
|
577
549
|
} catch (e) {
|
|
578
|
-
|
|
550
|
+
console.log(e);
|
|
551
|
+
if (this.starsConfig.indexOf(star) === this.starsConfig.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
|
|
579
552
|
}
|
|
580
553
|
}
|
|
581
554
|
}
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
return { send, request, pubsub, server, subscribe, unsubscribe, publish, peernet }
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
const socketRequestClient = (url, protocols = 'echo-protocol', options = { retry: false, pubsub: false }) => {
|
|
588
|
-
let { pubsub, retry } = options;
|
|
589
|
-
if (!pubsub) pubsub = new PubSub({verbose: false});
|
|
590
|
-
|
|
591
|
-
const api = clientApi(pubsub);
|
|
592
|
-
|
|
593
|
-
let tries = 0;
|
|
594
|
-
|
|
595
|
-
const onerror = error => {
|
|
596
|
-
if (pubsub.subscribers['error']) {
|
|
597
|
-
pubsub.publish('error', error);
|
|
598
|
-
} else {
|
|
599
|
-
console.error(error);
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
555
|
|
|
603
|
-
|
|
604
|
-
const {value, url, status, id} = JSON.parse(message.data.toString());
|
|
605
|
-
const publisher = id ? id : url;
|
|
606
|
-
if (status === 200) {
|
|
607
|
-
pubsub.publish(publisher, value);
|
|
608
|
-
} else {
|
|
609
|
-
pubsub.publish(publisher, {error: value});
|
|
610
|
-
}
|
|
556
|
+
console.log(`star ${id} left`);
|
|
611
557
|
|
|
612
|
-
};
|
|
613
558
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
pubsub.unsubscribe(id, handler);
|
|
621
|
-
return result
|
|
622
|
-
},
|
|
623
|
-
send: req => api.send(client, req),
|
|
624
|
-
subscribe: api.subscribe,
|
|
625
|
-
unsubscribe: api.unsubscribe,
|
|
626
|
-
subscribers: api.subscribers,
|
|
627
|
-
publish: api.publish,
|
|
628
|
-
pubsub: api.pubsub(client),
|
|
629
|
-
uptime: () => {
|
|
630
|
-
const now = new Date().getTime();
|
|
631
|
-
return (now - startTime)
|
|
632
|
-
},
|
|
633
|
-
peernet: api.peernet(client),
|
|
634
|
-
server: api.server(client),
|
|
635
|
-
close: exit => {
|
|
636
|
-
client.onclose = message => {
|
|
637
|
-
if (exit) process.exit();
|
|
638
|
-
};
|
|
639
|
-
client.close();
|
|
640
|
-
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
peerLeft(id) {
|
|
562
|
+
if (this.#connections[id]) {
|
|
563
|
+
this.#connections[id].close();
|
|
564
|
+
delete this.#connections[id];
|
|
641
565
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
566
|
+
console.log(`peer ${id} left`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
peerJoined(id, signal) {
|
|
570
|
+
if (this.#connections[id]) {
|
|
571
|
+
if (this.#connections[id].connected) this.#connections[id].close();
|
|
572
|
+
delete this.#connections[id];
|
|
573
|
+
}
|
|
574
|
+
// RTCPeerConnection
|
|
575
|
+
this.#connections[id] = new Peer({initiator: true, channelName: `${this.id}:${id}`, socketClient: this.socketClient, id: this.id, to: id});
|
|
576
|
+
console.log(`peer ${id} joined`);
|
|
577
|
+
}
|
|
578
|
+
|
|
653
579
|
|
|
654
|
-
client.onmessage = onmessage;
|
|
655
|
-
client.onerror = onerror;
|
|
656
|
-
client.onopen = () => {
|
|
657
|
-
tries = 0;
|
|
658
|
-
resolve(clientConnection(client));
|
|
659
|
-
};
|
|
660
|
-
client.onclose = message => {
|
|
661
|
-
tries++;
|
|
662
|
-
if (!retry) return reject(options)
|
|
663
|
-
if (tries > 5) {
|
|
664
|
-
console.log(`${protocols} Client Closed`);
|
|
665
|
-
console.error(`could not connect to - ${url}/`);
|
|
666
|
-
return resolve(clientConnection(client))
|
|
667
|
-
}
|
|
668
|
-
if (message.code === 1006) {
|
|
669
|
-
console.log('Retrying in 10 seconds');
|
|
670
|
-
setTimeout(() => {
|
|
671
|
-
return init();
|
|
672
|
-
}, retry);
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
};
|
|
676
|
-
return init();
|
|
677
|
-
});
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
class HttpClientApi$1 {
|
|
681
|
-
constructor(config = {}) {
|
|
682
|
-
if (!config.apiPath) config.apiPath = 'api';
|
|
683
|
-
|
|
684
|
-
const address = `ws://${config.host}:${config.port}`;
|
|
685
|
-
|
|
686
|
-
this.apiUrl = (url) => `${address}/${url}`;
|
|
687
|
-
return (async () => {
|
|
688
|
-
this.client = await socketRequestClient(address, config.protocol, {pubsub: config.pubsub, retry: 3000});
|
|
689
|
-
return this
|
|
690
|
-
})()
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
async get(url, obj) {
|
|
694
|
-
const headers = {};
|
|
695
|
-
let body = null;
|
|
696
|
-
let method = 'GET';
|
|
697
|
-
if (obj) {
|
|
698
|
-
method = 'POST';
|
|
699
|
-
headers['Content-Type'] = 'application/json';
|
|
700
|
-
body = JSON.stringify(obj);
|
|
701
|
-
}
|
|
702
|
-
let response = await this.client.request(url, {headers, body, method});
|
|
703
|
-
const type = response.headers.get('content-type').split(';')[0];
|
|
704
|
-
if (type==='application/json') response = await response.json();
|
|
705
|
-
return response
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
async put(url, obj) {
|
|
709
|
-
const headers = {};
|
|
710
|
-
let body = {};
|
|
711
|
-
if (obj) {
|
|
712
|
-
headers['Content-Type'] = 'application/json';
|
|
713
|
-
body = JSON.stringify(obj);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
let response = await fetch(this.apiUrl(url), {method: 'PUT', headers, body});
|
|
717
|
-
const type = response.headers.get('content-type').split(';')[0];
|
|
718
|
-
if (type==='application/json') response = await response.json();
|
|
719
|
-
return response
|
|
720
|
-
}
|
|
721
580
|
}
|
|
722
581
|
|
|
723
|
-
class
|
|
724
|
-
constructor(
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
this.properties = {
|
|
730
|
-
wallet: 'get',
|
|
731
|
-
version: 'get',
|
|
732
|
-
addresses: 'get',
|
|
733
|
-
config: 'get',
|
|
734
|
-
account: 'get',
|
|
735
|
-
accounts: 'get',
|
|
736
|
-
transaction: 'any',
|
|
737
|
-
transactions: 'get',
|
|
738
|
-
block: 'get',
|
|
739
|
-
blocks: 'get',
|
|
740
|
-
};
|
|
741
|
-
this.keys = Object.keys(this.properties);
|
|
742
|
-
return this
|
|
743
|
-
})()
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
async request(url, data) {
|
|
747
|
-
return await this.client.request({url, params: data})
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
async ready() {
|
|
751
|
-
return await this.request('ready')
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
async version() {
|
|
755
|
-
return await this.request('version')
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
async account(index) {
|
|
759
|
-
return await this.request('account', {index})
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
var httpClient = (config = {}) => {
|
|
764
|
-
if (typeof config !== 'object') config = {};
|
|
765
|
-
if (!config.protocol) config.protocol = 'peernet-v0.1.0';
|
|
766
|
-
if (!config.port) config.port = 1000;
|
|
767
|
-
if (!config.host) config.host = '127.0.0.1';
|
|
768
|
-
|
|
769
|
-
return new HttpClientApi(config)
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
class LeofcoinStorageClient {
|
|
773
|
-
constructor(name, root) {
|
|
774
|
-
this.name = name;
|
|
775
|
-
this.root = root;
|
|
776
|
-
}
|
|
582
|
+
class LeofcoinStorageClient {
|
|
583
|
+
constructor(name, root) {
|
|
584
|
+
this.name = name;
|
|
585
|
+
this.root = root;
|
|
586
|
+
}
|
|
777
587
|
|
|
778
588
|
async get(key) {
|
|
779
589
|
try {
|
|
@@ -811,606 +621,643 @@ var proto$a = `
|
|
|
811
621
|
message PeernetMessage {
|
|
812
622
|
required bytes data = 1;
|
|
813
623
|
required bytes signature = 2;
|
|
814
|
-
optional
|
|
815
|
-
optional
|
|
624
|
+
optional string from = 3;
|
|
625
|
+
optional string to = 4;
|
|
816
626
|
optional string id = 5;
|
|
817
627
|
}`;
|
|
818
628
|
|
|
819
|
-
var codecs = {
|
|
820
|
-
// just a hash
|
|
821
|
-
'disco-hash': {
|
|
822
|
-
codec: '30',
|
|
823
|
-
hashAlg: 'dbl-keccak-512', // ,
|
|
824
|
-
// testnet: 'olivia'
|
|
825
|
-
},
|
|
826
|
-
'peernet-peer-response': {
|
|
827
|
-
codec: '707072',
|
|
828
|
-
hashAlg: 'keccak-256',
|
|
829
|
-
},
|
|
830
|
-
'peernet-peer': {
|
|
831
|
-
codec: '7070',
|
|
832
|
-
hashAlg: 'keccak-256',
|
|
833
|
-
},
|
|
834
|
-
'peernet-dht': {
|
|
835
|
-
codec: '706468',
|
|
836
|
-
hashAlg: 'keccak-256',
|
|
837
|
-
},
|
|
838
|
-
'peernet-dht-response': {
|
|
839
|
-
codec: '706472',
|
|
840
|
-
hashAlg: 'keccak-256',
|
|
841
|
-
},
|
|
842
|
-
// data
|
|
843
|
-
'peernet-data': {
|
|
844
|
-
codec: '706461',
|
|
845
|
-
hashAlg: 'keccak-256',
|
|
846
|
-
},
|
|
847
|
-
'peernet-data-response': {
|
|
848
|
-
codec: '70646172',
|
|
849
|
-
hashAlg: 'keccak-256',
|
|
850
|
-
},
|
|
851
|
-
// message
|
|
852
|
-
'peernet-message': {
|
|
853
|
-
codec: '706d65',
|
|
854
|
-
hashAlg: 'keccak-512',
|
|
855
|
-
},
|
|
856
|
-
// pubsub
|
|
857
|
-
'peernet-ps': {
|
|
858
|
-
codec: '707073',
|
|
859
|
-
hashAlg: 'keccak-256',
|
|
860
|
-
},
|
|
861
|
-
'peernet-response': {
|
|
862
|
-
codec: '7072',
|
|
863
|
-
hashAlg: 'keccak-256',
|
|
864
|
-
},
|
|
865
|
-
'peernet-request': {
|
|
866
|
-
codec: '707271',
|
|
867
|
-
hashAlg: 'keccak-256',
|
|
868
|
-
},
|
|
869
|
-
// normal block
|
|
870
|
-
'leofcoin-block': {
|
|
871
|
-
codec: '6c62',
|
|
872
|
-
hashAlg: 'dbl-keccak-512', // ,
|
|
873
|
-
// testnet: 'olivia'
|
|
874
|
-
},
|
|
875
|
-
'leofcoin-tx': {
|
|
876
|
-
codec: '6c74',
|
|
877
|
-
hashAlg: 'dbl-keccak-512', // ,
|
|
878
|
-
// testnet: 'olivia'
|
|
879
|
-
},
|
|
880
|
-
// itx
|
|
881
|
-
'leofcoin-itx': {
|
|
882
|
-
codec: '6c69',
|
|
883
|
-
hashAlg: 'keccak-512', // ,
|
|
884
|
-
// testnet: 'olivia'
|
|
885
|
-
},
|
|
886
|
-
// peer reputation
|
|
887
|
-
'leofcoin-pr': {
|
|
888
|
-
codec: '6c70',
|
|
889
|
-
hashAlg: 'keccak-256', // ,
|
|
890
|
-
// testnet: 'olivia'
|
|
891
|
-
},
|
|
892
|
-
// chat message
|
|
893
|
-
'chat-message': {
|
|
894
|
-
codec: '636d',
|
|
895
|
-
hashAlg: 'dbl-keccak-512',
|
|
896
|
-
},
|
|
629
|
+
var codecs = {
|
|
630
|
+
// just a hash
|
|
631
|
+
'disco-hash': {
|
|
632
|
+
codec: parseInt('30', 16),
|
|
633
|
+
hashAlg: 'dbl-keccak-512', // ,
|
|
634
|
+
// testnet: 'olivia'
|
|
635
|
+
},
|
|
636
|
+
'peernet-peer-response': {
|
|
637
|
+
codec: parseInt('707072', 16),
|
|
638
|
+
hashAlg: 'keccak-256',
|
|
639
|
+
},
|
|
640
|
+
'peernet-peer': {
|
|
641
|
+
codec: parseInt('7070', 16),
|
|
642
|
+
hashAlg: 'keccak-256',
|
|
643
|
+
},
|
|
644
|
+
'peernet-dht': {
|
|
645
|
+
codec: parseInt('706468', 16),
|
|
646
|
+
hashAlg: 'keccak-256',
|
|
647
|
+
},
|
|
648
|
+
'peernet-dht-response': {
|
|
649
|
+
codec: parseInt('706472', 16),
|
|
650
|
+
hashAlg: 'keccak-256',
|
|
651
|
+
},
|
|
652
|
+
// data
|
|
653
|
+
'peernet-data': {
|
|
654
|
+
codec: parseInt('706461', 16),
|
|
655
|
+
hashAlg: 'keccak-256',
|
|
656
|
+
},
|
|
657
|
+
'peernet-data-response': {
|
|
658
|
+
codec: parseInt('70646172', 16),
|
|
659
|
+
hashAlg: 'keccak-256',
|
|
660
|
+
},
|
|
661
|
+
// message
|
|
662
|
+
'peernet-message': {
|
|
663
|
+
codec: parseInt('706d65', 16),
|
|
664
|
+
hashAlg: 'keccak-512',
|
|
665
|
+
},
|
|
666
|
+
// pubsub
|
|
667
|
+
'peernet-ps': {
|
|
668
|
+
codec: parseInt('707073', 16),
|
|
669
|
+
hashAlg: 'keccak-256',
|
|
670
|
+
},
|
|
671
|
+
'peernet-response': {
|
|
672
|
+
codec: parseInt('7072', 16),
|
|
673
|
+
hashAlg: 'keccak-256',
|
|
674
|
+
},
|
|
675
|
+
'peernet-request': {
|
|
676
|
+
codec: parseInt('707271', 16),
|
|
677
|
+
hashAlg: 'keccak-256',
|
|
678
|
+
},
|
|
679
|
+
// normal block
|
|
680
|
+
'leofcoin-block': {
|
|
681
|
+
codec: parseInt('6c62', 16),
|
|
682
|
+
hashAlg: 'dbl-keccak-512', // ,
|
|
683
|
+
// testnet: 'olivia'
|
|
684
|
+
},
|
|
685
|
+
'leofcoin-tx': {
|
|
686
|
+
codec: parseInt('6c74', 16),
|
|
687
|
+
hashAlg: 'dbl-keccak-512', // ,
|
|
688
|
+
// testnet: 'olivia'
|
|
689
|
+
},
|
|
690
|
+
// itx
|
|
691
|
+
'leofcoin-itx': {
|
|
692
|
+
codec: parseInt('6c69', 16),
|
|
693
|
+
hashAlg: 'keccak-512', // ,
|
|
694
|
+
// testnet: 'olivia'
|
|
695
|
+
},
|
|
696
|
+
// peer reputation
|
|
697
|
+
'leofcoin-pr': {
|
|
698
|
+
codec: parseInt('6c70', 16),
|
|
699
|
+
hashAlg: 'keccak-256', // ,
|
|
700
|
+
// testnet: 'olivia'
|
|
701
|
+
},
|
|
702
|
+
// chat message
|
|
703
|
+
'chat-message': {
|
|
704
|
+
codec: parseInt('636d', 16),
|
|
705
|
+
hashAlg: 'dbl-keccak-512',
|
|
706
|
+
},
|
|
897
707
|
};
|
|
898
708
|
|
|
899
|
-
class PeernetCodec {
|
|
900
|
-
get codecs() {
|
|
901
|
-
return {...globalThis.peernet.codecs, ...codecs}
|
|
902
|
-
}
|
|
903
|
-
constructor(buffer) {
|
|
904
|
-
if (buffer) {
|
|
905
|
-
if (
|
|
906
|
-
const codec = varint.decode(buffer);
|
|
907
|
-
const name = this.getCodecName(codec);
|
|
709
|
+
class PeernetCodec {
|
|
710
|
+
get codecs() {
|
|
711
|
+
return {...globalThis.peernet.codecs, ...codecs}
|
|
712
|
+
}
|
|
713
|
+
constructor(buffer) {
|
|
714
|
+
if (buffer) {
|
|
715
|
+
if (buffer instanceof Uint8Array) {
|
|
716
|
+
const codec = varint.decode(buffer);
|
|
717
|
+
const name = this.getCodecName(codec);
|
|
718
|
+
if (name) {
|
|
719
|
+
this.name = name;
|
|
720
|
+
this.encoded = buffer;
|
|
721
|
+
this.decode(buffer);
|
|
722
|
+
} else {
|
|
723
|
+
this.encode(buffer);
|
|
724
|
+
}
|
|
725
|
+
} else if (buffer instanceof ArrayBuffer) {
|
|
726
|
+
const encoded = new Uint8Array(buffer.byteLength);
|
|
727
|
+
|
|
728
|
+
for (let i = 0; i < buffer.byteLength; i++) {
|
|
729
|
+
encoded[i] = buffer[i];
|
|
730
|
+
}
|
|
731
|
+
this.encoded = encoded;
|
|
732
|
+
// this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
|
|
733
|
+
this.decode(buffer);
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
if (typeof buffer === 'string') {
|
|
737
|
+
if (this.codecs[buffer]) this.fromName(buffer);
|
|
738
|
+
else if (isHex(buffer)) this.fromHex(buffer);
|
|
739
|
+
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
740
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
741
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
742
|
+
}
|
|
743
|
+
if (!isNaN(buffer)) if (this.codecs[this.getCodecName(buffer)]) this.fromCodec(buffer);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
fromEncoded(encoded) {
|
|
748
|
+
const codec = varint.decode(encoded);
|
|
749
|
+
const name = this.getCodecName(codec);
|
|
750
|
+
this.name = name;
|
|
751
|
+
this.encoded = encoded;
|
|
752
|
+
this.decode(encoded);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
fromHex(hex) {
|
|
756
|
+
this.encoded = Buffer.from(hex, 'hex');
|
|
757
|
+
this.decode();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
fromBs32(input) {
|
|
761
|
+
this.encoded = bs32.decode(input);
|
|
762
|
+
this.decode();
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
fromBs58(input) {
|
|
766
|
+
this.encoded = bs58.decode(input);
|
|
767
|
+
this.decode();
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
getCodec(name) {
|
|
771
|
+
return this.codecs[name].codec
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
getCodecName(codec) {
|
|
775
|
+
return Object.keys(this.codecs).reduce((p, c) => {
|
|
776
|
+
const item = this.codecs[c];
|
|
777
|
+
if (item.codec === codec) return c;
|
|
778
|
+
else return p;
|
|
779
|
+
}, undefined)
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
getHashAlg(name) {
|
|
783
|
+
return this.codecs[name].hashAlg
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
fromCodec(codec) {
|
|
787
|
+
this.name = this.getCodecName(codec);
|
|
788
|
+
this.hashAlg = this.getHashAlg(this.name);
|
|
789
|
+
|
|
790
|
+
this.codec = this.getCodec(this.name);
|
|
791
|
+
this.codecBuffer = varint.encode(codec);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
fromName(name) {
|
|
795
|
+
const codec = this.getCodec(name);
|
|
796
|
+
this.name = name;
|
|
797
|
+
this.codec = codec;
|
|
798
|
+
this.hashAlg = this.getHashAlg(name);
|
|
799
|
+
this.codecBuffer = varint.encode(codec);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
toBs32() {
|
|
803
|
+
this.encode();
|
|
804
|
+
return bs32.encode(this.encoded)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
toBs58() {
|
|
808
|
+
this.encode();
|
|
809
|
+
return bs58.encode(this.encoded)
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
toHex() {
|
|
813
|
+
return this.encoded.toString('hex')
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
decode() {
|
|
817
|
+
const codec = varint.decode(this.encoded);
|
|
818
|
+
this.fromCodec(codec);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
encode() {
|
|
822
|
+
const codec = varint.encode(this.decoded);
|
|
823
|
+
this.encoded = codec;
|
|
824
|
+
return this.encoded
|
|
825
|
+
}
|
|
826
|
+
}
|
|
908
827
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
828
|
+
class PeernetHash {
|
|
829
|
+
constructor(buffer, options = {}) {
|
|
830
|
+
if (options.name) this.name = options.name;
|
|
831
|
+
else this.name = 'disco-hash';
|
|
832
|
+
if (options.codecs) this.codecs = options.codecs;
|
|
833
|
+
if (buffer) {
|
|
834
|
+
if (Buffer.isBuffer(buffer)) {
|
|
835
|
+
this.discoCodec = new PeernetCodec(buffer, this.codecs);
|
|
836
|
+
const name = this.discoCodec.name;
|
|
837
|
+
|
|
838
|
+
if (name) {
|
|
839
|
+
this.name = name;
|
|
840
|
+
this.decode(buffer);
|
|
841
|
+
} else {
|
|
842
|
+
this.encode(buffer);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (typeof buffer === 'string') {
|
|
847
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
848
|
+
if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
849
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
850
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
851
|
+
} else if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
get prefix() {
|
|
856
|
+
const length = this.length;
|
|
857
|
+
const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length);
|
|
858
|
+
for (let i = 0; i < this.discoCodec.codecBuffer.length; i++) {
|
|
859
|
+
uint8Array[i] = this.discoCodec.codecBuffer[i];
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
for (let i = uint8Array.length - 1; i < length.length; i++) {
|
|
863
|
+
uint8Array[i] = length[i];
|
|
864
|
+
}
|
|
865
|
+
return uint8Array
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
get length() {
|
|
869
|
+
return varint.encode(this.size)
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
get buffer() {
|
|
873
|
+
return this.hash
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
toHex() {
|
|
877
|
+
return this.hash.toString('hex')
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
fromHex(hex) {
|
|
881
|
+
return this.decode(Buffer.from(hex, 'hex'))
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
fromJSON(json) {
|
|
885
|
+
return this.encode(Buffer.from(JSON.stringify(json)))
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
toBs32() {
|
|
889
|
+
return bs32.encode(this.hash)
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
fromBs32(bs) {
|
|
893
|
+
return this.decode(bs32.decode(bs))
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
toBs58() {
|
|
897
|
+
return bs58.encode(this.hash)
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
fromBs58(bs) {
|
|
901
|
+
return this.decode(bs58.decode(bs))
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
toString(encoding = 'utf8') {
|
|
905
|
+
return this.hash.toString(encoding)
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
encode(buffer, name) {
|
|
909
|
+
if (!this.name && name) this.name = name;
|
|
910
|
+
if (!buffer) buffer = this.buffer;
|
|
911
|
+
this.discoCodec = new PeernetCodec(this.name, this.codecs);
|
|
912
|
+
this.discoCodec.fromName(this.name);
|
|
913
|
+
let hashAlg = this.discoCodec.hashAlg;
|
|
914
|
+
if (hashAlg.includes('dbl')) {
|
|
915
|
+
hashAlg = hashAlg.replace('dbl-', '');
|
|
916
|
+
buffer = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
917
|
+
}
|
|
918
|
+
this.digest = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
919
|
+
this.size = this.digest.length;
|
|
920
|
+
|
|
921
|
+
this.codec = this.discoCodec.encode();
|
|
922
|
+
this.codec = this.discoCodec.codecBuffer;
|
|
923
|
+
this.hash = Buffer.concat([
|
|
924
|
+
this.prefix,
|
|
925
|
+
this.digest,
|
|
926
|
+
]);
|
|
927
|
+
|
|
928
|
+
return this.hash
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
validate(buffer) {
|
|
932
|
+
if (Buffer.isBuffer(buffer)) {
|
|
933
|
+
const codec = varint.decode(buffer);
|
|
934
|
+
if (this.codecs[codec]) {
|
|
935
|
+
this.decode(buffer);
|
|
936
|
+
} else {
|
|
937
|
+
this.encode(buffer);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (typeof buffer === 'string') {
|
|
941
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
942
|
+
if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
943
|
+
}
|
|
944
|
+
if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
decode(buffer) {
|
|
948
|
+
this.hash = buffer;
|
|
949
|
+
const codec = varint.decode(buffer);
|
|
950
|
+
|
|
951
|
+
this.discoCodec = new PeernetCodec(codec, this.codecs);
|
|
952
|
+
// TODO: validate codec
|
|
953
|
+
buffer = buffer.slice(varint.decode.bytes);
|
|
954
|
+
this.size = varint.decode(buffer);
|
|
955
|
+
this.digest = buffer.slice(varint.decode.bytes);
|
|
956
|
+
if (this.digest.length !== this.size) {
|
|
957
|
+
throw new Error(`hash length inconsistent: 0x${this.hash.toString('hex')}`)
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// const discoCodec = new Codec(codec, this.codecs)
|
|
961
|
+
|
|
962
|
+
this.name = this.discoCodec.name;
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
this.size = this.digest.length;
|
|
966
|
+
|
|
967
|
+
return {
|
|
968
|
+
codec: this.codec,
|
|
969
|
+
name: this.name,
|
|
970
|
+
size: this.size,
|
|
971
|
+
length: this.length,
|
|
972
|
+
digest: this.digest,
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
926
976
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
}
|
|
977
|
+
class FormatInterface {
|
|
978
|
+
/**
|
|
979
|
+
* @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
|
|
980
|
+
* @param {Object} proto - {encode, decode}
|
|
981
|
+
* @param {Object} options - {hashFormat, name}
|
|
982
|
+
*/
|
|
983
|
+
constructor(buffer, proto, options = {}) {
|
|
984
|
+
this.protoEncode = proto.encode;
|
|
985
|
+
this.protoDecode = proto.decode;
|
|
986
|
+
this.hashFormat = options.hashFormat || 'bs32';
|
|
987
|
+
if (options.name) this.name = options.name;
|
|
988
|
+
if (buffer instanceof Uint8Array) return this.fromUint8Array(buffer)
|
|
989
|
+
else if (buffer instanceof ArrayBuffer) return this.fromArrayBuffer(buffer)
|
|
990
|
+
else if (buffer.name === options.name) return buffer
|
|
991
|
+
else if (typeof buffer === 'string') {
|
|
992
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
993
|
+
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
994
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
995
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
996
|
+
} else {
|
|
997
|
+
this.create(buffer);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* @return {PeernetHash}
|
|
1003
|
+
*/
|
|
1004
|
+
get peernetHash() {
|
|
1005
|
+
return new PeernetHash(this.decoded, {name: this.name})
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* @return {peernetHash}
|
|
1010
|
+
*/
|
|
1011
|
+
get hash() {
|
|
1012
|
+
const upper = this.hashFormat.charAt(0).toUpperCase();
|
|
1013
|
+
const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`;
|
|
1014
|
+
return this.peernetHash[`to${format}`]()
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* @return {Object}
|
|
1019
|
+
*/
|
|
1020
|
+
decode() {
|
|
1021
|
+
let encoded = this.encoded;
|
|
1022
|
+
const discoCodec = new PeernetCodec(this.encoded);
|
|
1023
|
+
encoded = encoded.slice(discoCodec.codecBuffer.length);
|
|
1024
|
+
this.name = discoCodec.name;
|
|
1025
|
+
this.decoded = this.protoDecode(encoded);
|
|
1026
|
+
return this.decoded
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* @return {Buffer}
|
|
1031
|
+
*/
|
|
1032
|
+
encode(decoded) {
|
|
1033
|
+
if (!decoded) decoded = this.decoded;
|
|
1034
|
+
const codec = new PeernetCodec(this.name);
|
|
1035
|
+
const encoded = this.protoEncode(decoded);
|
|
1036
|
+
const uint8Array = new Uint8Array(encoded.length + codec.codecBuffer.length);
|
|
1037
|
+
uint8Array.set(codec.codecBuffer);
|
|
1038
|
+
uint8Array.set(encoded, codec.codecBuffer.length);
|
|
1039
|
+
this.encoded = uint8Array;
|
|
1040
|
+
return this.encoded
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
hasCodec() {
|
|
1044
|
+
if (!this.encoded) return false
|
|
1045
|
+
const codec = new PeernetCodec(this.encoded);
|
|
1046
|
+
if (codec.name) return true
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
fromUint8Array(buffer) {
|
|
1050
|
+
this.encoded = buffer;
|
|
1051
|
+
if (!this.hasCodec()) this.create(
|
|
1052
|
+
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1053
|
+
);
|
|
1054
|
+
else this.decode();
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
fromArrayBuffer(buffer) {
|
|
1058
|
+
this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength);
|
|
1059
|
+
if (!this.hasCodec()) this.create(
|
|
1060
|
+
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1061
|
+
);
|
|
1062
|
+
else this.decode();
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* @param {Buffer} encoded
|
|
1067
|
+
*/
|
|
1068
|
+
fromEncoded(encoded) {
|
|
1069
|
+
this.encoded = encoded;
|
|
1070
|
+
this.decode();
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* @param {String} encoded
|
|
1075
|
+
*/
|
|
1076
|
+
fromHex(encoded) {
|
|
1077
|
+
this.encoded = Buffer.from(encoded, 'hex');
|
|
1078
|
+
this.decode();
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* @param {String} encoded
|
|
1083
|
+
*/
|
|
1084
|
+
fromBs32(encoded) {
|
|
1085
|
+
this.encoded = bs32.decode(encoded);
|
|
1086
|
+
this.decode();
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* @param {String} encoded
|
|
1091
|
+
*/
|
|
1092
|
+
fromBs58(encoded) {
|
|
1093
|
+
this.encoded = bs58.decode(encoded);
|
|
1094
|
+
this.decode();
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* @return {String} encoded
|
|
1099
|
+
*/
|
|
1100
|
+
toHex() {
|
|
1101
|
+
if (!this.encoded) this.encode();
|
|
1102
|
+
return this.encoded.toString('hex')
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* @return {String} encoded
|
|
1107
|
+
*/
|
|
1108
|
+
toBs32() {
|
|
1109
|
+
if (!this.encoded) this.encode();
|
|
1110
|
+
return bs32.encode(this.encoded)
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* @return {String} encoded
|
|
1115
|
+
*/
|
|
1116
|
+
toBs58() {
|
|
1117
|
+
if (!this.encoded) this.encode();
|
|
1118
|
+
return bs58.encode(this.encoded)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* @param {Object} data
|
|
1123
|
+
*/
|
|
1124
|
+
create(data) {
|
|
1125
|
+
const decoded = {};
|
|
1126
|
+
if (this.keys?.length > 0) {
|
|
1127
|
+
for (const key of this.keys) {
|
|
1128
|
+
Object.defineProperties(decoded, {
|
|
1129
|
+
[key]: {
|
|
1130
|
+
enumerable: true,
|
|
1131
|
+
configurable: true,
|
|
1132
|
+
set: (val) => value = data[key],
|
|
1133
|
+
get: () => data[key]
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
this.decoded = decoded;
|
|
1139
|
+
this.encode();
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
934
1143
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
1144
|
+
class PeernetMessage extends FormatInterface {
|
|
1145
|
+
get keys() {
|
|
1146
|
+
return ['data', 'signature', 'from', 'to', 'id']
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
constructor(buffer) {
|
|
1150
|
+
const name = 'peernet-message';
|
|
1151
|
+
super(buffer, protons(proto$a).PeernetMessage, {name});
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
939
1154
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1155
|
+
var proto$9 = `
|
|
1156
|
+
// PeernetDHTMessage
|
|
1157
|
+
message PeernetDHTMessage {
|
|
1158
|
+
required string hash = 1;
|
|
1159
|
+
optional string store = 2;
|
|
1160
|
+
}
|
|
1161
|
+
`;
|
|
944
1162
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1163
|
+
/**
|
|
1164
|
+
* @example `
|
|
1165
|
+
new DHTMessage(hash, store)
|
|
1166
|
+
// store = optional if not set, peernet checks every store
|
|
1167
|
+
let message = new DHTMessage('hashmvbs124xcfd...', 'transaction')
|
|
1168
|
+
message = new DHTMessage('hashmvbs124xcfd...', 'block')
|
|
1169
|
+
`
|
|
1170
|
+
*/
|
|
1171
|
+
class DHTMessage extends FormatInterface {
|
|
1172
|
+
/**
|
|
1173
|
+
*
|
|
1174
|
+
*/
|
|
1175
|
+
get keys() {
|
|
1176
|
+
return ['hash', 'store']
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
constructor(data) {
|
|
1180
|
+
const name = 'peernet-dht';
|
|
1181
|
+
super(data, protons(proto$9).PeernetDHTMessage, {name});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
949
1184
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1185
|
+
var proto$8 = `
|
|
1186
|
+
// PeernetDHTMessageResponse
|
|
1187
|
+
message PeernetDHTMessageResponse {
|
|
1188
|
+
required string hash = 1;
|
|
1189
|
+
required bool has = 2;
|
|
1190
|
+
}
|
|
1191
|
+
`;
|
|
953
1192
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1193
|
+
class DHTMessageResponse extends FormatInterface {
|
|
1194
|
+
get keys() {
|
|
1195
|
+
return ['hash', 'has']
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
constructor(data) {
|
|
1199
|
+
const name = 'peernet-dht-response';
|
|
1200
|
+
super(data, protons(proto$8).PeernetDHTMessageResponse, {name});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
960
1203
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1204
|
+
var proto$7 = `
|
|
1205
|
+
// PeernetDataMessage
|
|
1206
|
+
message PeernetDataMessage {
|
|
1207
|
+
required string hash = 1;
|
|
1208
|
+
optional string store = 2;
|
|
1209
|
+
}
|
|
1210
|
+
`;
|
|
964
1211
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1212
|
+
/**
|
|
1213
|
+
* @extends {CodecFormat}
|
|
1214
|
+
*/
|
|
1215
|
+
class DataMessage extends FormatInterface {
|
|
1216
|
+
get keys() {
|
|
1217
|
+
return ['hash', 'store']
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* @param {Buffer|String|Object|DataMessage} data - The data needed to create the DataMessage
|
|
1221
|
+
*/
|
|
1222
|
+
constructor(data) {
|
|
1223
|
+
super(data, protons(proto$7).PeernetDataMessage, {name: 'peernet-data'});
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
968
1226
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1227
|
+
var proto$6 = `
|
|
1228
|
+
// PsMessage
|
|
1229
|
+
message PsMessage {
|
|
1230
|
+
required bytes data = 1;
|
|
1231
|
+
required bytes topic = 2;
|
|
1232
|
+
}`;
|
|
972
1233
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1234
|
+
class PsMessage extends FormatInterface {
|
|
1235
|
+
get keys() {
|
|
1236
|
+
return ['data', 'topic']
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
constructor(buffer) {
|
|
1240
|
+
const name = 'peernet-ps';
|
|
1241
|
+
super(buffer, protons(proto$6).PsMessage, {name});
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
980
1244
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1245
|
+
var proto$5 = `
|
|
1246
|
+
// PeernetPeerMessage
|
|
1247
|
+
message PeernetPeerMessage {
|
|
1248
|
+
required string id = 1;
|
|
1249
|
+
}
|
|
1250
|
+
`;
|
|
985
1251
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
return
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
decode() {
|
|
996
|
-
const codec = varint.decode(this.encoded);
|
|
997
|
-
this.fromCodec(codec);
|
|
998
|
-
this.name = this.getCodecName(codec);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
encode() {
|
|
1002
|
-
const codec = Buffer.from(varint.encode(parseInt(Buffer.from(`${this.codec}`, 'hex').toString('hex'), 16)), 'hex');
|
|
1003
|
-
this.encoded = codec;
|
|
1004
|
-
return this.encoded
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
class PeernetHash {
|
|
1009
|
-
constructor(buffer, options = {}) {
|
|
1010
|
-
if (options.name) this.name = options.name;
|
|
1011
|
-
else this.name = 'disco-hash';
|
|
1012
|
-
if (options.codecs) this.codecs = options.codecs;
|
|
1013
|
-
if (buffer) {
|
|
1014
|
-
if (Buffer.isBuffer(buffer)) {
|
|
1015
|
-
this.discoCodec = new PeernetCodec(buffer, this.codecs);
|
|
1016
|
-
const name = this.discoCodec.name;
|
|
1017
|
-
|
|
1018
|
-
if (name) {
|
|
1019
|
-
this.name = name;
|
|
1020
|
-
this.decode(buffer);
|
|
1021
|
-
} else {
|
|
1022
|
-
this.encode(buffer);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
if (typeof buffer === 'string') {
|
|
1027
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
1028
|
-
if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
1029
|
-
else this.fromBs58(buffer);
|
|
1030
|
-
} else if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
get prefix() {
|
|
1035
|
-
return Buffer.concat([this.discoCodec.codecBuffer, this.length])
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
get length() {
|
|
1039
|
-
return Buffer.from(varint.encode(this.size))
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
get buffer() {
|
|
1043
|
-
return this.hash
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
toHex() {
|
|
1047
|
-
return this.hash.toString('hex')
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
fromHex(hex) {
|
|
1051
|
-
return this.decode(Buffer.from(hex, 'hex'))
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
fromJSON(json) {
|
|
1055
|
-
return this.encode(Buffer.from(JSON.stringify(json)))
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
toBs32() {
|
|
1059
|
-
return bs32.encode(this.hash)
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
fromBs32(bs) {
|
|
1063
|
-
return this.decode(bs32.decode(bs))
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
toBs58() {
|
|
1067
|
-
return bs58.encode(this.hash)
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
fromBs58(bs) {
|
|
1071
|
-
return this.decode(bs58.decode(bs))
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
toString(encoding = 'utf8') {
|
|
1075
|
-
return this.hash.toString(encoding)
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
encode(buffer, name) {
|
|
1079
|
-
if (!this.name && name) this.name = name;
|
|
1080
|
-
if (!buffer) buffer = this.buffer;
|
|
1081
|
-
this.discoCodec = new PeernetCodec(this.name, this.codecs);
|
|
1082
|
-
this.discoCodec.fromName(this.name);
|
|
1083
|
-
let hashAlg = this.discoCodec.hashAlg;
|
|
1084
|
-
if (hashAlg.includes('dbl')) {
|
|
1085
|
-
hashAlg = hashAlg.replace('dbl-', '');
|
|
1086
|
-
buffer = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
1087
|
-
}
|
|
1088
|
-
this.digest = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
1089
|
-
this.size = this.digest.length;
|
|
1090
|
-
|
|
1091
|
-
this.codec = this.discoCodec.encode();
|
|
1092
|
-
this.codec = this.discoCodec.codecBuffer;
|
|
1093
|
-
this.hash = Buffer.concat([
|
|
1094
|
-
this.prefix,
|
|
1095
|
-
this.digest,
|
|
1096
|
-
]);
|
|
1097
|
-
|
|
1098
|
-
return this.hash
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
validate(buffer) {
|
|
1102
|
-
if (Buffer.isBuffer(buffer)) {
|
|
1103
|
-
const codec = varint.decode(buffer);
|
|
1104
|
-
if (this.codecs[codec]) {
|
|
1105
|
-
this.decode(buffer);
|
|
1106
|
-
} else {
|
|
1107
|
-
this.encode(buffer);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
if (typeof buffer === 'string') {
|
|
1111
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
1112
|
-
if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
1113
|
-
}
|
|
1114
|
-
if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
decode(buffer) {
|
|
1118
|
-
this.hash = buffer;
|
|
1119
|
-
const codec = varint.decode(buffer);
|
|
1120
|
-
|
|
1121
|
-
this.discoCodec = new PeernetCodec(codec, this.codecs);
|
|
1122
|
-
// TODO: validate codec
|
|
1123
|
-
buffer = buffer.slice(varint.decode.bytes);
|
|
1124
|
-
this.size = varint.decode(buffer);
|
|
1125
|
-
this.digest = buffer.slice(varint.decode.bytes);
|
|
1126
|
-
if (this.digest.length !== this.size) {
|
|
1127
|
-
throw new Error(`hash length inconsistent: 0x${this.hash.toString('hex')}`)
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// const discoCodec = new Codec(codec, this.codecs)
|
|
1131
|
-
|
|
1132
|
-
this.name = this.discoCodec.name;
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
this.size = this.digest.length;
|
|
1136
|
-
|
|
1137
|
-
return {
|
|
1138
|
-
codec: this.codec,
|
|
1139
|
-
name: this.name,
|
|
1140
|
-
size: this.size,
|
|
1141
|
-
length: this.length,
|
|
1142
|
-
digest: this.digest,
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
class FormatInterface {
|
|
1148
|
-
/**
|
|
1149
|
-
* @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
|
|
1150
|
-
* @param {Object} proto - {encode, decode}
|
|
1151
|
-
* @param {Object} options - {hashFormat, name}
|
|
1152
|
-
*/
|
|
1153
|
-
constructor(buffer, proto, options = {}) {
|
|
1154
|
-
this.protoEncode = proto.encode;
|
|
1155
|
-
this.protoDecode = proto.decode;
|
|
1156
|
-
if (options.name) this.name = options.name;
|
|
1157
|
-
this.hashFormat = options.hashFormat || 'bs32';
|
|
1158
|
-
if (buffer.name === options.name) {
|
|
1159
|
-
return buffer
|
|
1160
|
-
} else if (Buffer.isBuffer(buffer)) {
|
|
1161
|
-
const codec = new PeernetCodec(buffer);
|
|
1162
|
-
if (codec.name) {
|
|
1163
|
-
this.fromEncoded(buffer);
|
|
1164
|
-
} else {
|
|
1165
|
-
this.create(buffer);
|
|
1166
|
-
}
|
|
1167
|
-
} else {
|
|
1168
|
-
if (typeof buffer === 'string') {
|
|
1169
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
1170
|
-
else if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
1171
|
-
else this.fromBs58(buffer);
|
|
1172
|
-
} else if (typeof buffer === 'object' && !Array.isArray(buffer)) {
|
|
1173
|
-
this.create(buffer);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* @return {PeernetHash}
|
|
1180
|
-
*/
|
|
1181
|
-
get peernetHash() {
|
|
1182
|
-
return new PeernetHash(this.decoded, {name: this.name})
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
/**
|
|
1186
|
-
* @return {peernetHash}
|
|
1187
|
-
*/
|
|
1188
|
-
get hash() {
|
|
1189
|
-
const upper = this.hashFormat.charAt(0).toUpperCase();
|
|
1190
|
-
const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`;
|
|
1191
|
-
return this.peernetHash[`to${format}`]()
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
/**
|
|
1195
|
-
* @return {Object}
|
|
1196
|
-
*/
|
|
1197
|
-
decode() {
|
|
1198
|
-
let encoded = this.encoded;
|
|
1199
|
-
const discoCodec = new PeernetCodec(this.encoded.toString('hex'));
|
|
1200
|
-
encoded = encoded.slice(discoCodec.codecBuffer.length);
|
|
1201
|
-
this.name = discoCodec.name;
|
|
1202
|
-
this.decoded = this.protoDecode(encoded);
|
|
1203
|
-
return this.decoded
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
/**
|
|
1207
|
-
* @return {Buffer}
|
|
1208
|
-
*/
|
|
1209
|
-
encode(decoded) {
|
|
1210
|
-
if (!decoded) decoded = this.decoded;
|
|
1211
|
-
const codec = new PeernetCodec(this.name);
|
|
1212
|
-
this.encoded = Buffer.concat([codec.codecBuffer, this.protoEncode(decoded)]);
|
|
1213
|
-
return this.encoded
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
/**
|
|
1217
|
-
* @param {Buffer} encoded
|
|
1218
|
-
*/
|
|
1219
|
-
fromEncoded(encoded) {
|
|
1220
|
-
const codec = new PeernetCodec(encoded);
|
|
1221
|
-
this.name = codec.name;
|
|
1222
|
-
this.encoded = encoded;
|
|
1223
|
-
this.decode();
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
/**
|
|
1227
|
-
* @param {String} encoded
|
|
1228
|
-
*/
|
|
1229
|
-
fromHex(encoded) {
|
|
1230
|
-
this.encoded = Buffer.from(encoded, 'hex');
|
|
1231
|
-
this.decode();
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
/**
|
|
1235
|
-
* @param {String} encoded
|
|
1236
|
-
*/
|
|
1237
|
-
fromBs32(encoded) {
|
|
1238
|
-
this.encoded = bs32.decode(encoded);
|
|
1239
|
-
this.decode();
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
/**
|
|
1243
|
-
* @param {String} encoded
|
|
1244
|
-
*/
|
|
1245
|
-
fromBs58(encoded) {
|
|
1246
|
-
this.encoded = bs58.decode(encoded);
|
|
1247
|
-
this.decode();
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
/**
|
|
1251
|
-
* @return {String} encoded
|
|
1252
|
-
*/
|
|
1253
|
-
toHex() {
|
|
1254
|
-
if (!this.encoded) this.encode();
|
|
1255
|
-
return this.encoded.toString('hex')
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
/**
|
|
1259
|
-
* @return {String} encoded
|
|
1260
|
-
*/
|
|
1261
|
-
toBs32() {
|
|
1262
|
-
if (!this.encoded) this.encode();
|
|
1263
|
-
return bs32.encode(this.encoded)
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
/**
|
|
1267
|
-
* @return {String} encoded
|
|
1268
|
-
*/
|
|
1269
|
-
toBs58() {
|
|
1270
|
-
if (!this.encoded) this.encode();
|
|
1271
|
-
return bs58.encode(this.encoded)
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
/**
|
|
1275
|
-
* @param {Object} data
|
|
1276
|
-
*/
|
|
1277
|
-
create(data) {
|
|
1278
|
-
const decoded = {};
|
|
1279
|
-
if (this.keys?.length > 0) {
|
|
1280
|
-
for (const key of this.keys) {
|
|
1281
|
-
Object.defineProperties(decoded, {
|
|
1282
|
-
[key]: {
|
|
1283
|
-
enumerable: true,
|
|
1284
|
-
configurable: true,
|
|
1285
|
-
set: (val) => value = data[key],
|
|
1286
|
-
get: () => data[key]
|
|
1287
|
-
}
|
|
1288
|
-
});
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
this.decoded = decoded;
|
|
1292
|
-
this.encode();
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
class PeernetMessage extends FormatInterface {
|
|
1298
|
-
get keys() {
|
|
1299
|
-
return ['data', 'signature', 'from', 'to', 'id']
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
constructor(buffer) {
|
|
1303
|
-
const name = 'peernet-message';
|
|
1304
|
-
super(buffer, protons(proto$a).PeernetMessage, {name});
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
var proto$9 = `
|
|
1309
|
-
// PeernetDHTMessage
|
|
1310
|
-
message PeernetDHTMessage {
|
|
1311
|
-
required string hash = 1;
|
|
1312
|
-
optional string store = 2;
|
|
1313
|
-
}
|
|
1314
|
-
`;
|
|
1315
|
-
|
|
1316
|
-
/**
|
|
1317
|
-
* @example `
|
|
1318
|
-
new DHTMessage(hash, store)
|
|
1319
|
-
// store = optional if not set, peernet checks every store
|
|
1320
|
-
let message = new DHTMessage('hashmvbs124xcfd...', 'transaction')
|
|
1321
|
-
message = new DHTMessage('hashmvbs124xcfd...', 'block')
|
|
1322
|
-
`
|
|
1323
|
-
*/
|
|
1324
|
-
class DHTMessage extends FormatInterface {
|
|
1325
|
-
/**
|
|
1326
|
-
*
|
|
1327
|
-
*/
|
|
1328
|
-
get keys() {
|
|
1329
|
-
return ['hash', 'store']
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
constructor(data) {
|
|
1333
|
-
const name = 'peernet-dht';
|
|
1334
|
-
super(data, protons(proto$9).PeernetDHTMessage, {name});
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
var proto$8 = `
|
|
1339
|
-
// PeernetDHTMessageResponse
|
|
1340
|
-
message PeernetDHTMessageResponse {
|
|
1341
|
-
required string hash = 1;
|
|
1342
|
-
required bool has = 2;
|
|
1343
|
-
}
|
|
1344
|
-
`;
|
|
1345
|
-
|
|
1346
|
-
class DHTMessageResponse extends FormatInterface {
|
|
1347
|
-
get keys() {
|
|
1348
|
-
return ['hash', 'has']
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
constructor(data) {
|
|
1352
|
-
const name = 'peernet-dht-response';
|
|
1353
|
-
super(data, protons(proto$8).PeernetDHTMessageResponse, {name});
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
var proto$7 = `
|
|
1358
|
-
// PeernetDataMessage
|
|
1359
|
-
message PeernetDataMessage {
|
|
1360
|
-
required string hash = 1;
|
|
1361
|
-
optional string store = 2;
|
|
1362
|
-
}
|
|
1363
|
-
`;
|
|
1364
|
-
|
|
1365
|
-
/**
|
|
1366
|
-
* @extends {CodecFormat}
|
|
1367
|
-
*/
|
|
1368
|
-
class DataMessage extends FormatInterface {
|
|
1369
|
-
get keys() {
|
|
1370
|
-
return ['hash', 'store']
|
|
1371
|
-
}
|
|
1372
|
-
/**
|
|
1373
|
-
* @param {Buffer|String|Object|DataMessage} data - The data needed to create the DataMessage
|
|
1374
|
-
*/
|
|
1375
|
-
constructor(data) {
|
|
1376
|
-
super(data, protons(proto$7).PeernetDataMessage, {name: 'peernet-data'});
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
var proto$6 = `
|
|
1381
|
-
// PsMessage
|
|
1382
|
-
message PsMessage {
|
|
1383
|
-
required bytes data = 1;
|
|
1384
|
-
required bytes topic = 2;
|
|
1385
|
-
}`;
|
|
1386
|
-
|
|
1387
|
-
class PsMessage extends FormatInterface {
|
|
1388
|
-
get keys() {
|
|
1389
|
-
return ['data', 'topic']
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
constructor(buffer) {
|
|
1393
|
-
const name = 'peernet-ps';
|
|
1394
|
-
super(buffer, protons(proto$6).PsMessage, {name});
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
var proto$5 = `
|
|
1399
|
-
// PeernetPeerMessage
|
|
1400
|
-
message PeernetPeerMessage {
|
|
1401
|
-
required string id = 1;
|
|
1402
|
-
}
|
|
1403
|
-
`;
|
|
1404
|
-
|
|
1405
|
-
class PeerMessage extends FormatInterface {
|
|
1406
|
-
get keys() {
|
|
1407
|
-
return ['id']
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
constructor(data) {
|
|
1411
|
-
const name = 'peernet-peer';
|
|
1412
|
-
super(data, protons(proto$5).PeernetPeerMessage, {name});
|
|
1413
|
-
}
|
|
1252
|
+
class PeerMessage extends FormatInterface {
|
|
1253
|
+
get keys() {
|
|
1254
|
+
return ['id']
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
constructor(data) {
|
|
1258
|
+
const name = 'peernet-peer';
|
|
1259
|
+
super(data, protons(proto$5).PeernetPeerMessage, {name});
|
|
1260
|
+
}
|
|
1414
1261
|
}
|
|
1415
1262
|
|
|
1416
1263
|
var proto$4 = `
|
|
@@ -1420,15 +1267,15 @@ message PeernetRequestMessage {
|
|
|
1420
1267
|
}
|
|
1421
1268
|
`;
|
|
1422
1269
|
|
|
1423
|
-
class RequestMessage extends FormatInterface {
|
|
1424
|
-
get keys() {
|
|
1425
|
-
return ['request']
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
constructor(data) {
|
|
1429
|
-
const name = 'peernet-request';
|
|
1430
|
-
super(data, protons(proto$4).PeernetRequestMessage, {name});
|
|
1431
|
-
}
|
|
1270
|
+
class RequestMessage extends FormatInterface {
|
|
1271
|
+
get keys() {
|
|
1272
|
+
return ['request']
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
constructor(data) {
|
|
1276
|
+
const name = 'peernet-request';
|
|
1277
|
+
super(data, protons(proto$4).PeernetRequestMessage, {name});
|
|
1278
|
+
}
|
|
1432
1279
|
}
|
|
1433
1280
|
|
|
1434
1281
|
var proto$3 = `
|
|
@@ -1438,15 +1285,15 @@ message PeernetResponseMessage {
|
|
|
1438
1285
|
}
|
|
1439
1286
|
`;
|
|
1440
1287
|
|
|
1441
|
-
class ResponseMessage extends FormatInterface {
|
|
1442
|
-
get keys() {
|
|
1443
|
-
return ['response']
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
constructor(data) {
|
|
1447
|
-
const name = 'peernet-response';
|
|
1448
|
-
super(data, protons(proto$3).PeernetResponseMessage, {name});
|
|
1449
|
-
}
|
|
1288
|
+
class ResponseMessage extends FormatInterface {
|
|
1289
|
+
get keys() {
|
|
1290
|
+
return ['response']
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
constructor(data) {
|
|
1294
|
+
const name = 'peernet-response';
|
|
1295
|
+
super(data, protons(proto$3).PeernetResponseMessage, {name});
|
|
1296
|
+
}
|
|
1450
1297
|
}
|
|
1451
1298
|
|
|
1452
1299
|
var proto$2 = `
|
|
@@ -1456,15 +1303,15 @@ message PeernetPeerMessageResponse {
|
|
|
1456
1303
|
}
|
|
1457
1304
|
`;
|
|
1458
1305
|
|
|
1459
|
-
class PeerMessageResponse extends FormatInterface {
|
|
1460
|
-
get keys() {
|
|
1461
|
-
return ['id']
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
constructor(data) {
|
|
1465
|
-
const name = 'peernet-peer-response';
|
|
1466
|
-
super(data, protons(proto$2).PeernetPeerMessageResponse, {name});
|
|
1467
|
-
}
|
|
1306
|
+
class PeerMessageResponse extends FormatInterface {
|
|
1307
|
+
get keys() {
|
|
1308
|
+
return ['id']
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
constructor(data) {
|
|
1312
|
+
const name = 'peernet-peer-response';
|
|
1313
|
+
super(data, protons(proto$2).PeernetPeerMessageResponse, {name});
|
|
1314
|
+
}
|
|
1468
1315
|
}
|
|
1469
1316
|
|
|
1470
1317
|
var proto$1 = `
|
|
@@ -1475,378 +1322,724 @@ message PeernetDataMessageResponse {
|
|
|
1475
1322
|
}
|
|
1476
1323
|
`;
|
|
1477
1324
|
|
|
1478
|
-
class DataMessageResponse extends FormatInterface {
|
|
1479
|
-
get keys() {
|
|
1480
|
-
return ['hash', 'data']
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
constructor(data) {
|
|
1484
|
-
const name = 'peernet-data-response';
|
|
1485
|
-
super(data, protons(proto$1).PeernetDataMessageResponse, {name});
|
|
1486
|
-
}
|
|
1325
|
+
class DataMessageResponse extends FormatInterface {
|
|
1326
|
+
get keys() {
|
|
1327
|
+
return ['hash', 'data']
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
constructor(data) {
|
|
1331
|
+
const name = 'peernet-data-response';
|
|
1332
|
+
super(data, protons(proto$1).PeernetDataMessageResponse, {name});
|
|
1333
|
+
}
|
|
1487
1334
|
}
|
|
1488
1335
|
|
|
1489
|
-
var proto = `
|
|
1490
|
-
message ChatMessage {
|
|
1491
|
-
required string value = 1;
|
|
1492
|
-
required string author = 2;
|
|
1493
|
-
required uint64 timestamp = 3;
|
|
1494
|
-
repeated string files = 4;
|
|
1336
|
+
var proto = `
|
|
1337
|
+
message ChatMessage {
|
|
1338
|
+
required string value = 1;
|
|
1339
|
+
required string author = 2;
|
|
1340
|
+
required uint64 timestamp = 3;
|
|
1341
|
+
repeated string files = 4;
|
|
1495
1342
|
}`;
|
|
1496
1343
|
|
|
1497
|
-
class ChatMessage extends FormatInterface {
|
|
1498
|
-
get keys() {
|
|
1499
|
-
return ['author', 'value', 'timestamp', 'files']
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
constructor(buffer) {
|
|
1503
|
-
const name = 'chat-message';
|
|
1504
|
-
super(buffer, protons(proto).ChatMessage, {name});
|
|
1505
|
-
}
|
|
1344
|
+
class ChatMessage extends FormatInterface {
|
|
1345
|
+
get keys() {
|
|
1346
|
+
return ['author', 'value', 'timestamp', 'files']
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
constructor(buffer) {
|
|
1350
|
+
const name = 'chat-message';
|
|
1351
|
+
super(buffer, protons(proto).ChatMessage, {name});
|
|
1352
|
+
}
|
|
1506
1353
|
}
|
|
1507
1354
|
|
|
1508
|
-
const debug = (log) => {
|
|
1509
|
-
if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
|
|
1510
|
-
};
|
|
1511
|
-
|
|
1512
|
-
const protoFor = (data) => {
|
|
1513
|
-
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
1514
|
-
const codec = new PeernetCodec(data);
|
|
1515
|
-
if (!codec.name) throw new Error('proto not found')
|
|
1516
|
-
const Proto = globalThis.peernet.protos[codec.name];
|
|
1517
|
-
if (!Proto) throw (new Error(`No proto defined for ${codec.name}`))
|
|
1518
|
-
return new Proto(data)
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
/**
|
|
1522
|
-
* wether or not a peernet daemon is active
|
|
1523
|
-
* @return {Boolean}
|
|
1524
|
-
*/
|
|
1525
|
-
const hasDaemon = async () => {
|
|
1526
|
-
try {
|
|
1527
|
-
let response = await fetch
|
|
1528
|
-
response = await response.json();
|
|
1529
|
-
return Boolean(response.client === '@peernet/api/http')
|
|
1530
|
-
} catch (e) {
|
|
1531
|
-
return false
|
|
1532
|
-
}
|
|
1533
|
-
};
|
|
1534
|
-
|
|
1535
|
-
const https = () => {
|
|
1536
|
-
if (!globalThis.location) return false;
|
|
1537
|
-
return Boolean(globalThis.location.protocol === 'https:')
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
|
-
/**
|
|
1541
|
-
* Get current environment
|
|
1542
|
-
* @return {String} current environment [node, electron, browser]
|
|
1543
|
-
*/
|
|
1544
|
-
const environment = () => {
|
|
1545
|
-
const _navigator = globalThis.navigator;
|
|
1546
|
-
if (!_navigator) {
|
|
1547
|
-
return 'node'
|
|
1548
|
-
} else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
1549
|
-
return 'electron'
|
|
1550
|
-
} else {
|
|
1551
|
-
return 'browser'
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
|
|
1555
|
-
/**
|
|
1556
|
-
* * Get current environment
|
|
1557
|
-
* @return {Object} result
|
|
1558
|
-
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
1559
|
-
* @property {Boolean} reult.environment Current environment
|
|
1560
|
-
*/
|
|
1561
|
-
const target = async () => {
|
|
1562
|
-
let daemon = false;
|
|
1563
|
-
const env = await environment();
|
|
1564
|
-
if (!https()) daemon = await hasDaemon();
|
|
1565
|
-
|
|
1566
|
-
return {daemon, environment: env}
|
|
1355
|
+
const debug = (log) => {
|
|
1356
|
+
if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const protoFor = (data) => {
|
|
1360
|
+
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
1361
|
+
const codec = new PeernetCodec(data);
|
|
1362
|
+
if (!codec.name) throw new Error('proto not found')
|
|
1363
|
+
const Proto = globalThis.peernet.protos[codec.name];
|
|
1364
|
+
if (!Proto) throw (new Error(`No proto defined for ${codec.name}`))
|
|
1365
|
+
return new Proto(data)
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* wether or not a peernet daemon is active
|
|
1370
|
+
* @return {Boolean}
|
|
1371
|
+
*/
|
|
1372
|
+
const hasDaemon = async () => {
|
|
1373
|
+
try {
|
|
1374
|
+
let response = await fetch('http://127.0.0.1:1000/api/version');
|
|
1375
|
+
response = await response.json();
|
|
1376
|
+
return Boolean(response.client === '@peernet/api/http')
|
|
1377
|
+
} catch (e) {
|
|
1378
|
+
return false
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1382
|
+
const https = () => {
|
|
1383
|
+
if (!globalThis.location) return false;
|
|
1384
|
+
return Boolean(globalThis.location.protocol === 'https:')
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Get current environment
|
|
1389
|
+
* @return {String} current environment [node, electron, browser]
|
|
1390
|
+
*/
|
|
1391
|
+
const environment = () => {
|
|
1392
|
+
const _navigator = globalThis.navigator;
|
|
1393
|
+
if (!_navigator) {
|
|
1394
|
+
return 'node'
|
|
1395
|
+
} else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
1396
|
+
return 'electron'
|
|
1397
|
+
} else {
|
|
1398
|
+
return 'browser'
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* * Get current environment
|
|
1404
|
+
* @return {Object} result
|
|
1405
|
+
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
1406
|
+
* @property {Boolean} reult.environment Current environment
|
|
1407
|
+
*/
|
|
1408
|
+
const target = async () => {
|
|
1409
|
+
let daemon = false;
|
|
1410
|
+
const env = await environment();
|
|
1411
|
+
if (!https()) daemon = await hasDaemon();
|
|
1412
|
+
|
|
1413
|
+
return {daemon, environment: env}
|
|
1567
1414
|
};
|
|
1568
1415
|
|
|
1569
|
-
class PeerDiscovery {
|
|
1570
|
-
constructor(id) {
|
|
1571
|
-
this.id = id;
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
_getPeerId(id) {
|
|
1575
|
-
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
|
|
1576
|
-
|
|
1577
|
-
for (const entry of [...peernet.peerMap.entries()]) {
|
|
1578
|
-
for (const _id of entry[1]) {
|
|
1579
|
-
if (_id === id) return entry[0]
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1416
|
+
class PeerDiscovery {
|
|
1417
|
+
constructor(id) {
|
|
1418
|
+
this.id = id;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
_getPeerId(id) {
|
|
1422
|
+
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
|
|
1423
|
+
|
|
1424
|
+
for (const entry of [...peernet.peerMap.entries()]) {
|
|
1425
|
+
for (const _id of entry[1]) {
|
|
1426
|
+
if (_id === id) return entry[0]
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
async discover(peer) {
|
|
1432
|
+
let id = this._getPeerId(peer.id);
|
|
1433
|
+
if (id) return id
|
|
1434
|
+
const data = new peernet.protos['peernet-peer']({id: this.id});
|
|
1435
|
+
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
1436
|
+
|
|
1437
|
+
let response = await peer.request(node.encoded);
|
|
1438
|
+
response = protoFor(response);
|
|
1439
|
+
response = new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
1440
|
+
|
|
1441
|
+
id = response.decoded.id;
|
|
1442
|
+
if (id === this.id) return;
|
|
1443
|
+
|
|
1444
|
+
if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
|
|
1445
|
+
else {
|
|
1446
|
+
const connections = peernet.peerMap.get(id);
|
|
1447
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1448
|
+
connections.push(peer.id);
|
|
1449
|
+
peernet.peerMap.set(peer.id, connections);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return id
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
async discoverHandler(message, peer) {
|
|
1456
|
+
const {id, proto} = message;
|
|
1457
|
+
// if (typeof message.data === 'string') message.data = Buffer.from(message.data)
|
|
1458
|
+
if (proto.name === 'peernet-peer') {
|
|
1459
|
+
const from = proto.decoded.id;
|
|
1460
|
+
if (from === this.id) return;
|
|
1461
|
+
|
|
1462
|
+
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1463
|
+
else {
|
|
1464
|
+
const connections = peernet.peerMap.get(from);
|
|
1465
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1466
|
+
connections.push(peer.id);
|
|
1467
|
+
peernet.peerMap.set(from, connections);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const data = new peernet.protos['peernet-peer-response']({id: this.id});
|
|
1471
|
+
const node = await peernet.prepareMessage(from, data.encoded);
|
|
1472
|
+
|
|
1473
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
1474
|
+
} else if (proto.name === 'peernet-peer-response') {
|
|
1475
|
+
const from = proto.decoded.id;
|
|
1476
|
+
if (from === this.id) return;
|
|
1477
|
+
|
|
1478
|
+
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1479
|
+
else {
|
|
1480
|
+
const connections = peernet.peerMap.get(from);
|
|
1481
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1482
|
+
connections.push(peer.id);
|
|
1483
|
+
peernet.peerMap.set(from, connections);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1583
1489
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1490
|
+
/**
|
|
1491
|
+
* Keep history of fetched address and ptr
|
|
1492
|
+
* @property {Object} address
|
|
1493
|
+
* @property {Object} ptr
|
|
1494
|
+
*/
|
|
1495
|
+
const lastFetched = {
|
|
1496
|
+
address: {
|
|
1497
|
+
value: undefined,
|
|
1498
|
+
timestamp: 0,
|
|
1499
|
+
},
|
|
1500
|
+
ptr: {
|
|
1501
|
+
value: undefined,
|
|
1502
|
+
timestamp: 0,
|
|
1503
|
+
},
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
const getAddress = async () => {
|
|
1507
|
+
const {address} = lastFetched;
|
|
1508
|
+
const now = Math.round(new Date().getTime() / 1000);
|
|
1509
|
+
if (now - address.timestamp > 1200000) {
|
|
1510
|
+
address.value = await fetch('https://icanhazip.com/');
|
|
1511
|
+
address.value = await address.value.text();
|
|
1512
|
+
address.timestamp = Math.round(new Date().getTime() / 1000);
|
|
1513
|
+
lastFetched.address = address;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return address.value
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
const degreesToRadians = (degrees) => {
|
|
1520
|
+
return degrees * Math.PI / 180;
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
|
|
1524
|
+
const earthRadiusKm = 6371;
|
|
1525
|
+
|
|
1526
|
+
const dLat = degreesToRadians(lat2-lat1);
|
|
1527
|
+
const dLon = degreesToRadians(lon2-lon1);
|
|
1528
|
+
|
|
1529
|
+
lat1 = degreesToRadians(lat1);
|
|
1530
|
+
lat2 = degreesToRadians(lat2);
|
|
1531
|
+
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
1532
|
+
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
1533
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
1534
|
+
return earthRadiusKm * c;
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
class DhtEarth {
|
|
1538
|
+
/**
|
|
1539
|
+
*
|
|
1540
|
+
*/
|
|
1541
|
+
constructor() {
|
|
1542
|
+
this.providerMap = new Map();
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* @param {Object} address
|
|
1547
|
+
* @return {Object} {latitude: lat, longitude: lon}
|
|
1548
|
+
*/
|
|
1549
|
+
async getCoordinates(address) {
|
|
1550
|
+
// const {address} = parseAddress(provider)
|
|
1551
|
+
const request = `https://whereis.leofcoin.org/?ip=${address}`;
|
|
1552
|
+
let response = await fetch(request);
|
|
1553
|
+
response = await response.json();
|
|
1554
|
+
const {lat, lon} = response;
|
|
1555
|
+
return {latitude: lat, longitude: lon}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* @param {Object} peer
|
|
1560
|
+
* @param {Object} provider
|
|
1561
|
+
* @return {Object} {provider, distance}
|
|
1562
|
+
*/
|
|
1563
|
+
async getDistance(peer, provider) {
|
|
1564
|
+
const {latitude, longitude} = await this.getCoordinates(provider.address);
|
|
1565
|
+
return {provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude)}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* @param {Array} providers
|
|
1570
|
+
* @return {Object} closestPeer
|
|
1571
|
+
*/
|
|
1572
|
+
async closestPeer(providers) {
|
|
1573
|
+
let all = [];
|
|
1574
|
+
const address = await getAddress();
|
|
1575
|
+
const peerLoc = await this.getCoordinates(address);
|
|
1576
|
+
|
|
1577
|
+
for (const provider of providers) {
|
|
1578
|
+
if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
|
|
1579
|
+
else all.push(this.getDistance(peerLoc, provider));
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
all = await Promise.all(all);
|
|
1583
|
+
all = all.sort((previous, current) => previous.distance - current.distance);
|
|
1584
|
+
return all[0].provider;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* @param {String} hash
|
|
1589
|
+
* @return {Array} providers
|
|
1590
|
+
*/
|
|
1591
|
+
providersFor(hash) {
|
|
1592
|
+
return this.providerMap.get(hash);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* @param {String} address
|
|
1597
|
+
* @param {String} hash
|
|
1598
|
+
* @return {Array} providers
|
|
1599
|
+
*/
|
|
1600
|
+
async addProvider(address, hash) {
|
|
1601
|
+
let providers = [];
|
|
1602
|
+
if (this.providerMap.has(hash)) providers = this.providerMap.get(hash);
|
|
1603
|
+
|
|
1604
|
+
providers = new Set([...providers, address]);
|
|
1605
|
+
this.providerMap.set(hash, providers);
|
|
1606
|
+
return providers;
|
|
1607
|
+
}
|
|
1641
1608
|
}
|
|
1642
1609
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
},
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
return address.value
|
|
1610
|
+
var testnets = {
|
|
1611
|
+
'leofcoin:olivia': {
|
|
1612
|
+
messagePrefix: '\u0019Leofcoin Signed Message:',
|
|
1613
|
+
pubKeyHash: 0x73, // o
|
|
1614
|
+
scriptHash: 0x76, // p
|
|
1615
|
+
multiTxHash: 0x8b4125, // omtx
|
|
1616
|
+
payments: {
|
|
1617
|
+
version: 0,
|
|
1618
|
+
unspent: 0x1fa443d7 // ounsp
|
|
1619
|
+
},
|
|
1620
|
+
wif: 0x7D, // s
|
|
1621
|
+
multiCodec: 0x7c4,
|
|
1622
|
+
bip32: { public: 0x13BBF2D5, private: 0x13BBCBC5 }
|
|
1623
|
+
},
|
|
1624
|
+
'bitcoin:testnet': {
|
|
1625
|
+
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
1626
|
+
bech32: 'tb',
|
|
1627
|
+
pubKeyHash: 0x6f,
|
|
1628
|
+
scriptHash: 0xc4,
|
|
1629
|
+
wif: 0xef,
|
|
1630
|
+
bip32: {
|
|
1631
|
+
public: 0x043587cf,
|
|
1632
|
+
private: 0x04358394
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1670
1636
|
};
|
|
1671
1637
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1638
|
+
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
|
1639
|
+
/**
|
|
1640
|
+
* Main network
|
|
1641
|
+
* @return {messagePrefix, pubKeyHash, scriptHash, wif, bip32}
|
|
1642
|
+
*/
|
|
1643
|
+
const leofcoin = {
|
|
1644
|
+
messagePrefix: '\u0019Leofcoin Signed Message:',
|
|
1645
|
+
pubKeyHash: 0x30, // L
|
|
1646
|
+
scriptHash: 0x37, // P
|
|
1647
|
+
multiTxHash: 0x3adeed, // Lmtx
|
|
1648
|
+
payments: {
|
|
1649
|
+
version: 0,
|
|
1650
|
+
unspent: 0x0d6e0327 // Lunsp
|
|
1651
|
+
},
|
|
1652
|
+
coin_type: 640,
|
|
1653
|
+
wif: 0x3F, // S
|
|
1654
|
+
multiCodec: 0x3c4,
|
|
1655
|
+
bip32: { public: 0x13BBF2D4, private: 0x13BBCBC4 },
|
|
1656
|
+
testnet: testnets['leofcoin:olivia']
|
|
1657
|
+
};
|
|
1658
|
+
|
|
1659
|
+
const bitcoin = {
|
|
1660
|
+
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
1661
|
+
bech32: 'bc',
|
|
1662
|
+
pubKeyHash: 0x00,
|
|
1663
|
+
scriptHash: 0x05,
|
|
1664
|
+
wif: 0x80,
|
|
1665
|
+
coin_type: 0,
|
|
1666
|
+
bip32: {
|
|
1667
|
+
public: 0x0488b21e, private: 0x0488ade4
|
|
1668
|
+
},
|
|
1669
|
+
testnet: testnets['bitcoin:testnet']
|
|
1670
|
+
};
|
|
1671
|
+
|
|
1672
|
+
const litecoin = {
|
|
1673
|
+
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
1674
|
+
pubKeyHash: 0x30,
|
|
1675
|
+
scriptHash: 0x32,
|
|
1676
|
+
wif: 0xb0,
|
|
1677
|
+
bip32: {
|
|
1678
|
+
public: 0x019da462,
|
|
1679
|
+
private: 0x019d9cfe
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
|
|
1683
|
+
const ethereum = {
|
|
1684
|
+
messagePrefix: '\x19Ethereum Signed Message:\n',
|
|
1685
|
+
pubKeyHash: 0x30,
|
|
1686
|
+
scriptHash: 0x32,
|
|
1687
|
+
bip32: {
|
|
1688
|
+
private: 0x0488ADE4, public: 0x0488B21E
|
|
1689
|
+
},
|
|
1690
|
+
coin_type: 60,
|
|
1691
|
+
wif: 0x45,//E
|
|
1692
|
+
multiCodec: 0x3c5
|
|
1693
|
+
};
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Our & supported networks
|
|
1697
|
+
* @return {leofcoin, olivia}
|
|
1698
|
+
*/
|
|
1699
|
+
var networks = {
|
|
1700
|
+
leofcoin,
|
|
1701
|
+
bitcoin,
|
|
1702
|
+
litecoin,
|
|
1703
|
+
ethereum
|
|
1674
1704
|
};
|
|
1675
1705
|
|
|
1676
|
-
const
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
1686
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
1687
|
-
return earthRadiusKm * c;
|
|
1706
|
+
const fromNetworkString = network => {
|
|
1707
|
+
const parts = network.split(':');
|
|
1708
|
+
network = networks[parts[0]];
|
|
1709
|
+
if (parts[1]) {
|
|
1710
|
+
if (network[parts[1]]) network = network[parts[1]];
|
|
1711
|
+
|
|
1712
|
+
network.coin_type = 1;
|
|
1713
|
+
}
|
|
1714
|
+
return network;
|
|
1688
1715
|
};
|
|
1689
1716
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1717
|
+
const { encode: encode$1, decode: decode$1 } = bs58check__default;
|
|
1718
|
+
class HDWallet {
|
|
1719
|
+
|
|
1720
|
+
get chainCodeBuffer() {
|
|
1721
|
+
return this.ifNotLocked(() => this.hdnode.chainCode)
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
get chainCode() {
|
|
1725
|
+
return this.ifNotLocked(() => this.chainCodeBuffer.toString('hex'))
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
get privateKeyBuffer() {
|
|
1729
|
+
return this.ifNotLocked(() => this.hdnode.privateKey)
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
get privateKey() {
|
|
1733
|
+
return this.ifNotLocked(() => this.privateKeyBuffer.toString('hex'))
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
get publicKeyBuffer() {
|
|
1737
|
+
return this.ifNotLocked(() => this.hdnode.publicKey)
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
get publicKey() {
|
|
1741
|
+
return this.ifNotLocked(() => this.publicKeyBuffer.toString('hex'))
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
get address() {
|
|
1745
|
+
// override testnet coin_type
|
|
1746
|
+
let coin_type = this.hdnode.network.coin_type;
|
|
1747
|
+
if (coin_type === 1) {
|
|
1748
|
+
if (this.networkName?.split(':')[0] === 'ethereum') coin_type = 60;
|
|
1749
|
+
if (this.networkName?.split(':')[0] === 'leofcoin') coin_type = 640;
|
|
1750
|
+
}
|
|
1751
|
+
if (coin_type === 60 || coin_type === 640) {
|
|
1752
|
+
let buffer = ecc.pointFromScalar(this.hdnode.__D, false);
|
|
1753
|
+
buffer = Buffer.from(publicKeyConvert(buffer, false)).slice(1);
|
|
1754
|
+
let hash = createKeccakHash('keccak256').update(buffer).digest();
|
|
1755
|
+
return hash.slice(-20).toString('hex')
|
|
1756
|
+
}
|
|
1757
|
+
return encode$1(this.neutered.publicKeyBuffer)
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
get accountAddress() {
|
|
1761
|
+
return this.ifNotLocked(() => encode$1(this.hdnode.publicKeyBuffer))
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
get isTestnet() {
|
|
1765
|
+
if (typeof network === 'string')
|
|
1766
|
+
this.hdnode.network = fromNetworkString(network);
|
|
1767
|
+
|
|
1768
|
+
return Boolean(this.hdnode.network.coin_type === 1)
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
constructor(network, hdnode) {
|
|
1772
|
+
if (typeof network === 'string') {
|
|
1773
|
+
this.networkName = network;
|
|
1774
|
+
this.network = fromNetworkString(network);
|
|
1775
|
+
} else if (typeof network === 'object')
|
|
1776
|
+
this.network = network;
|
|
1777
|
+
|
|
1778
|
+
if (hdnode) this.defineHDNode(hdnode);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
ifNotLocked(fn, params) {
|
|
1782
|
+
if (!this.locked) return fn(params);
|
|
1783
|
+
return null
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
defineHDNode(value) {
|
|
1787
|
+
Object.defineProperty(this, 'hdnode', {
|
|
1788
|
+
configurable: false,
|
|
1789
|
+
writable: false,
|
|
1790
|
+
value: value
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
validateNetwork(network) {
|
|
1795
|
+
if (!network && !this.network) return console.error(`expected network to be defined`);
|
|
1796
|
+
if (!network && this.network) network = this.network;
|
|
1797
|
+
if (typeof network === 'string') network = fromNetworkString(network);
|
|
1798
|
+
if (typeof network !== 'object') return console.error('network not found');
|
|
1799
|
+
return network;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
async generate(network) {
|
|
1803
|
+
network = this.validateNetwork(network);
|
|
1804
|
+
const mnemonic = generateMnemonic();
|
|
1805
|
+
const seed = await mnemonicToSeed(mnemonic);
|
|
1806
|
+
this.defineHDNode(bip32.fromSeed(seed, network));
|
|
1807
|
+
return mnemonic; // userpw
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/**
|
|
1811
|
+
* recover using mnemonic (recovery word list)
|
|
1812
|
+
*/
|
|
1813
|
+
async recover(mnemonic, network) {
|
|
1814
|
+
network = this.validateNetwork(network);
|
|
1815
|
+
const seed = await mnemonicToSeed(mnemonic);
|
|
1816
|
+
this.defineHDNode(bip32.fromSeed(seed, network));
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
load(base58, network) {
|
|
1820
|
+
network = this.validateNetwork(network);
|
|
1821
|
+
this.defineHDNode(bip32.fromBase58(base58, network));
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
save() {
|
|
1825
|
+
return this.hdnode.toBase58();
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
fromAddress(address, chainCode, network) {
|
|
1829
|
+
network = this.validateNetwork(network);
|
|
1830
|
+
// if (network.coin_type === 60) {
|
|
1831
|
+
// address = Buffer.from(address, 'hex')
|
|
1832
|
+
// } else {
|
|
1833
|
+
address = decode$1(address);
|
|
1834
|
+
// }
|
|
1835
|
+
|
|
1836
|
+
if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = address.slice(1);
|
|
1837
|
+
this.defineHDNode(bip32.fromPublicKey(address, chainCode, network));
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
fromPublicKey(hex, chainCode, network) {
|
|
1841
|
+
network = this.validateNetwork(network);
|
|
1842
|
+
if (!Buffer.isBuffer(hex)) hex = Buffer.from(hex, 'hex');
|
|
1843
|
+
if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = hex.slice(1);
|
|
1844
|
+
this.defineHDNode(bip32.fromPublicKey(hex, chainCode, network));
|
|
1845
|
+
}
|
|
1761
1846
|
}
|
|
1762
1847
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1848
|
+
const { encode, decode } = bs58check;
|
|
1849
|
+
|
|
1850
|
+
// TODO: multihash addresses
|
|
1851
|
+
class HDAccount {
|
|
1852
|
+
/**
|
|
1853
|
+
* @param {number} depth - acount depth
|
|
1854
|
+
*/
|
|
1855
|
+
constructor(node, depth = 0) {
|
|
1856
|
+
this.node = node;
|
|
1857
|
+
this.depth = depth;
|
|
1858
|
+
this._prefix = `m/44'/${node.network.coin_type}'/${depth}'/`;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
/**
|
|
1862
|
+
* @param {number} index - address index
|
|
1863
|
+
*/
|
|
1864
|
+
internal(index = 0) {
|
|
1865
|
+
return this.node.derivePath(`${this._prefix}1/${index}`)
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/**
|
|
1869
|
+
* @param {number} index - address index
|
|
1870
|
+
*/
|
|
1871
|
+
external(index = 0) {
|
|
1872
|
+
return this.node.derivePath(`${this._prefix}0/${index}`)
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
class MultiWallet extends HDWallet {
|
|
1877
|
+
constructor(network, hdnode) {
|
|
1878
|
+
const networkName = network;
|
|
1879
|
+
super(network, hdnode);
|
|
1880
|
+
if (typeof networkName === 'string') this.networkName = networkName;
|
|
1881
|
+
this.multiCodec = this.network.multiCodec;
|
|
1882
|
+
this.version = 0x00;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
get id() {
|
|
1886
|
+
const buffer = Buffer.concat([
|
|
1887
|
+
Buffer.from(varint.encode(this.multiCodec)),
|
|
1888
|
+
Buffer.from(this.account(0).node.neutered.publicKey, 'hex')
|
|
1889
|
+
]);
|
|
1890
|
+
return encode(buffer)
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
get multiWIF() {
|
|
1894
|
+
return this.ifNotLocked(() => this.encode())
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
get neutered() {
|
|
1898
|
+
const neutered = this.ifNotLocked(() => new MultiWallet(this.network, this.hdnode.neutered()));
|
|
1899
|
+
if (neutered) this._neutered = neutered;
|
|
1900
|
+
return this._neutered
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
fromId(id) {
|
|
1904
|
+
let buffer = decode(id);
|
|
1905
|
+
varint.decode(buffer);
|
|
1906
|
+
buffer = buffer.slice(varint.decode.bytes);
|
|
1907
|
+
this.fromPublicKey(buffer, null, this.network);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
lock(key, multiWIF) {
|
|
1911
|
+
if (!multiWIF) multiWIF = this.multiWIF;
|
|
1912
|
+
this.encrypted = AES.encrypt(multiWIF.toString('hex'), key).toString();
|
|
1913
|
+
this.locked = true;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
unlock(key, encrypted) {
|
|
1917
|
+
if (!encrypted) encrypted = this.encrypted;
|
|
1918
|
+
this.import(AES.decrypt(encrypted, key).toString(ENC));
|
|
1919
|
+
this.locked = false;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
export() {
|
|
1923
|
+
return this.encode();
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
/**
|
|
1927
|
+
* encodes the multiWIF and loads wallet from bs58
|
|
1928
|
+
*
|
|
1929
|
+
* @param {multiWIF} multiWIF - note a multiWIF is not the same as a wif
|
|
1930
|
+
*/
|
|
1931
|
+
import(multiWIF) {
|
|
1932
|
+
const { bs58, version, multiCodec } = this.decode(multiWIF);
|
|
1933
|
+
this.network = Object.values(networks).reduce((p, c) => {
|
|
1934
|
+
if (c.multiCodec===multiCodec) return c
|
|
1935
|
+
else if (c.testnet && c.testnet.multiCodec === multiCodec) return c.testnet
|
|
1936
|
+
else return p
|
|
1937
|
+
}, networks['leofcoin']);
|
|
1938
|
+
this.load(bs58, this.network);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* @return base58Check encoded string
|
|
1943
|
+
*/
|
|
1944
|
+
encode() {
|
|
1945
|
+
const buffer = Buffer.concat([
|
|
1946
|
+
Buffer.from(varint.encode(this.version)),
|
|
1947
|
+
Buffer.from(varint.encode(this.multiCodec)),
|
|
1948
|
+
decode(this.save())
|
|
1949
|
+
]);
|
|
1950
|
+
return encode(buffer);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
decode(bs58) {
|
|
1954
|
+
let buffer = decode(bs58);
|
|
1955
|
+
const version = varint.decode(buffer);
|
|
1956
|
+
buffer = buffer.slice(varint.decode.bytes);
|
|
1957
|
+
const multiCodec = varint.decode(buffer);
|
|
1958
|
+
buffer = buffer.slice(varint.decode.bytes);
|
|
1959
|
+
bs58 = encode(buffer);
|
|
1960
|
+
if (version !== this.version) throw TypeError('Invalid version');
|
|
1961
|
+
if (this.multiCodec !== multiCodec) throw TypeError('Invalid multiCodec');
|
|
1962
|
+
return { version, multiCodec, bs58 };
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
sign(hash) {
|
|
1966
|
+
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1967
|
+
.sign(hash, this.privateKeyBuffer);
|
|
1968
|
+
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
verify(multiSignature, hash) {
|
|
1972
|
+
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1973
|
+
.verify(multiSignature, hash, this.publicKeyBuffer)
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
/**
|
|
1977
|
+
* @param {number} account - account to return chain for
|
|
1978
|
+
* @return { internal(addressIndex), external(addressIndex) }
|
|
1979
|
+
*/
|
|
1980
|
+
account(index) {
|
|
1981
|
+
return new HDAccount(new MultiWallet(this.network, this.hdnode), index);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* m / purpose' / coin_type' / account' / change / aadress_index
|
|
1986
|
+
*
|
|
1987
|
+
* see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
|
1988
|
+
*/
|
|
1989
|
+
derivePath(path) {
|
|
1990
|
+
return new MultiWallet(this.network, this.hdnode.derivePath(path))
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
derive(index) {
|
|
1994
|
+
return new MultiWallet(this.network, this.hdnode.derive(index));
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1847
1997
|
|
|
1848
|
-
|
|
1849
|
-
|
|
1998
|
+
class MessageHandler {
|
|
1999
|
+
constructor(network) {
|
|
2000
|
+
this.network = network;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* hash and sign message
|
|
2004
|
+
*
|
|
2005
|
+
* @param {object} message
|
|
2006
|
+
* @param {Buffer} message.from peer id
|
|
2007
|
+
* @param {Buffer} message.to peer id
|
|
2008
|
+
* @param {string} message.data Peernet message
|
|
2009
|
+
* (PeernetMessage excluded) encoded as a string
|
|
2010
|
+
* @return signature
|
|
2011
|
+
*/
|
|
2012
|
+
async hashAndSignMessage(message) {
|
|
2013
|
+
const hasher = new PeernetHash(message, {name: 'peernet-message'});
|
|
2014
|
+
const identity = await walletStore.get('identity');
|
|
2015
|
+
|
|
2016
|
+
const wallet = new MultiWallet(this.network);
|
|
2017
|
+
wallet.import(identity.multiWIF);
|
|
2018
|
+
return wallet.sign(hasher.hash.slice(0, 32))
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* @param {String} from - peer id
|
|
2023
|
+
* @param {String} to - peer id
|
|
2024
|
+
* @param {String|PeernetMessage} data - data encoded message string
|
|
2025
|
+
* or the messageNode itself
|
|
2026
|
+
*/
|
|
2027
|
+
async prepareMessage(from, to, data, id) {
|
|
2028
|
+
if (data.encoded) data = data.encoded;
|
|
2029
|
+
|
|
2030
|
+
const message = {
|
|
2031
|
+
from,
|
|
2032
|
+
to,
|
|
2033
|
+
data,
|
|
2034
|
+
};
|
|
2035
|
+
const signature = await this.hashAndSignMessage(message);
|
|
2036
|
+
const node = new PeernetMessage({
|
|
2037
|
+
...message,
|
|
2038
|
+
signature,
|
|
2039
|
+
});
|
|
2040
|
+
|
|
2041
|
+
return node
|
|
2042
|
+
}
|
|
1850
2043
|
}
|
|
1851
2044
|
|
|
1852
2045
|
const encapsulatedError = () => {
|
|
@@ -1862,671 +2055,592 @@ const nothingFoundError = (hash) => {
|
|
|
1862
2055
|
return new Error(`nothing found for ${hash}`)
|
|
1863
2056
|
};
|
|
1864
2057
|
|
|
1865
|
-
globalThis.leofcoin = globalThis.leofcoin || {};
|
|
1866
|
-
globalThis.globalSub = globalThis.globalSub || new PubSub({verbose: true});
|
|
1867
|
-
|
|
1868
|
-
/**
|
|
1869
|
-
* @access public
|
|
1870
|
-
* @example
|
|
1871
|
-
* const peernet = new Peernet();
|
|
1872
|
-
*/
|
|
1873
|
-
class Peernet {
|
|
1874
|
-
/**
|
|
1875
|
-
* @access public
|
|
1876
|
-
* @param {Object} options
|
|
1877
|
-
* @param {String} options.network - desired network
|
|
1878
|
-
* @param {String} options.root - path to root directory
|
|
1879
|
-
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
1880
|
-
*
|
|
1881
|
-
* @return {Promise} instance of Peernet
|
|
1882
|
-
*
|
|
1883
|
-
* @example
|
|
1884
|
-
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
1885
|
-
*/
|
|
1886
|
-
constructor(options = {}) {
|
|
1887
|
-
this._discovered = [];
|
|
1888
|
-
/**
|
|
1889
|
-
* @property {String} network - current network
|
|
1890
|
-
*/
|
|
1891
|
-
this.network = options.network || 'leofcoin';
|
|
1892
|
-
const parts = this.network.split(':');
|
|
1893
|
-
|
|
1894
|
-
if (!options.storePrefix) options.storePrefix = 'lfc';
|
|
1895
|
-
if (!options.port) options.port = 2000;
|
|
1896
|
-
if (!options.root) {
|
|
1897
|
-
if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
|
|
1898
|
-
else options.root = `.${this.network}/peernet`;
|
|
1899
|
-
}
|
|
1900
|
-
globalThis.peernet = this;
|
|
1901
|
-
this.bw = {
|
|
1902
|
-
up: 0,
|
|
1903
|
-
down: 0,
|
|
1904
|
-
};
|
|
1905
|
-
return this._init(options)
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
get defaultStores() {
|
|
1909
|
-
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
addProto(name, proto) {
|
|
1913
|
-
if (!this.protos[name]) this.protos[name] = proto;
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
addCodec(name, codec) {
|
|
1917
|
-
if (!this.codecs[name]) this.codecs[name] = codec;
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
async addStore(name, prefix, root, isPrivate = true) {
|
|
1921
|
-
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
1922
|
-
name === 'data' || name === 'message') isPrivate = false;
|
|
1923
|
-
|
|
1924
|
-
let Storage;
|
|
1925
|
-
if (this.hasDaemon) {
|
|
1926
|
-
Storage = LeofcoinStorageClient;
|
|
1927
|
-
} else {
|
|
1928
|
-
Storage = LeofcoinStorage
|
|
1929
|
-
}
|
|
1930
|
-
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
1931
|
-
await new Storage(`${prefix}-${name}`, root);
|
|
1932
|
-
|
|
1933
|
-
globalThis[`${name}Store`].private = isPrivate;
|
|
1934
|
-
if (!isPrivate) this.stores.push(name);
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
/**
|
|
1939
|
-
* @see MessageHandler
|
|
1940
|
-
*/
|
|
1941
|
-
prepareMessage(to, data) {
|
|
1942
|
-
return this._messageHandler.prepareMessage(this.id, to, data)
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
/**
|
|
1946
|
-
* @access public
|
|
1947
|
-
*
|
|
1948
|
-
* @return {Array} peerId
|
|
1949
|
-
*/
|
|
1950
|
-
get peers() {
|
|
1951
|
-
return [...connections.values()]
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
/**
|
|
1955
|
-
* @private
|
|
1956
|
-
*
|
|
1957
|
-
* @param {Object} options
|
|
1958
|
-
* @param {String} options.root - path to root directory
|
|
1959
|
-
*
|
|
1960
|
-
* @return {Promise} instance of Peernet
|
|
1961
|
-
*/
|
|
1962
|
-
async _init(options) {
|
|
1963
|
-
// peernetDHT aka closesPeer by coordinates
|
|
1964
|
-
/**
|
|
1965
|
-
* @type {Object}
|
|
1966
|
-
* @property {Object} peer Instance of Peer
|
|
1967
|
-
*/
|
|
1968
|
-
this.dht = new DhtEarth();
|
|
1969
|
-
/**
|
|
1970
|
-
* @type {Map}
|
|
1971
|
-
* @property {Object} peer Instance of Peer
|
|
1972
|
-
*/
|
|
1973
|
-
this.peerMap = new Map();
|
|
1974
|
-
this.stores = [];
|
|
1975
|
-
this.requestProtos = {};
|
|
1976
|
-
this.storePrefix = options.storePrefix;
|
|
1977
|
-
this.root = options.root;
|
|
1978
|
-
|
|
1979
|
-
/**
|
|
1980
|
-
* proto Object containing protos
|
|
1981
|
-
* @type {Object}
|
|
1982
|
-
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
1983
|
-
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
1984
|
-
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
1985
|
-
* @property {DataMessage} protos[peernet-data] messageNode
|
|
1986
|
-
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
1987
|
-
*/
|
|
1988
|
-
globalThis.peernet.protos = {
|
|
1989
|
-
'peernet-request': RequestMessage,
|
|
1990
|
-
'peernet-response': ResponseMessage,
|
|
1991
|
-
'peernet-peer': PeerMessage,
|
|
1992
|
-
'peernet-peer-response': PeerMessageResponse,
|
|
1993
|
-
'peernet-message': PeernetMessage,
|
|
1994
|
-
'peernet-dht': DHTMessage,
|
|
1995
|
-
'peernet-dht-response': DHTMessageResponse,
|
|
1996
|
-
'peernet-data': DataMessage,
|
|
1997
|
-
'peernet-data-response': DataMessageResponse,
|
|
1998
|
-
'peernet-ps': PsMessage,
|
|
1999
|
-
'chat-message': ChatMessage,
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
this.protos = globalThis.peernet.protos;
|
|
2003
|
-
this.codecs = codecs;
|
|
2004
|
-
|
|
2005
|
-
this._messageHandler = new MessageHandler(this.network);
|
|
2006
|
-
|
|
2007
|
-
const {daemon, environment} = await target();
|
|
2008
|
-
this.hasDaemon = daemon;
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
if (
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
put: async (hash,
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
if (
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
/**
|
|
2453
|
-
* put content
|
|
2454
|
-
*
|
|
2455
|
-
* @param {String} hash
|
|
2456
|
-
* @param {Buffer} data
|
|
2457
|
-
* @param {String} store - storeName to access
|
|
2458
|
-
*/
|
|
2459
|
-
async put(hash, data, store = 'data') {
|
|
2460
|
-
store = globalThis[`${store}Store`];
|
|
2461
|
-
return store.put(hash, data)
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
/**
|
|
2465
|
-
* @param {String} hash
|
|
2466
|
-
* @return {Boolean}
|
|
2467
|
-
*/
|
|
2468
|
-
async has(hash) {
|
|
2469
|
-
const store = await this.whichStore([...this.stores], hash);
|
|
2470
|
-
if (store) {
|
|
2471
|
-
if (store.private) return false
|
|
2472
|
-
else return true
|
|
2473
|
-
}
|
|
2474
|
-
return false
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
/**
|
|
2478
|
-
*
|
|
2479
|
-
* @param {String} topic
|
|
2480
|
-
* @param {String|Object|Array|Boolean|Buffer} data
|
|
2481
|
-
*/
|
|
2482
|
-
async publish(topic, data) {
|
|
2483
|
-
// globalSub.publish(topic, data)
|
|
2484
|
-
if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
|
|
2485
|
-
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
2486
|
-
const id = Math.random().toString(36).slice(-12);
|
|
2487
|
-
data = new PsMessage({data, topic});
|
|
2488
|
-
for (const peer of this.peers) {
|
|
2489
|
-
if (peer.connection._connected) {
|
|
2490
|
-
if (peer.id.toString() !== this.peerId.toString()) {
|
|
2491
|
-
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2492
|
-
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2493
|
-
}
|
|
2494
|
-
} else {
|
|
2495
|
-
this.removePeer(peer);
|
|
2496
|
-
}
|
|
2497
|
-
// TODO: if peer subscribed
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
createHash(data, name) {
|
|
2502
|
-
return new PeernetHash(data, {name})
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
/**
|
|
2506
|
-
*
|
|
2507
|
-
* @param {String} topic
|
|
2508
|
-
* @param {Method} cb
|
|
2509
|
-
*/
|
|
2510
|
-
async subscribe(topic, cb) {
|
|
2511
|
-
// TODO: if peer subscribed
|
|
2512
|
-
globalSub.subscribe(topic, cb);
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
async removePeer(peer) {
|
|
2516
|
-
connections.delete(peer.id);
|
|
2517
|
-
}
|
|
2518
|
-
|
|
2519
|
-
get Buffer() {
|
|
2520
|
-
return Buffer
|
|
2521
|
-
}
|
|
2522
|
-
// async block(index) {
|
|
2523
|
-
// const _values = []
|
|
2524
|
-
// for (const peer of this.peers) {
|
|
2525
|
-
// const value = await peer.request({type: 'block', index})
|
|
2526
|
-
// console.log(value);
|
|
2527
|
-
// }
|
|
2528
|
-
//
|
|
2529
|
-
// }
|
|
2058
|
+
globalThis.leofcoin = globalThis.leofcoin || {};
|
|
2059
|
+
globalThis.globalSub = globalThis.globalSub || new PubSub({verbose: true});
|
|
2060
|
+
|
|
2061
|
+
/**
|
|
2062
|
+
* @access public
|
|
2063
|
+
* @example
|
|
2064
|
+
* const peernet = new Peernet();
|
|
2065
|
+
*/
|
|
2066
|
+
class Peernet {
|
|
2067
|
+
/**
|
|
2068
|
+
* @access public
|
|
2069
|
+
* @param {Object} options
|
|
2070
|
+
* @param {String} options.network - desired network
|
|
2071
|
+
* @param {String} options.root - path to root directory
|
|
2072
|
+
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
2073
|
+
*
|
|
2074
|
+
* @return {Promise} instance of Peernet
|
|
2075
|
+
*
|
|
2076
|
+
* @example
|
|
2077
|
+
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
2078
|
+
*/
|
|
2079
|
+
constructor(options = {}) {
|
|
2080
|
+
this._discovered = [];
|
|
2081
|
+
/**
|
|
2082
|
+
* @property {String} network - current network
|
|
2083
|
+
*/
|
|
2084
|
+
this.network = options.network || 'leofcoin';
|
|
2085
|
+
const parts = this.network.split(':');
|
|
2086
|
+
|
|
2087
|
+
if (!options.storePrefix) options.storePrefix = 'lfc';
|
|
2088
|
+
if (!options.port) options.port = 2000;
|
|
2089
|
+
if (!options.root) {
|
|
2090
|
+
if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
|
|
2091
|
+
else options.root = `.${this.network}/peernet`;
|
|
2092
|
+
}
|
|
2093
|
+
globalThis.peernet = this;
|
|
2094
|
+
this.bw = {
|
|
2095
|
+
up: 0,
|
|
2096
|
+
down: 0,
|
|
2097
|
+
};
|
|
2098
|
+
return this._init(options)
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
get defaultStores() {
|
|
2102
|
+
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
addProto(name, proto) {
|
|
2106
|
+
if (!this.protos[name]) this.protos[name] = proto;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
addCodec(name, codec) {
|
|
2110
|
+
if (!this.codecs[name]) this.codecs[name] = codec;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
async addStore(name, prefix, root, isPrivate = true) {
|
|
2114
|
+
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
2115
|
+
name === 'data' || name === 'message') isPrivate = false;
|
|
2116
|
+
|
|
2117
|
+
let Storage;
|
|
2118
|
+
if (this.hasDaemon) {
|
|
2119
|
+
Storage = LeofcoinStorageClient;
|
|
2120
|
+
} else {
|
|
2121
|
+
Storage = globalThis.LeofcoinStorage?.default ? globalThis.LeofcoinStorage.default : LeofcoinStorage;
|
|
2122
|
+
}
|
|
2123
|
+
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
2124
|
+
await new Storage(`${prefix}-${name}`, root);
|
|
2125
|
+
|
|
2126
|
+
globalThis[`${name}Store`].private = isPrivate;
|
|
2127
|
+
if (!isPrivate) this.stores.push(name);
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
/**
|
|
2132
|
+
* @see MessageHandler
|
|
2133
|
+
*/
|
|
2134
|
+
prepareMessage(to, data) {
|
|
2135
|
+
return this._messageHandler.prepareMessage(this.id, to, data)
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* @access public
|
|
2140
|
+
*
|
|
2141
|
+
* @return {Array} peerId
|
|
2142
|
+
*/
|
|
2143
|
+
get peers() {
|
|
2144
|
+
return [...connections.values()]
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
/**
|
|
2148
|
+
* @private
|
|
2149
|
+
*
|
|
2150
|
+
* @param {Object} options
|
|
2151
|
+
* @param {String} options.root - path to root directory
|
|
2152
|
+
*
|
|
2153
|
+
* @return {Promise} instance of Peernet
|
|
2154
|
+
*/
|
|
2155
|
+
async _init(options) {
|
|
2156
|
+
// peernetDHT aka closesPeer by coordinates
|
|
2157
|
+
/**
|
|
2158
|
+
* @type {Object}
|
|
2159
|
+
* @property {Object} peer Instance of Peer
|
|
2160
|
+
*/
|
|
2161
|
+
this.dht = new DhtEarth();
|
|
2162
|
+
/**
|
|
2163
|
+
* @type {Map}
|
|
2164
|
+
* @property {Object} peer Instance of Peer
|
|
2165
|
+
*/
|
|
2166
|
+
this.peerMap = new Map();
|
|
2167
|
+
this.stores = [];
|
|
2168
|
+
this.requestProtos = {};
|
|
2169
|
+
this.storePrefix = options.storePrefix;
|
|
2170
|
+
this.root = options.root;
|
|
2171
|
+
|
|
2172
|
+
/**
|
|
2173
|
+
* proto Object containing protos
|
|
2174
|
+
* @type {Object}
|
|
2175
|
+
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
2176
|
+
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
2177
|
+
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
2178
|
+
* @property {DataMessage} protos[peernet-data] messageNode
|
|
2179
|
+
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
2180
|
+
*/
|
|
2181
|
+
globalThis.peernet.protos = {
|
|
2182
|
+
'peernet-request': RequestMessage,
|
|
2183
|
+
'peernet-response': ResponseMessage,
|
|
2184
|
+
'peernet-peer': PeerMessage,
|
|
2185
|
+
'peernet-peer-response': PeerMessageResponse,
|
|
2186
|
+
'peernet-message': PeernetMessage,
|
|
2187
|
+
'peernet-dht': DHTMessage,
|
|
2188
|
+
'peernet-dht-response': DHTMessageResponse,
|
|
2189
|
+
'peernet-data': DataMessage,
|
|
2190
|
+
'peernet-data-response': DataMessageResponse,
|
|
2191
|
+
'peernet-ps': PsMessage,
|
|
2192
|
+
'chat-message': ChatMessage,
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2195
|
+
this.protos = globalThis.peernet.protos;
|
|
2196
|
+
this.codecs = codecs;
|
|
2197
|
+
|
|
2198
|
+
this._messageHandler = new MessageHandler(this.network);
|
|
2199
|
+
|
|
2200
|
+
const {daemon, environment} = await target();
|
|
2201
|
+
this.hasDaemon = daemon;
|
|
2202
|
+
|
|
2203
|
+
HTTP_IMPORT;
|
|
2204
|
+
|
|
2205
|
+
for (const store of this.defaultStores) {
|
|
2206
|
+
await this.addStore(store, options.storePrefix, options.root);
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
try {
|
|
2210
|
+
const pub = await accountStore.get('public');
|
|
2211
|
+
this.id = pub.walletId;
|
|
2212
|
+
} catch (e) {
|
|
2213
|
+
if (e.code === 'ERR_NOT_FOUND') {
|
|
2214
|
+
const wallet = {};
|
|
2215
|
+
const {identity, accounts, config} = await generateAccount(this.network);
|
|
2216
|
+
wallet.identity = identity;
|
|
2217
|
+
wallet.accounts = accounts;
|
|
2218
|
+
wallet.version = 1;
|
|
2219
|
+
walletStore.put(wallet);
|
|
2220
|
+
await accountStore.put('config', config);
|
|
2221
|
+
await accountStore.put('public', {walletId: wallet.identity.walletId});
|
|
2222
|
+
|
|
2223
|
+
this.id = wallet.identity.walletId;
|
|
2224
|
+
} else {
|
|
2225
|
+
throw e
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
this._peerHandler = new PeerDiscovery(this.id);
|
|
2229
|
+
this.peerId = this.id;
|
|
2230
|
+
|
|
2231
|
+
pubsub.subscribe('peer:connected', async (peer) => {
|
|
2232
|
+
console.log(peer);
|
|
2233
|
+
// console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
2234
|
+
// peer.on('peernet.data', async (message) => {
|
|
2235
|
+
// const id = message.id
|
|
2236
|
+
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
2237
|
+
// const proto = protoFor(message.decoded.data)
|
|
2238
|
+
// this._protoHandler({id, proto}, peer)
|
|
2239
|
+
// })
|
|
2240
|
+
});
|
|
2241
|
+
|
|
2242
|
+
pubsub.subscribe('peer:data', async message => {
|
|
2243
|
+
if (!message.data) return
|
|
2244
|
+
const {id, data} = JSON.parse(new TextDecoder().decode(message.data));
|
|
2245
|
+
const uint8Array = new Uint8Array(Object.keys(data).length);
|
|
2246
|
+
for (var i = 0; i < Object.keys(data).length; i++) {
|
|
2247
|
+
uint8Array[i] = data[i];
|
|
2248
|
+
}
|
|
2249
|
+
message = new PeernetMessage(uint8Array);
|
|
2250
|
+
const proto = protoFor(message.decoded.data);
|
|
2251
|
+
|
|
2252
|
+
const from = message.decoded.from;
|
|
2253
|
+
this._protoHandler({id, proto}, this.client.connections[from], from);
|
|
2254
|
+
});
|
|
2255
|
+
|
|
2256
|
+
/**
|
|
2257
|
+
* @access public
|
|
2258
|
+
* @type {PeernetClient}
|
|
2259
|
+
*/
|
|
2260
|
+
this.client = new Client(this.id);
|
|
2261
|
+
if (globalThis.onbeforeunload) {
|
|
2262
|
+
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
2263
|
+
}
|
|
2264
|
+
return this
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
_getPeerId(id) {
|
|
2268
|
+
for (const entry of [...this.peerMap.entries()]) {
|
|
2269
|
+
for (const _id of entry[1]) {
|
|
2270
|
+
if (_id === id) return entry[0]
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
addRequestHandler(name, method) {
|
|
2276
|
+
this.requestProtos[name] = method;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
/**
|
|
2280
|
+
* @private
|
|
2281
|
+
*
|
|
2282
|
+
* @param {Buffer} message - peernet message
|
|
2283
|
+
* @param {PeernetPeer} peer - peernet peer
|
|
2284
|
+
*/
|
|
2285
|
+
async _protoHandler(message, peer, from) {
|
|
2286
|
+
const {id, proto} = message;
|
|
2287
|
+
this.bw.down += proto.encoded.length;
|
|
2288
|
+
|
|
2289
|
+
if (proto.name === 'peernet-dht') {
|
|
2290
|
+
let { hash, store } = proto.decoded;
|
|
2291
|
+
let has;
|
|
2292
|
+
|
|
2293
|
+
if (!store) {
|
|
2294
|
+
has = await this.has(hash);
|
|
2295
|
+
} else {
|
|
2296
|
+
store = globalThis[`${store}Store`];
|
|
2297
|
+
if (store.private) has = false;
|
|
2298
|
+
else has = await store.has(hash);
|
|
2299
|
+
}
|
|
2300
|
+
const data = new DHTMessageResponse({hash, has});
|
|
2301
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2302
|
+
|
|
2303
|
+
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2304
|
+
this.bw.up += node.encoded.length;
|
|
2305
|
+
} else if (proto.name === 'peernet-data') {
|
|
2306
|
+
let { hash, store } = proto.decoded;
|
|
2307
|
+
let data;
|
|
2308
|
+
if (!store) {
|
|
2309
|
+
store = await this.whichStore([...this.stores], hash);
|
|
2310
|
+
} else {
|
|
2311
|
+
store = globalThis[`${store}Store`];
|
|
2312
|
+
}
|
|
2313
|
+
if (store && !store.private) {
|
|
2314
|
+
data = await store.get(hash);
|
|
2315
|
+
|
|
2316
|
+
if (data) {
|
|
2317
|
+
data = new DataMessageResponse({hash, data: data.decoded ? new TextEncoder().encode(JSON.stringify(data.decoded)) : data});
|
|
2318
|
+
|
|
2319
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2320
|
+
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2321
|
+
this.bw.up += node.encoded.length;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
} else if (proto.name === 'peernet-request') {
|
|
2326
|
+
// TODO: make dynamic
|
|
2327
|
+
// exposeddevapi[proto.decoded.request](proto.decoded.params)
|
|
2328
|
+
const method = this.requestProtos[proto.decoded.request];
|
|
2329
|
+
if (method) {
|
|
2330
|
+
const data = await method();
|
|
2331
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2332
|
+
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2333
|
+
|
|
2334
|
+
this.bw.up += node.encoded.length;
|
|
2335
|
+
}
|
|
2336
|
+
} else if (proto.name === 'peernet-ps' &&
|
|
2337
|
+
this._getPeerId(peer.id) !== this.id.toString()) {
|
|
2338
|
+
globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
|
|
2339
|
+
}
|
|
2340
|
+
// }
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/**
|
|
2344
|
+
* performs a walk and resolves first encounter
|
|
2345
|
+
*
|
|
2346
|
+
* @param {String} hash
|
|
2347
|
+
*/
|
|
2348
|
+
async walk(hash) {
|
|
2349
|
+
if (!hash) throw new Error('hash expected, received undefined')
|
|
2350
|
+
const data = new DHTMessage({hash});
|
|
2351
|
+
this.client.id;
|
|
2352
|
+
for (const peer of this.peers) {
|
|
2353
|
+
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2354
|
+
|
|
2355
|
+
const result = await peer.request(node.encoded);
|
|
2356
|
+
|
|
2357
|
+
let proto = protoFor(result.data);
|
|
2358
|
+
|
|
2359
|
+
if (proto.name !== 'peernet-message') throw encapsulatedError()
|
|
2360
|
+
const from = proto.decoded.from;
|
|
2361
|
+
proto = protoFor(proto.decoded.data);
|
|
2362
|
+
|
|
2363
|
+
if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
|
|
2364
|
+
|
|
2365
|
+
// TODO: give ip and port (just used for location)
|
|
2366
|
+
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
2367
|
+
peer.connection.remoteFamily = 'ipv4';
|
|
2368
|
+
peer.connection.remoteAddress = '127.0.0.1';
|
|
2369
|
+
peer.connection.remotePort = '0000';
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
const peerInfo = {
|
|
2373
|
+
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
2374
|
+
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
2375
|
+
port: peer.connection.remotePort || peer.connection.localPort,
|
|
2376
|
+
id: from,
|
|
2377
|
+
};
|
|
2378
|
+
|
|
2379
|
+
if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
|
|
2380
|
+
}
|
|
2381
|
+
return
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
/**
|
|
2385
|
+
* Override DHT behavior, try's finding the content three times
|
|
2386
|
+
*
|
|
2387
|
+
* @param {String} hash
|
|
2388
|
+
*/
|
|
2389
|
+
async providersFor(hash) {
|
|
2390
|
+
let providers = await this.dht.providersFor(hash);
|
|
2391
|
+
// walk the network to find a provider
|
|
2392
|
+
if (!providers || providers.length === 0) {
|
|
2393
|
+
await this.walk(hash);
|
|
2394
|
+
providers = await this.dht.providersFor(hash);
|
|
2395
|
+
// second walk the network to find a provider
|
|
2396
|
+
if (!providers || providers.length === 0) {
|
|
2397
|
+
await this.walk(hash);
|
|
2398
|
+
providers = await this.dht.providersFor(hash);
|
|
2399
|
+
}
|
|
2400
|
+
// last walk
|
|
2401
|
+
if (!providers || providers.length === 0) {
|
|
2402
|
+
await this.walk(hash);
|
|
2403
|
+
providers = await this.dht.providersFor(hash);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
// undefined if no providers given
|
|
2407
|
+
return providers
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
get block() {
|
|
2411
|
+
return {
|
|
2412
|
+
get: async (hash) => {
|
|
2413
|
+
const data = await blockStore.has(hash);
|
|
2414
|
+
if (data) return await blockStore.get(hash)
|
|
2415
|
+
return this.requestData(hash, 'block')
|
|
2416
|
+
},
|
|
2417
|
+
put: async (hash, data) => {
|
|
2418
|
+
if (await blockStore.has(hash)) return
|
|
2419
|
+
return await blockStore.put(hash, data)
|
|
2420
|
+
},
|
|
2421
|
+
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
get transaction() {
|
|
2426
|
+
return {
|
|
2427
|
+
get: async (hash) => {
|
|
2428
|
+
const data = await transactionStore.has(hash);
|
|
2429
|
+
if (data) return await transactionStore.get(hash)
|
|
2430
|
+
return this.requestData(hash, 'transaction')
|
|
2431
|
+
},
|
|
2432
|
+
put: async (hash, data) => {
|
|
2433
|
+
if (await transactionStore.has(hash)) return
|
|
2434
|
+
return await transactionStore.put(hash, data)
|
|
2435
|
+
},
|
|
2436
|
+
has: async (hash) => await transactionStore.has(hash),
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
async requestData(hash, store) {
|
|
2441
|
+
const providers = await this.providersFor(hash);
|
|
2442
|
+
if (!providers || providers.size === 0) throw nothingFoundError(hash)
|
|
2443
|
+
debug(`found ${providers.size} provider(s) for ${hash}`);
|
|
2444
|
+
// get closest peer on earth
|
|
2445
|
+
const closestPeer = await this.dht.closestPeer(providers);
|
|
2446
|
+
// get peer instance by id
|
|
2447
|
+
if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
|
|
2448
|
+
|
|
2449
|
+
const id = closestPeer.id.toString();
|
|
2450
|
+
if (this.peers) {
|
|
2451
|
+
let closest = this.peers.filter((peer) => {
|
|
2452
|
+
if (this._getPeerId(peer.id) === id) return peer
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2455
|
+
let data = new DataMessage({hash, store: store.name ? store.name : store});
|
|
2456
|
+
|
|
2457
|
+
const node = await this.prepareMessage(id, data.encoded);
|
|
2458
|
+
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2459
|
+
else {
|
|
2460
|
+
closest = this.peers.filter((peer) => {
|
|
2461
|
+
if (peer.id.toString() === id) return peer
|
|
2462
|
+
});
|
|
2463
|
+
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2464
|
+
}
|
|
2465
|
+
if (data.data) {
|
|
2466
|
+
console.log(data.data);
|
|
2467
|
+
let proto = protoFor(data.data);
|
|
2468
|
+
proto = protoFor(proto.decoded.data);
|
|
2469
|
+
return proto.decoded.data
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// this.put(hash, proto.decoded.data)
|
|
2473
|
+
}
|
|
2474
|
+
return null
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
get message() {
|
|
2479
|
+
return {
|
|
2480
|
+
/**
|
|
2481
|
+
* Get content for given message hash
|
|
2482
|
+
*
|
|
2483
|
+
* @param {String} hash
|
|
2484
|
+
*/
|
|
2485
|
+
get: async (hash) => {
|
|
2486
|
+
debug(`get message ${hash}`);
|
|
2487
|
+
const message = await messageStore.has(hash);
|
|
2488
|
+
if (message) return await messageStore.get(hash)
|
|
2489
|
+
return this.requestData(hash, 'message')
|
|
2490
|
+
},
|
|
2491
|
+
/**
|
|
2492
|
+
* put message content
|
|
2493
|
+
*
|
|
2494
|
+
* @param {String} hash
|
|
2495
|
+
* @param {Buffer} message
|
|
2496
|
+
*/
|
|
2497
|
+
put: async (hash, message) => await messageStore.put(hash, message),
|
|
2498
|
+
/**
|
|
2499
|
+
* @param {String} hash
|
|
2500
|
+
* @return {Boolean}
|
|
2501
|
+
*/
|
|
2502
|
+
has: async (hash) => await messageStore.has(hash),
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
get data() {
|
|
2507
|
+
return {
|
|
2508
|
+
/**
|
|
2509
|
+
* Get content for given data hash
|
|
2510
|
+
*
|
|
2511
|
+
* @param {String} hash
|
|
2512
|
+
*/
|
|
2513
|
+
get: async (hash) => {
|
|
2514
|
+
debug(`get data ${hash}`);
|
|
2515
|
+
const data = await dataStore.has(hash);
|
|
2516
|
+
if (data) return await dataStore.get(hash)
|
|
2517
|
+
return this.requestData(hash, 'data')
|
|
2518
|
+
},
|
|
2519
|
+
/**
|
|
2520
|
+
* put data content
|
|
2521
|
+
*
|
|
2522
|
+
* @param {String} hash
|
|
2523
|
+
* @param {Buffer} data
|
|
2524
|
+
*/
|
|
2525
|
+
put: async (hash, data) => await dataStore.put(hash, data),
|
|
2526
|
+
/**
|
|
2527
|
+
* @param {String} hash
|
|
2528
|
+
* @return {Boolean}
|
|
2529
|
+
*/
|
|
2530
|
+
has: async (hash) => await dataStore.has(hash),
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
/**
|
|
2535
|
+
* goes trough given stores and tries to find data for given hash
|
|
2536
|
+
* @param {Array} stores
|
|
2537
|
+
* @param {string} hash
|
|
2538
|
+
*/
|
|
2539
|
+
async whichStore(stores, hash) {
|
|
2540
|
+
let store = stores.pop();
|
|
2541
|
+
store = globalThis[`${store}Store`];
|
|
2542
|
+
if (store) {
|
|
2543
|
+
const has = await store.has(hash);
|
|
2544
|
+
if (has) return store
|
|
2545
|
+
if (stores.length > 0) return this.whichStore(stores, hash)
|
|
2546
|
+
} else return null
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
/**
|
|
2550
|
+
* Get content for given hash
|
|
2551
|
+
*
|
|
2552
|
+
* @param {String} hash - the hash of the wanted data
|
|
2553
|
+
* @param {String} store - storeName to access
|
|
2554
|
+
*/
|
|
2555
|
+
async get(hash, store) {
|
|
2556
|
+
debug(`get ${hash}`);
|
|
2557
|
+
let data;
|
|
2558
|
+
if (store) store = globalThis[`${store}Store`];
|
|
2559
|
+
if (!store) store = await this.whichStore([...this.stores], hash);
|
|
2560
|
+
if (store && await store.has(hash)) data = await store.get(hash);
|
|
2561
|
+
if (data) return data
|
|
2562
|
+
|
|
2563
|
+
return this.requestData(hash, store.name ? store.name : store)
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
/**
|
|
2567
|
+
* put content
|
|
2568
|
+
*
|
|
2569
|
+
* @param {String} hash
|
|
2570
|
+
* @param {Buffer} data
|
|
2571
|
+
* @param {String} store - storeName to access
|
|
2572
|
+
*/
|
|
2573
|
+
async put(hash, data, store = 'data') {
|
|
2574
|
+
store = globalThis[`${store}Store`];
|
|
2575
|
+
return store.put(hash, data)
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
/**
|
|
2579
|
+
* @param {String} hash
|
|
2580
|
+
* @return {Boolean}
|
|
2581
|
+
*/
|
|
2582
|
+
async has(hash) {
|
|
2583
|
+
const store = await this.whichStore([...this.stores], hash);
|
|
2584
|
+
if (store) {
|
|
2585
|
+
if (store.private) return false
|
|
2586
|
+
else return true
|
|
2587
|
+
}
|
|
2588
|
+
return false
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
/**
|
|
2592
|
+
*
|
|
2593
|
+
* @param {String} topic
|
|
2594
|
+
* @param {String|Object|Array|Boolean|Buffer} data
|
|
2595
|
+
*/
|
|
2596
|
+
async publish(topic, data) {
|
|
2597
|
+
// globalSub.publish(topic, data)
|
|
2598
|
+
if (topic instanceof Uint8Array === false) topic = new TextEncoder().encode(topic);
|
|
2599
|
+
if (data instanceof Uint8Array === false) data = new TextEncoder().encode(JSON.stringify(data));
|
|
2600
|
+
const id = Math.random().toString(36).slice(-12);
|
|
2601
|
+
data = new PsMessage({data, topic});
|
|
2602
|
+
for (const peer of this.peers) {
|
|
2603
|
+
if (peer.connection._connected) {
|
|
2604
|
+
if (peer.id.toString() !== this.peerId.toString()) {
|
|
2605
|
+
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2606
|
+
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2607
|
+
}
|
|
2608
|
+
} else {
|
|
2609
|
+
this.removePeer(peer);
|
|
2610
|
+
}
|
|
2611
|
+
// TODO: if peer subscribed
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
createHash(data, name) {
|
|
2616
|
+
return new PeernetHash(data, {name})
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
/**
|
|
2620
|
+
*
|
|
2621
|
+
* @param {String} topic
|
|
2622
|
+
* @param {Method} cb
|
|
2623
|
+
*/
|
|
2624
|
+
async subscribe(topic, cb) {
|
|
2625
|
+
// TODO: if peer subscribed
|
|
2626
|
+
globalSub.subscribe(topic, cb);
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
async removePeer(peer) {
|
|
2630
|
+
connections.delete(peer.id);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
get Buffer() {
|
|
2634
|
+
return Buffer
|
|
2635
|
+
}
|
|
2636
|
+
// async block(index) {
|
|
2637
|
+
// const _values = []
|
|
2638
|
+
// for (const peer of this.peers) {
|
|
2639
|
+
// const value = await peer.request({type: 'block', index})
|
|
2640
|
+
// console.log(value);
|
|
2641
|
+
// }
|
|
2642
|
+
//
|
|
2643
|
+
// }
|
|
2530
2644
|
}
|
|
2531
2645
|
|
|
2532
2646
|
export { Peernet as default };
|