@leofcoin/peernet 1.0.1 → 1.0.3
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/.esdoc.json +10 -10
- package/.eslintrc.json +24 -24
- package/.travis.yml +27 -25
- package/LICENSE +21 -21
- package/README.md +64 -64
- package/deploy.js +8 -8
- package/exports/browser/browser-10ffabe1.js +56 -0
- package/exports/browser/{browser-2f0b2829.js → browser-e1cd4e67.js} +46 -38
- package/exports/browser/{client-95423644.js → client-471408b6.js} +3 -3
- package/exports/browser/dht/dht.d.ts +30 -30
- package/exports/browser/discovery/peer-discovery.d.ts +7 -7
- package/exports/browser/errors/errors.d.ts +3 -3
- package/exports/browser/handlers/data.d.ts +2 -2
- package/exports/browser/handlers/message.d.ts +30 -30
- package/exports/browser/index-601960df.js +58 -0
- package/exports/browser/messages/chat.d.ts +6 -6
- package/exports/browser/messages/data-response.d.ts +6 -6
- package/exports/browser/messages/data.d.ts +10 -10
- package/exports/browser/messages/dht-response.d.ts +6 -6
- package/exports/browser/messages/dht.d.ts +14 -14
- package/exports/browser/messages/file-link.d.ts +10 -10
- package/exports/browser/messages/file.d.ts +10 -10
- package/exports/browser/messages/peer-response.d.ts +6 -6
- package/exports/browser/messages/peer.d.ts +6 -6
- package/exports/browser/messages/peernet.d.ts +6 -6
- package/exports/browser/messages/ps.d.ts +6 -6
- package/exports/browser/messages/request.d.ts +6 -6
- package/exports/browser/messages/response.d.ts +6 -6
- package/exports/browser/{messages-2214fcbb.js → messages-cf68bfb3.js} +153 -153
- package/exports/browser/messages.d.ts +12 -12
- package/exports/browser/peer-info.d.ts +4 -4
- package/exports/browser/{peernet-e74b2091.js → peernet-e1e0a046.js} +13021 -9963
- package/exports/browser/peernet.js +1 -1
- package/exports/browser/prompts/password/browser.d.ts +2 -2
- package/exports/browser/prompts/password/node.d.ts +2 -2
- package/exports/browser/proto/chat-message.proto.d.ts +7 -7
- package/exports/browser/proto/data-response.proto.d.ts +5 -5
- package/exports/browser/proto/data.proto.d.ts +5 -5
- package/exports/browser/proto/dht-response.proto.d.ts +5 -5
- package/exports/browser/proto/dht.proto.d.ts +5 -5
- package/exports/browser/proto/file-link.proto.d.ts +6 -6
- package/exports/browser/proto/file.proto.d.ts +6 -6
- package/exports/browser/proto/peer-response.proto.d.ts +4 -4
- package/exports/browser/proto/peer.proto.d.ts +4 -4
- package/exports/browser/proto/peernet.proto.d.ts +8 -8
- package/exports/browser/proto/ps.proto.d.ts +5 -5
- package/exports/browser/proto/request.proto.d.ts +4 -4
- package/exports/browser/proto/response.proto.d.ts +4 -4
- package/exports/browser/qr-scanner-worker.min-c002e984.js +100 -0
- package/exports/browser/{prompts → src/prompts}/password.js +0 -0
- package/exports/browser/utils/utils.d.ts +7 -7
- package/exports/dht/dht.d.ts +30 -30
- package/exports/discovery/peer-discovery.d.ts +7 -7
- package/exports/errors/errors.d.ts +3 -3
- package/exports/handlers/data.d.ts +2 -2
- package/exports/handlers/message.d.ts +30 -30
- package/exports/messages/chat.d.ts +6 -6
- package/exports/messages/data-response.d.ts +6 -6
- package/exports/messages/data.d.ts +10 -10
- package/exports/messages/dht-response.d.ts +6 -6
- package/exports/messages/dht.d.ts +14 -14
- package/exports/messages/file-link.d.ts +10 -10
- package/exports/messages/file.d.ts +10 -10
- package/exports/messages/peer-response.d.ts +6 -6
- package/exports/messages/peer.d.ts +6 -6
- package/exports/messages/peernet.d.ts +6 -6
- package/exports/messages/ps.d.ts +6 -6
- package/exports/messages/request.d.ts +6 -6
- package/exports/messages/response.d.ts +6 -6
- package/exports/{messages-75fe56a4.js → messages-65501ef9.js} +152 -152
- package/exports/messages.d.ts +12 -12
- package/exports/peer-info.d.ts +4 -4
- package/exports/peernet.js +969 -968
- package/exports/prompts/password/browser.d.ts +2 -2
- package/exports/prompts/password/node.d.ts +2 -2
- package/exports/proto/chat-message.proto.d.ts +7 -7
- package/exports/proto/data-response.proto.d.ts +5 -5
- package/exports/proto/data.proto.d.ts +5 -5
- package/exports/proto/dht-response.proto.d.ts +5 -5
- package/exports/proto/dht.proto.d.ts +5 -5
- package/exports/proto/file-link.proto.d.ts +6 -6
- package/exports/proto/file.proto.d.ts +6 -6
- package/exports/proto/peer-response.proto.d.ts +4 -4
- package/exports/proto/peer.proto.d.ts +4 -4
- package/exports/proto/peernet.proto.d.ts +8 -8
- package/exports/proto/ps.proto.d.ts +5 -5
- package/exports/proto/request.proto.d.ts +4 -4
- package/exports/proto/response.proto.d.ts +4 -4
- package/exports/{prompts → src/prompts}/password.js +3 -3
- package/exports/utils/utils.d.ts +7 -7
- package/index.html +19 -19
- package/package.json +59 -55
- package/rollup.config.js +53 -53
- package/src/dht/dht.js +119 -119
- package/src/discovery/peer-discovery.js +75 -75
- package/src/handlers/data.js +12 -12
- package/src/handlers/message.js +34 -34
- package/src/identity.ts +81 -80
- package/src/messages/chat.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 -14
- package/src/messages/dht.js +22 -22
- package/src/messages/file-link.js +18 -18
- package/src/messages/file.js +18 -18
- package/src/messages/peer-response.js +14 -14
- package/src/messages/peer.js +13 -13
- package/src/messages/peernet.js +14 -14
- package/src/messages/ps.js +13 -13
- package/src/messages/request.js +14 -14
- package/src/messages/response.js +14 -14
- package/src/messages.js +13 -13
- package/src/peer-info.js +9 -9
- package/src/peernet.ts +699 -699
- package/src/prompts/password/node.js +5 -5
- package/src/proto/chat-message.proto.js +6 -6
- package/src/utils/utils.js +78 -78
- package/test/client.js +6 -6
- package/test/codec.js +56 -56
- package/test/hash.js +13 -13
- package/test/index.js +3 -3
- package/test/lastBlock.js +7 -7
- package/test/messages.js +26 -26
- package/test/peernet.js +17 -17
- package/test.js +47 -47
- package/test2.js +9 -9
- package/test3.js +15 -15
- package/test4.js +7 -7
- package/tsconfig.json +12 -12
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/codec-format-interface.js.html +0 -637
- package/coverage/lcov-report/dht-response.js.html +0 -193
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov.info +0 -199
- package/exports/browser/browser-3c1d0477.js +0 -73
- package/exports/browser/index-3699fac4.js +0 -34
package/exports/peernet.js
CHANGED
|
@@ -4,989 +4,990 @@ import { Codec } from '@leofcoin/codec-format-interface';
|
|
|
4
4
|
import LeofcoinStorage from '@leofcoin/storage';
|
|
5
5
|
import { utils } from '@leofcoin/codecs';
|
|
6
6
|
import MultiWallet from '@leofcoin/multi-wallet';
|
|
7
|
+
import base58 from '@vandeurenglenn/base58';
|
|
8
|
+
import { decrypt, encrypt } from '@leofcoin/identity-utils';
|
|
9
|
+
import QrScanner from 'qr-scanner';
|
|
10
|
+
import qrcode from 'qrcode';
|
|
7
11
|
|
|
8
|
-
const BufferToUint8Array = data => {
|
|
9
|
-
if (data.type === 'Buffer') {
|
|
10
|
-
data = new Uint8Array(data.data);
|
|
11
|
-
}
|
|
12
|
-
return data;
|
|
13
|
-
};
|
|
14
|
-
const protoFor = (message) => {
|
|
15
|
-
const codec = new Codec(message);
|
|
16
|
-
if (!codec.name)
|
|
17
|
-
throw new Error('proto not found');
|
|
18
|
-
const Proto = globalThis.peernet.protos[codec.name];
|
|
19
|
-
if (!Proto)
|
|
20
|
-
throw (new Error(`No proto defined for ${codec.name}`));
|
|
21
|
-
return new Proto(message);
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* wether or not a peernet daemon is active
|
|
25
|
-
* @return {Boolean}
|
|
26
|
-
*/
|
|
27
|
-
const hasDaemon = async () => {
|
|
28
|
-
try {
|
|
29
|
-
let response = await fetch('http://127.0.0.1:1000/api/version');
|
|
30
|
-
response = await response.json();
|
|
31
|
-
return Boolean(response.client === '@peernet/api/http');
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const https = () => {
|
|
38
|
-
if (!globalThis.location)
|
|
39
|
-
return false;
|
|
40
|
-
return Boolean(globalThis.location.protocol === 'https:');
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Get current environment
|
|
44
|
-
* @return {String} current environment [node, electron, browser]
|
|
45
|
-
*/
|
|
46
|
-
const environment = () => {
|
|
47
|
-
const _navigator = globalThis.navigator;
|
|
48
|
-
if (!_navigator) {
|
|
49
|
-
return 'node';
|
|
50
|
-
}
|
|
51
|
-
else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
52
|
-
return 'electron';
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
return 'browser';
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
/**
|
|
59
|
-
* * Get current environment
|
|
60
|
-
* @return {Object} result
|
|
61
|
-
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
62
|
-
* @property {Boolean} reult.environment Current environment
|
|
63
|
-
*/
|
|
64
|
-
const target = async () => {
|
|
65
|
-
let daemon = false;
|
|
66
|
-
if (!https())
|
|
67
|
-
daemon = await hasDaemon();
|
|
68
|
-
return { daemon, environment: environment() };
|
|
12
|
+
const BufferToUint8Array = data => {
|
|
13
|
+
if (data.type === 'Buffer') {
|
|
14
|
+
data = new Uint8Array(data.data);
|
|
15
|
+
}
|
|
16
|
+
return data;
|
|
17
|
+
};
|
|
18
|
+
const protoFor = (message) => {
|
|
19
|
+
const codec = new Codec(message);
|
|
20
|
+
if (!codec.name)
|
|
21
|
+
throw new Error('proto not found');
|
|
22
|
+
const Proto = globalThis.peernet.protos[codec.name];
|
|
23
|
+
if (!Proto)
|
|
24
|
+
throw (new Error(`No proto defined for ${codec.name}`));
|
|
25
|
+
return new Proto(message);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* wether or not a peernet daemon is active
|
|
29
|
+
* @return {Boolean}
|
|
30
|
+
*/
|
|
31
|
+
const hasDaemon = async () => {
|
|
32
|
+
try {
|
|
33
|
+
let response = await fetch('http://127.0.0.1:1000/api/version');
|
|
34
|
+
response = await response.json();
|
|
35
|
+
return Boolean(response.client === '@peernet/api/http');
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const https = () => {
|
|
42
|
+
if (!globalThis.location)
|
|
43
|
+
return false;
|
|
44
|
+
return Boolean(globalThis.location.protocol === 'https:');
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Get current environment
|
|
48
|
+
* @return {String} current environment [node, electron, browser]
|
|
49
|
+
*/
|
|
50
|
+
const environment = () => {
|
|
51
|
+
const _navigator = globalThis.navigator;
|
|
52
|
+
if (!_navigator) {
|
|
53
|
+
return 'node';
|
|
54
|
+
}
|
|
55
|
+
else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
56
|
+
return 'electron';
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
return 'browser';
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* * Get current environment
|
|
64
|
+
* @return {Object} result
|
|
65
|
+
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
66
|
+
* @property {Boolean} reult.environment Current environment
|
|
67
|
+
*/
|
|
68
|
+
const target = async () => {
|
|
69
|
+
let daemon = false;
|
|
70
|
+
if (!https())
|
|
71
|
+
daemon = await hasDaemon();
|
|
72
|
+
return { daemon, environment: environment() };
|
|
69
73
|
};
|
|
70
74
|
|
|
71
|
-
class PeerDiscovery {
|
|
72
|
-
constructor(id) {
|
|
73
|
-
this.id = id;
|
|
74
|
-
}
|
|
75
|
-
_getPeerId(id) {
|
|
76
|
-
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0)
|
|
77
|
-
return false;
|
|
78
|
-
for (const entry of [...peernet.peerMap.entries()]) {
|
|
79
|
-
for (const _id of entry[1]) {
|
|
80
|
-
if (_id === id)
|
|
81
|
-
return entry[0];
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
async discover(peer) {
|
|
86
|
-
let id = this._getPeerId(peer.id);
|
|
87
|
-
if (id)
|
|
88
|
-
return id;
|
|
89
|
-
const data = await new peernet.protos['peernet-peer']({ id: this.id });
|
|
90
|
-
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
91
|
-
let response = await peer.request(node.encoded);
|
|
92
|
-
response = await protoFor(response);
|
|
93
|
-
response = await new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
94
|
-
id = response.decoded.id;
|
|
95
|
-
if (id === this.id)
|
|
96
|
-
return;
|
|
97
|
-
if (!peernet.peerMap.has(id))
|
|
98
|
-
peernet.peerMap.set(id, [peer.id]);
|
|
99
|
-
else {
|
|
100
|
-
const connections = peernet.peerMap.get(id);
|
|
101
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
102
|
-
connections.push(peer.id);
|
|
103
|
-
peernet.peerMap.set(peer.id, connections);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return id;
|
|
107
|
-
}
|
|
108
|
-
async discoverHandler(message, peer) {
|
|
109
|
-
const { id, proto } = message;
|
|
110
|
-
// if (typeof message.data === 'string') message.data = Buffer.from(message.data)
|
|
111
|
-
if (proto.name === 'peernet-peer') {
|
|
112
|
-
const from = proto.decoded.id;
|
|
113
|
-
if (from === this.id)
|
|
114
|
-
return;
|
|
115
|
-
if (!peernet.peerMap.has(from))
|
|
116
|
-
peernet.peerMap.set(from, [peer.id]);
|
|
117
|
-
else {
|
|
118
|
-
const connections = peernet.peerMap.get(from);
|
|
119
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
120
|
-
connections.push(peer.id);
|
|
121
|
-
peernet.peerMap.set(from, connections);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const data = await new peernet.protos['peernet-peer-response']({ id: this.id });
|
|
125
|
-
const node = await peernet.prepareMessage(from, data.encoded);
|
|
126
|
-
peer.write(Buffer.from(JSON.stringify({ id, data: node.encoded })));
|
|
127
|
-
}
|
|
128
|
-
else if (proto.name === 'peernet-peer-response') {
|
|
129
|
-
const from = proto.decoded.id;
|
|
130
|
-
if (from === this.id)
|
|
131
|
-
return;
|
|
132
|
-
if (!peernet.peerMap.has(from))
|
|
133
|
-
peernet.peerMap.set(from, [peer.id]);
|
|
134
|
-
else {
|
|
135
|
-
const connections = peernet.peerMap.get(from);
|
|
136
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
137
|
-
connections.push(peer.id);
|
|
138
|
-
peernet.peerMap.set(from, connections);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
75
|
+
class PeerDiscovery {
|
|
76
|
+
constructor(id) {
|
|
77
|
+
this.id = id;
|
|
78
|
+
}
|
|
79
|
+
_getPeerId(id) {
|
|
80
|
+
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0)
|
|
81
|
+
return false;
|
|
82
|
+
for (const entry of [...peernet.peerMap.entries()]) {
|
|
83
|
+
for (const _id of entry[1]) {
|
|
84
|
+
if (_id === id)
|
|
85
|
+
return entry[0];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async discover(peer) {
|
|
90
|
+
let id = this._getPeerId(peer.id);
|
|
91
|
+
if (id)
|
|
92
|
+
return id;
|
|
93
|
+
const data = await new peernet.protos['peernet-peer']({ id: this.id });
|
|
94
|
+
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
95
|
+
let response = await peer.request(node.encoded);
|
|
96
|
+
response = await protoFor(response);
|
|
97
|
+
response = await new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
98
|
+
id = response.decoded.id;
|
|
99
|
+
if (id === this.id)
|
|
100
|
+
return;
|
|
101
|
+
if (!peernet.peerMap.has(id))
|
|
102
|
+
peernet.peerMap.set(id, [peer.id]);
|
|
103
|
+
else {
|
|
104
|
+
const connections = peernet.peerMap.get(id);
|
|
105
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
106
|
+
connections.push(peer.id);
|
|
107
|
+
peernet.peerMap.set(peer.id, connections);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return id;
|
|
111
|
+
}
|
|
112
|
+
async discoverHandler(message, peer) {
|
|
113
|
+
const { id, proto } = message;
|
|
114
|
+
// if (typeof message.data === 'string') message.data = Buffer.from(message.data)
|
|
115
|
+
if (proto.name === 'peernet-peer') {
|
|
116
|
+
const from = proto.decoded.id;
|
|
117
|
+
if (from === this.id)
|
|
118
|
+
return;
|
|
119
|
+
if (!peernet.peerMap.has(from))
|
|
120
|
+
peernet.peerMap.set(from, [peer.id]);
|
|
121
|
+
else {
|
|
122
|
+
const connections = peernet.peerMap.get(from);
|
|
123
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
124
|
+
connections.push(peer.id);
|
|
125
|
+
peernet.peerMap.set(from, connections);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const data = await new peernet.protos['peernet-peer-response']({ id: this.id });
|
|
129
|
+
const node = await peernet.prepareMessage(from, data.encoded);
|
|
130
|
+
peer.write(Buffer.from(JSON.stringify({ id, data: node.encoded })));
|
|
131
|
+
}
|
|
132
|
+
else if (proto.name === 'peernet-peer-response') {
|
|
133
|
+
const from = proto.decoded.id;
|
|
134
|
+
if (from === this.id)
|
|
135
|
+
return;
|
|
136
|
+
if (!peernet.peerMap.has(from))
|
|
137
|
+
peernet.peerMap.set(from, [peer.id]);
|
|
138
|
+
else {
|
|
139
|
+
const connections = peernet.peerMap.get(from);
|
|
140
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
141
|
+
connections.push(peer.id);
|
|
142
|
+
peernet.peerMap.set(from, connections);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
/**
|
|
146
|
-
* Keep history of fetched address and ptr
|
|
147
|
-
* @property {Object} address
|
|
148
|
-
* @property {Object} ptr
|
|
149
|
-
*/
|
|
150
|
-
const lastFetched = {
|
|
151
|
-
address: {
|
|
152
|
-
value: undefined,
|
|
153
|
-
timestamp: 0,
|
|
154
|
-
},
|
|
155
|
-
ptr: {
|
|
156
|
-
value: undefined,
|
|
157
|
-
timestamp: 0,
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
const getAddress = async () => {
|
|
161
|
-
const { address } = lastFetched;
|
|
162
|
-
const now = Math.round(new Date().getTime() / 1000);
|
|
163
|
-
if (now - address.timestamp > 1200000) {
|
|
164
|
-
address.value = await fetch('https://icanhazip.com/');
|
|
165
|
-
address.value = await address.value.text();
|
|
166
|
-
address.timestamp = Math.round(new Date().getTime() / 1000);
|
|
167
|
-
lastFetched.address = address;
|
|
168
|
-
}
|
|
169
|
-
return address.value;
|
|
170
|
-
};
|
|
171
|
-
const degreesToRadians = (degrees) => {
|
|
172
|
-
return degrees * Math.PI / 180;
|
|
173
|
-
};
|
|
174
|
-
const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
|
|
175
|
-
const earthRadiusKm = 6371;
|
|
176
|
-
const dLat = degreesToRadians(lat2 - lat1);
|
|
177
|
-
const dLon = degreesToRadians(lon2 - lon1);
|
|
178
|
-
lat1 = degreesToRadians(lat1);
|
|
179
|
-
lat2 = degreesToRadians(lat2);
|
|
180
|
-
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
181
|
-
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
|
|
182
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
183
|
-
return earthRadiusKm * c;
|
|
184
|
-
};
|
|
185
|
-
class DhtEarth {
|
|
186
|
-
/**
|
|
187
|
-
*
|
|
188
|
-
*/
|
|
189
|
-
constructor() {
|
|
190
|
-
this.providerMap = new Map();
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* @param {Object} address
|
|
194
|
-
* @return {Object} {latitude: lat, longitude: lon}
|
|
195
|
-
*/
|
|
196
|
-
async getCoordinates(address) {
|
|
197
|
-
// const {address} = parseAddress(provider)
|
|
198
|
-
const request = `https://whereis.leofcoin.org/?ip=${address}`;
|
|
199
|
-
let response = await fetch(request);
|
|
200
|
-
response = await response.json();
|
|
201
|
-
const { lat, lon } = response;
|
|
202
|
-
return { latitude: lat, longitude: lon };
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* @param {Object} peer
|
|
206
|
-
* @param {Object} provider
|
|
207
|
-
* @return {Object} {provider, distance}
|
|
208
|
-
*/
|
|
209
|
-
async getDistance(peer, provider) {
|
|
210
|
-
const { latitude, longitude } = await this.getCoordinates(provider.address);
|
|
211
|
-
return { provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude) };
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* @param {Array} providers
|
|
215
|
-
* @return {Object} closestPeer
|
|
216
|
-
*/
|
|
217
|
-
async closestPeer(providers) {
|
|
218
|
-
let all = [];
|
|
219
|
-
const address = await getAddress();
|
|
220
|
-
const peerLoc = await this.getCoordinates(address);
|
|
221
|
-
for (const provider of providers) {
|
|
222
|
-
if (provider.address === '127.0.0.1')
|
|
223
|
-
all.push({ provider, distance: 0 });
|
|
224
|
-
else
|
|
225
|
-
all.push(this.getDistance(peerLoc, provider));
|
|
226
|
-
}
|
|
227
|
-
all = await Promise.all(all);
|
|
228
|
-
all = all.sort((previous, current) => previous.distance - current.distance);
|
|
229
|
-
return all[0].provider;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* @param {String} hash
|
|
233
|
-
* @return {Array} providers
|
|
234
|
-
*/
|
|
235
|
-
providersFor(hash) {
|
|
236
|
-
return this.providerMap.get(hash);
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* @param {String} address
|
|
240
|
-
* @param {String} hash
|
|
241
|
-
* @return {Array} providers
|
|
242
|
-
*/
|
|
243
|
-
async addProvider(address, hash) {
|
|
244
|
-
let providers = [];
|
|
245
|
-
if (this.providerMap.has(hash))
|
|
246
|
-
providers = this.providerMap.get(hash);
|
|
247
|
-
providers = new Set([...providers, address]);
|
|
248
|
-
this.providerMap.set(hash, providers);
|
|
249
|
-
return providers;
|
|
250
|
-
}
|
|
149
|
+
/**
|
|
150
|
+
* Keep history of fetched address and ptr
|
|
151
|
+
* @property {Object} address
|
|
152
|
+
* @property {Object} ptr
|
|
153
|
+
*/
|
|
154
|
+
const lastFetched = {
|
|
155
|
+
address: {
|
|
156
|
+
value: undefined,
|
|
157
|
+
timestamp: 0,
|
|
158
|
+
},
|
|
159
|
+
ptr: {
|
|
160
|
+
value: undefined,
|
|
161
|
+
timestamp: 0,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const getAddress = async () => {
|
|
165
|
+
const { address } = lastFetched;
|
|
166
|
+
const now = Math.round(new Date().getTime() / 1000);
|
|
167
|
+
if (now - address.timestamp > 1200000) {
|
|
168
|
+
address.value = await fetch('https://icanhazip.com/');
|
|
169
|
+
address.value = await address.value.text();
|
|
170
|
+
address.timestamp = Math.round(new Date().getTime() / 1000);
|
|
171
|
+
lastFetched.address = address;
|
|
172
|
+
}
|
|
173
|
+
return address.value;
|
|
174
|
+
};
|
|
175
|
+
const degreesToRadians = (degrees) => {
|
|
176
|
+
return degrees * Math.PI / 180;
|
|
177
|
+
};
|
|
178
|
+
const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
|
|
179
|
+
const earthRadiusKm = 6371;
|
|
180
|
+
const dLat = degreesToRadians(lat2 - lat1);
|
|
181
|
+
const dLon = degreesToRadians(lon2 - lon1);
|
|
182
|
+
lat1 = degreesToRadians(lat1);
|
|
183
|
+
lat2 = degreesToRadians(lat2);
|
|
184
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
185
|
+
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
|
|
186
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
187
|
+
return earthRadiusKm * c;
|
|
188
|
+
};
|
|
189
|
+
class DhtEarth {
|
|
190
|
+
/**
|
|
191
|
+
*
|
|
192
|
+
*/
|
|
193
|
+
constructor() {
|
|
194
|
+
this.providerMap = new Map();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* @param {Object} address
|
|
198
|
+
* @return {Object} {latitude: lat, longitude: lon}
|
|
199
|
+
*/
|
|
200
|
+
async getCoordinates(address) {
|
|
201
|
+
// const {address} = parseAddress(provider)
|
|
202
|
+
const request = `https://whereis.leofcoin.org/?ip=${address}`;
|
|
203
|
+
let response = await fetch(request);
|
|
204
|
+
response = await response.json();
|
|
205
|
+
const { lat, lon } = response;
|
|
206
|
+
return { latitude: lat, longitude: lon };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* @param {Object} peer
|
|
210
|
+
* @param {Object} provider
|
|
211
|
+
* @return {Object} {provider, distance}
|
|
212
|
+
*/
|
|
213
|
+
async getDistance(peer, provider) {
|
|
214
|
+
const { latitude, longitude } = await this.getCoordinates(provider.address);
|
|
215
|
+
return { provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude) };
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* @param {Array} providers
|
|
219
|
+
* @return {Object} closestPeer
|
|
220
|
+
*/
|
|
221
|
+
async closestPeer(providers) {
|
|
222
|
+
let all = [];
|
|
223
|
+
const address = await getAddress();
|
|
224
|
+
const peerLoc = await this.getCoordinates(address);
|
|
225
|
+
for (const provider of providers) {
|
|
226
|
+
if (provider.address === '127.0.0.1')
|
|
227
|
+
all.push({ provider, distance: 0 });
|
|
228
|
+
else
|
|
229
|
+
all.push(this.getDistance(peerLoc, provider));
|
|
230
|
+
}
|
|
231
|
+
all = await Promise.all(all);
|
|
232
|
+
all = all.sort((previous, current) => previous.distance - current.distance);
|
|
233
|
+
return all[0].provider;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* @param {String} hash
|
|
237
|
+
* @return {Array} providers
|
|
238
|
+
*/
|
|
239
|
+
providersFor(hash) {
|
|
240
|
+
return this.providerMap.get(hash);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* @param {String} address
|
|
244
|
+
* @param {String} hash
|
|
245
|
+
* @return {Array} providers
|
|
246
|
+
*/
|
|
247
|
+
async addProvider(address, hash) {
|
|
248
|
+
let providers = [];
|
|
249
|
+
if (this.providerMap.has(hash))
|
|
250
|
+
providers = this.providerMap.get(hash);
|
|
251
|
+
providers = new Set([...providers, address]);
|
|
252
|
+
this.providerMap.set(hash, providers);
|
|
253
|
+
return providers;
|
|
254
|
+
}
|
|
251
255
|
}
|
|
252
256
|
|
|
253
|
-
class MessageHandler {
|
|
254
|
-
constructor(network) {
|
|
255
|
-
this.network = network;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* hash and sign message
|
|
259
|
-
*
|
|
260
|
-
* @param {object} message
|
|
261
|
-
* @param {Buffer} message.from peer id
|
|
262
|
-
* @param {Buffer} message.to peer id
|
|
263
|
-
* @param {string} message.data Peernet message
|
|
264
|
-
* (PeernetMessage excluded) encoded as a string
|
|
265
|
-
* @return message
|
|
266
|
-
*/
|
|
267
|
-
async hashAndSignMessage(message) {
|
|
268
|
-
const hash = await message.peernetHash;
|
|
269
|
-
message.decoded.signature = globalThis.identity.sign(hash.buffer);
|
|
270
|
-
return message;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* @param {String} from - peer id
|
|
274
|
-
* @param {String} to - peer id
|
|
275
|
-
* @param {String|PeernetMessage} data - data encoded message string
|
|
276
|
-
* or the messageNode itself
|
|
277
|
-
*/
|
|
278
|
-
async prepareMessage(message) {
|
|
279
|
-
if (message.keys.includes('signature')) {
|
|
280
|
-
message = await this.hashAndSignMessage(message);
|
|
281
|
-
}
|
|
282
|
-
return message;
|
|
283
|
-
}
|
|
257
|
+
class MessageHandler {
|
|
258
|
+
constructor(network) {
|
|
259
|
+
this.network = network;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* hash and sign message
|
|
263
|
+
*
|
|
264
|
+
* @param {object} message
|
|
265
|
+
* @param {Buffer} message.from peer id
|
|
266
|
+
* @param {Buffer} message.to peer id
|
|
267
|
+
* @param {string} message.data Peernet message
|
|
268
|
+
* (PeernetMessage excluded) encoded as a string
|
|
269
|
+
* @return message
|
|
270
|
+
*/
|
|
271
|
+
async hashAndSignMessage(message) {
|
|
272
|
+
const hash = await message.peernetHash;
|
|
273
|
+
message.decoded.signature = globalThis.identity.sign(hash.buffer);
|
|
274
|
+
return message;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @param {String} from - peer id
|
|
278
|
+
* @param {String} to - peer id
|
|
279
|
+
* @param {String|PeernetMessage} data - data encoded message string
|
|
280
|
+
* or the messageNode itself
|
|
281
|
+
*/
|
|
282
|
+
async prepareMessage(message) {
|
|
283
|
+
if (message.keys.includes('signature')) {
|
|
284
|
+
message = await this.hashAndSignMessage(message);
|
|
285
|
+
}
|
|
286
|
+
return message;
|
|
287
|
+
}
|
|
284
288
|
}
|
|
285
289
|
|
|
286
|
-
const dataHandler = async (message) => {
|
|
287
|
-
if (!message)
|
|
288
|
-
return;
|
|
289
|
-
const { data, id, from } = message;
|
|
290
|
-
const proto = await protoFor(data);
|
|
291
|
-
peernet._protoHandler({ id, proto }, peernet.client.connections[from], from);
|
|
290
|
+
const dataHandler = async (message) => {
|
|
291
|
+
if (!message)
|
|
292
|
+
return;
|
|
293
|
+
const { data, id, from } = message;
|
|
294
|
+
const proto = await protoFor(data);
|
|
295
|
+
peernet._protoHandler({ id, proto }, peernet.client.connections[from], from);
|
|
292
296
|
};
|
|
293
297
|
|
|
294
|
-
const dhtError = (proto) => {
|
|
295
|
-
const text = `Received proto ${proto.name} expected peernet-dht-response`;
|
|
296
|
-
return new Error(`Routing error: ${text}`);
|
|
297
|
-
};
|
|
298
|
-
const nothingFoundError = (hash) => {
|
|
299
|
-
return new Error(`nothing found for ${hash}`);
|
|
298
|
+
const dhtError = (proto) => {
|
|
299
|
+
const text = `Received proto ${proto.name} expected peernet-dht-response`;
|
|
300
|
+
return new Error(`Routing error: ${text}`);
|
|
301
|
+
};
|
|
302
|
+
const nothingFoundError = (hash) => {
|
|
303
|
+
return new Error(`nothing found for ${hash}`);
|
|
300
304
|
};
|
|
301
305
|
|
|
302
|
-
class Identity {
|
|
303
|
-
#wallet;
|
|
304
|
-
network;
|
|
305
|
-
id;
|
|
306
|
-
constructor(network) {
|
|
307
|
-
this.network = network;
|
|
308
|
-
}
|
|
309
|
-
get accounts() {
|
|
310
|
-
return this.getAccounts();
|
|
311
|
-
}
|
|
312
|
-
async getAccounts() {
|
|
313
|
-
let accounts = await globalThis.walletStore.get('accounts');
|
|
314
|
-
accounts = new TextDecoder().decode(accounts);
|
|
315
|
-
return JSON.parse(accounts);
|
|
316
|
-
}
|
|
317
|
-
async load(password) {
|
|
318
|
-
if (!password) {
|
|
319
|
-
const importee = await import('./src/prompts/password.js');
|
|
320
|
-
password = await importee.default();
|
|
321
|
-
}
|
|
322
|
-
const accountExists = await globalThis.accountStore.has('public');
|
|
323
|
-
if (accountExists) {
|
|
324
|
-
const pub = await globalThis.accountStore.get('public');
|
|
325
|
-
this.id = JSON.parse(new TextDecoder().decode(pub)).walletId;
|
|
326
|
-
const selected = await globalThis.walletStore.get('selected-account');
|
|
327
|
-
globalThis.peernet.selectedAccount = new TextDecoder().decode(selected);
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
const importee = await import(/* webpackChunkName: "generate-account" */ '@leofcoin/generate-account');
|
|
331
|
-
const { identity, accounts } = await importee.default(password, this.network);
|
|
332
|
-
await globalThis.accountStore.put('public', JSON.stringify({ walletId: identity.walletId }));
|
|
333
|
-
await globalThis.walletStore.put('version', String(1));
|
|
334
|
-
await globalThis.walletStore.put('accounts', JSON.stringify(accounts));
|
|
335
|
-
await globalThis.walletStore.put('selected-account', accounts[0][1]);
|
|
336
|
-
await globalThis.walletStore.put('identity', JSON.stringify(identity));
|
|
337
|
-
globalThis.peernet.selectedAccount = accounts[0][1];
|
|
338
|
-
this.id = identity.walletId;
|
|
339
|
-
}
|
|
340
|
-
const identity = JSON.parse(new TextDecoder().decode(await globalThis.walletStore.get('identity')));
|
|
341
|
-
this.#wallet = new MultiWallet(this.network);
|
|
342
|
-
await
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
this.#wallet
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
*/
|
|
367
|
-
async lock(password, multiWIF) {
|
|
368
|
-
}
|
|
306
|
+
class Identity {
|
|
307
|
+
#wallet;
|
|
308
|
+
network;
|
|
309
|
+
id;
|
|
310
|
+
constructor(network) {
|
|
311
|
+
this.network = network;
|
|
312
|
+
}
|
|
313
|
+
get accounts() {
|
|
314
|
+
return this.getAccounts();
|
|
315
|
+
}
|
|
316
|
+
async getAccounts() {
|
|
317
|
+
let accounts = await globalThis.walletStore.get('accounts');
|
|
318
|
+
accounts = new TextDecoder().decode(accounts);
|
|
319
|
+
return JSON.parse(accounts);
|
|
320
|
+
}
|
|
321
|
+
async load(password) {
|
|
322
|
+
if (!password) {
|
|
323
|
+
const importee = await import('./src/prompts/password.js');
|
|
324
|
+
password = await importee.default();
|
|
325
|
+
}
|
|
326
|
+
const accountExists = await globalThis.accountStore.has('public');
|
|
327
|
+
if (accountExists) {
|
|
328
|
+
const pub = await globalThis.accountStore.get('public');
|
|
329
|
+
this.id = JSON.parse(new TextDecoder().decode(pub)).walletId;
|
|
330
|
+
const selected = await globalThis.walletStore.get('selected-account');
|
|
331
|
+
globalThis.peernet.selectedAccount = new TextDecoder().decode(selected);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
const importee = await import(/* webpackChunkName: "generate-account" */ '@leofcoin/generate-account');
|
|
335
|
+
const { identity, accounts } = await importee.default(password, this.network);
|
|
336
|
+
await globalThis.accountStore.put('public', JSON.stringify({ walletId: identity.walletId }));
|
|
337
|
+
await globalThis.walletStore.put('version', String(1));
|
|
338
|
+
await globalThis.walletStore.put('accounts', JSON.stringify(accounts));
|
|
339
|
+
await globalThis.walletStore.put('selected-account', accounts[0][1]);
|
|
340
|
+
await globalThis.walletStore.put('identity', JSON.stringify(identity));
|
|
341
|
+
globalThis.peernet.selectedAccount = accounts[0][1];
|
|
342
|
+
this.id = identity.walletId;
|
|
343
|
+
}
|
|
344
|
+
const identity = JSON.parse(new TextDecoder().decode(await globalThis.walletStore.get('identity')));
|
|
345
|
+
this.#wallet = new MultiWallet(this.network);
|
|
346
|
+
const multiWIF = await decrypt(password, base58.decode(identity.multiWIF));
|
|
347
|
+
await this.#wallet.fromMultiWif(multiWIF);
|
|
348
|
+
}
|
|
349
|
+
sign(hash) {
|
|
350
|
+
return this.#wallet.sign(hash.subarray(0, 32));
|
|
351
|
+
}
|
|
352
|
+
async export(password) {
|
|
353
|
+
const multiWIF = this.#wallet.toMultiWif();
|
|
354
|
+
const encypted = await encrypt(password, multiWIF);
|
|
355
|
+
return base58.encode(encypted);
|
|
356
|
+
}
|
|
357
|
+
async import(password, encrypted) {
|
|
358
|
+
this.#wallet = new MultiWallet(this.network);
|
|
359
|
+
const decrypted = await decrypt(password, base58.decode(encrypted));
|
|
360
|
+
await this.#wallet.fromMultiWif(decrypted);
|
|
361
|
+
}
|
|
362
|
+
async exportQR(password) {
|
|
363
|
+
const exported = await this.export(password);
|
|
364
|
+
return globalThis.navigator ? await qrcode.toDataURL(exported) : await qrcode.toString(exported, { type: 'terminal' });
|
|
365
|
+
}
|
|
366
|
+
async importQR(image, password) {
|
|
367
|
+
const multiWIF = QrScanner.scanImage(image);
|
|
368
|
+
return this.import(password, multiWIF);
|
|
369
|
+
}
|
|
369
370
|
}
|
|
370
371
|
|
|
371
|
-
globalThis.LeofcoinStorage = LeofcoinStorage;
|
|
372
|
-
globalThis.leofcoin = globalThis.leofcoin || {};
|
|
373
|
-
globalThis.pubsub = globalThis.pubsub || new PubSub();
|
|
374
|
-
globalThis.globalSub = globalThis.globalSub || new PubSub(true);
|
|
375
|
-
/**
|
|
376
|
-
* @access public
|
|
377
|
-
* @example
|
|
378
|
-
* const peernet = new Peernet();
|
|
379
|
-
*/
|
|
380
|
-
class Peernet {
|
|
381
|
-
identity;
|
|
382
|
-
stores = [];
|
|
383
|
-
/**
|
|
384
|
-
* @type {Object}
|
|
385
|
-
* @property {Object} peer Instance of Peer
|
|
386
|
-
*/
|
|
387
|
-
dht = new DhtEarth();
|
|
388
|
-
/** @leofcoin/peernet-swarm/client */
|
|
389
|
-
client;
|
|
390
|
-
network;
|
|
391
|
-
stars;
|
|
392
|
-
networkVersion;
|
|
393
|
-
bw;
|
|
394
|
-
/**
|
|
395
|
-
* @access public
|
|
396
|
-
* @param {Object} options
|
|
397
|
-
* @param {String} options.network - desired network
|
|
398
|
-
* @param {String} options.stars - star list for selected network (these should match, don't mix networks)
|
|
399
|
-
* @param {String} options.root - path to root directory
|
|
400
|
-
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
401
|
-
*
|
|
402
|
-
* @return {Promise} instance of Peernet
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
406
|
-
*/
|
|
407
|
-
constructor(options, password) {
|
|
408
|
-
/**
|
|
409
|
-
* @property {String} network - current network
|
|
410
|
-
*/
|
|
411
|
-
this.network = options.network || 'leofcoin';
|
|
412
|
-
this.stars = options.stars;
|
|
413
|
-
const parts = this.network.split(':');
|
|
414
|
-
this.networkVersion = options.networkVersion || parts.length > 1 ? parts[1] : 'mainnet';
|
|
415
|
-
if (!options.storePrefix)
|
|
416
|
-
options.storePrefix = 'lfc';
|
|
417
|
-
if (!options.port)
|
|
418
|
-
options.port = 2000;
|
|
419
|
-
if (!options.root) {
|
|
420
|
-
parts[1] ? options.root = `.${parts[0]}/${parts[1]}` : options.root = `.${this.network}`;
|
|
421
|
-
}
|
|
422
|
-
globalThis.peernet = this;
|
|
423
|
-
this.bw = {
|
|
424
|
-
up: 0,
|
|
425
|
-
down: 0,
|
|
426
|
-
};
|
|
427
|
-
return this._init(options, password);
|
|
428
|
-
}
|
|
429
|
-
get id() {
|
|
430
|
-
return this.identity.id;
|
|
431
|
-
}
|
|
432
|
-
get accounts() {
|
|
433
|
-
return this.identity.accounts;
|
|
434
|
-
}
|
|
435
|
-
get defaultStores() {
|
|
436
|
-
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message'];
|
|
437
|
-
}
|
|
438
|
-
addProto(name, proto) {
|
|
439
|
-
if (!globalThis.peernet.protos[name])
|
|
440
|
-
globalThis.peernet.protos[name] = proto;
|
|
441
|
-
}
|
|
442
|
-
addCodec(codec) {
|
|
443
|
-
return utils.addCodec(codec);
|
|
444
|
-
}
|
|
445
|
-
async addStore(name, prefix, root, isPrivate = true) {
|
|
446
|
-
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
447
|
-
name === 'data' || name === 'message')
|
|
448
|
-
isPrivate = false;
|
|
449
|
-
let Storage;
|
|
450
|
-
this.hasDaemon ? Storage = LeofcoinStorageClient : Storage = LeofcoinStorage;
|
|
451
|
-
if (!globalThis[`${name}Store`]) {
|
|
452
|
-
globalThis[`${name}Store`] = new Storage(name, root);
|
|
453
|
-
await globalThis[`${name}Store`].init();
|
|
454
|
-
}
|
|
455
|
-
globalThis[`${name}Store`].private = isPrivate;
|
|
456
|
-
if (!isPrivate)
|
|
457
|
-
this.stores.push(name);
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* @see MessageHandler
|
|
461
|
-
*/
|
|
462
|
-
prepareMessage(data) {
|
|
463
|
-
return this._messageHandler.prepareMessage(data);
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* @access public
|
|
467
|
-
*
|
|
468
|
-
* @return {Array} peerId
|
|
469
|
-
*/
|
|
470
|
-
get peers() {
|
|
471
|
-
return Object.keys(this.client.connections);
|
|
472
|
-
}
|
|
473
|
-
get connections() {
|
|
474
|
-
return Object.values(this.client.connections);
|
|
475
|
-
}
|
|
476
|
-
get peerEntries() {
|
|
477
|
-
return Object.entries(this.client.connections);
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* @return {String} id - peerId
|
|
481
|
-
*/
|
|
482
|
-
getConnection(id) {
|
|
483
|
-
return this.client.connections[id];
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* @private
|
|
487
|
-
*
|
|
488
|
-
* @param {Object} options
|
|
489
|
-
* @param {String} options.root - path to root directory
|
|
490
|
-
*
|
|
491
|
-
* @return {Promise} instance of Peernet
|
|
492
|
-
*/
|
|
493
|
-
async _init(options, password) {
|
|
494
|
-
this.requestProtos = {};
|
|
495
|
-
this.storePrefix = options.storePrefix;
|
|
496
|
-
this.root = options.root;
|
|
497
|
-
const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile
|
|
498
|
-
// FolderMessageResponse
|
|
499
|
-
} = await import(/* webpackChunkName: "messages" */ './messages-
|
|
500
|
-
/**
|
|
501
|
-
* proto Object containing protos
|
|
502
|
-
* @type {Object}
|
|
503
|
-
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
504
|
-
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
505
|
-
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
506
|
-
* @property {DataMessage} protos[peernet-data] messageNode
|
|
507
|
-
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
508
|
-
*/
|
|
509
|
-
globalThis.peernet.protos = {
|
|
510
|
-
'peernet-request': RequestMessage,
|
|
511
|
-
'peernet-response': ResponseMessage,
|
|
512
|
-
'peernet-peer': PeerMessage,
|
|
513
|
-
'peernet-peer-response': PeerMessageResponse,
|
|
514
|
-
'peernet-message': PeernetMessage,
|
|
515
|
-
'peernet-dht': DHTMessage,
|
|
516
|
-
'peernet-dht-response': DHTMessageResponse,
|
|
517
|
-
'peernet-data': DataMessage,
|
|
518
|
-
'peernet-data-response': DataMessageResponse,
|
|
519
|
-
'peernet-ps': PsMessage,
|
|
520
|
-
'chat-message': ChatMessage,
|
|
521
|
-
'peernet-file': PeernetFile
|
|
522
|
-
};
|
|
523
|
-
this._messageHandler = new MessageHandler(this.network);
|
|
524
|
-
const { daemon, environment } = await target();
|
|
525
|
-
this.hasDaemon = daemon;
|
|
526
|
-
for (const store of this.defaultStores) {
|
|
527
|
-
await this.addStore(store, options.storePrefix, options.root);
|
|
528
|
-
}
|
|
529
|
-
this.identity = new Identity(this.network);
|
|
530
|
-
await this.identity.load(password);
|
|
531
|
-
this._peerHandler = new PeerDiscovery(this.id);
|
|
532
|
-
this.peerId = this.id;
|
|
533
|
-
pubsub.subscribe('peer:connected', async (peer) => {
|
|
534
|
-
// console.log(peer);
|
|
535
|
-
// console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
536
|
-
// peer.on('peernet.data', async (message) => {
|
|
537
|
-
// const id = message.id
|
|
538
|
-
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
539
|
-
// const proto = protoFor(message.decoded.data)
|
|
540
|
-
// this._protoHandler({id, proto}, peer)
|
|
541
|
-
// })
|
|
542
|
-
});
|
|
543
|
-
/**
|
|
544
|
-
* converts data -> message -> proto
|
|
545
|
-
* @see DataHandler
|
|
546
|
-
*/
|
|
547
|
-
pubsub.subscribe('peer:data', dataHandler);
|
|
548
|
-
const importee = await import('@leofcoin/peernet-swarm/client');
|
|
549
|
-
/**
|
|
550
|
-
* @access public
|
|
551
|
-
* @type {PeernetClient}
|
|
552
|
-
*/
|
|
553
|
-
this.client = new importee.default(this.id, this.networkVersion, this.stars);
|
|
554
|
-
if (globalThis.navigator) {
|
|
555
|
-
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
process.on('SIGTERM', async () => {
|
|
559
|
-
process.stdin.resume();
|
|
560
|
-
await this.client.close();
|
|
561
|
-
process.exit();
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
return this;
|
|
565
|
-
}
|
|
566
|
-
addRequestHandler(name, method) {
|
|
567
|
-
this.requestProtos[name] = method;
|
|
568
|
-
}
|
|
569
|
-
sendMessage(peer, id, data) {
|
|
570
|
-
if (peer.readyState === 'open') {
|
|
571
|
-
peer.send(data, id);
|
|
572
|
-
this.bw.up += data.length;
|
|
573
|
-
}
|
|
574
|
-
else if (peer.readyState === 'closed') ;
|
|
575
|
-
}
|
|
576
|
-
async handleDHT(peer, id, proto) {
|
|
577
|
-
let { hash, store } = proto.decoded;
|
|
578
|
-
let has;
|
|
579
|
-
if (store) {
|
|
580
|
-
store = globalThis[`${store}Store`];
|
|
581
|
-
has = store.private ? false : await store.has(hash);
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
has = await this.has(hash);
|
|
585
|
-
}
|
|
586
|
-
const data = await new globalThis.peernet.protos['peernet-dht-response']({ hash, has });
|
|
587
|
-
const node = await this.prepareMessage(data);
|
|
588
|
-
this.sendMessage(peer, id, node.encoded);
|
|
589
|
-
}
|
|
590
|
-
async handleData(peer, id, proto) {
|
|
591
|
-
let { hash, store } = proto.decoded;
|
|
592
|
-
let data;
|
|
593
|
-
store = globalThis[`${store}Store`] || await this.whichStore([...this.stores], hash);
|
|
594
|
-
if (store && !store.private) {
|
|
595
|
-
data = await store.get(hash);
|
|
596
|
-
if (data) {
|
|
597
|
-
data = await new globalThis.peernet.protos['peernet-data-response']({ hash, data });
|
|
598
|
-
const node = await this.prepareMessage(data);
|
|
599
|
-
this.sendMessage(peer, id, node.encoded);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
async handleRequest(peer, id, proto) {
|
|
604
|
-
const method = this.requestProtos[proto.decoded.request];
|
|
605
|
-
if (method) {
|
|
606
|
-
const data = await method();
|
|
607
|
-
const node = await this.prepareMessage(data);
|
|
608
|
-
this.sendMessage(peer, id, node.encoded);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* @private
|
|
613
|
-
*
|
|
614
|
-
* @param {Buffer} message - peernet message
|
|
615
|
-
* @param {PeernetPeer} peer - peernet peer
|
|
616
|
-
*/
|
|
617
|
-
async _protoHandler(message, peer, from) {
|
|
618
|
-
const { id, proto } = message;
|
|
619
|
-
this.bw.down += proto.encoded.length;
|
|
620
|
-
switch (proto.name) {
|
|
621
|
-
case 'peernet-dht': {
|
|
622
|
-
this.handleDHT(peer, id, proto);
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
case 'peernet-data': {
|
|
626
|
-
this.handleData(peer, id, proto);
|
|
627
|
-
break;
|
|
628
|
-
}
|
|
629
|
-
case 'peernet-request': {
|
|
630
|
-
this.handleRequest(peer, id, proto);
|
|
631
|
-
}
|
|
632
|
-
case 'peernet-ps': {
|
|
633
|
-
if (peer.peerId !== this.id)
|
|
634
|
-
globalSub.publish(proto.decoded.topic, proto.decoded.data);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* performs a walk and resolves first encounter
|
|
640
|
-
*
|
|
641
|
-
* @param {String} hash
|
|
642
|
-
*/
|
|
643
|
-
async walk(hash) {
|
|
644
|
-
if (!hash)
|
|
645
|
-
throw new Error('hash expected, received undefined');
|
|
646
|
-
const data = await new globalThis.peernet.protos['peernet-dht']({ hash });
|
|
647
|
-
this.client.id;
|
|
648
|
-
const walk = async (peer) => {
|
|
649
|
-
const node = await this.prepareMessage(data);
|
|
650
|
-
let result = await peer.request(node.encoded);
|
|
651
|
-
result = new Uint8Array(Object.values(result));
|
|
652
|
-
const proto = await protoFor(result);
|
|
653
|
-
if (proto.name !== 'peernet-dht-response')
|
|
654
|
-
throw dhtError(proto.name);
|
|
655
|
-
// TODO: give ip and port (just used for location)
|
|
656
|
-
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
657
|
-
peer.connection.remoteFamily = 'ipv4';
|
|
658
|
-
peer.connection.remoteAddress = '127.0.0.1';
|
|
659
|
-
peer.connection.remotePort = '0000';
|
|
660
|
-
}
|
|
661
|
-
const peerInfo = {
|
|
662
|
-
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
663
|
-
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
664
|
-
port: peer.connection.remotePort || peer.connection.localPort,
|
|
665
|
-
id: peer.peerId,
|
|
666
|
-
};
|
|
667
|
-
if (proto.decoded.has)
|
|
668
|
-
this.dht.addProvider(peerInfo, proto.decoded.hash);
|
|
669
|
-
};
|
|
670
|
-
let walks = [];
|
|
671
|
-
for (const peer of this.connections) {
|
|
672
|
-
if (peer.peerId !== this.id) {
|
|
673
|
-
walks.push(walk(peer));
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
return Promise.all(walks);
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Override DHT behavior, try's finding the content three times
|
|
680
|
-
*
|
|
681
|
-
* @param {String} hash
|
|
682
|
-
*/
|
|
683
|
-
async providersFor(hash) {
|
|
684
|
-
let providers = await this.dht.providersFor(hash);
|
|
685
|
-
// walk the network to find a provider
|
|
686
|
-
if (!providers || providers.length === 0) {
|
|
687
|
-
await this.walk(hash);
|
|
688
|
-
providers = await this.dht.providersFor(hash);
|
|
689
|
-
// second walk the network to find a provider
|
|
690
|
-
if (!providers || providers.length === 0) {
|
|
691
|
-
await this.walk(hash);
|
|
692
|
-
providers = await this.dht.providersFor(hash);
|
|
693
|
-
}
|
|
694
|
-
// last walk
|
|
695
|
-
if (!providers || providers.length === 0) {
|
|
696
|
-
await this.walk(hash);
|
|
697
|
-
providers = await this.dht.providersFor(hash);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
// undefined if no providers given
|
|
701
|
-
return providers;
|
|
702
|
-
}
|
|
703
|
-
get block() {
|
|
704
|
-
return {
|
|
705
|
-
get: async (hash) => {
|
|
706
|
-
const data = await blockStore.has(hash);
|
|
707
|
-
if (data)
|
|
708
|
-
return blockStore.get(hash);
|
|
709
|
-
return this.requestData(hash, 'block');
|
|
710
|
-
},
|
|
711
|
-
put: async (hash, data) => {
|
|
712
|
-
if (await blockStore.has(hash))
|
|
713
|
-
return;
|
|
714
|
-
return await blockStore.put(hash, data);
|
|
715
|
-
},
|
|
716
|
-
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
get transaction() {
|
|
720
|
-
return {
|
|
721
|
-
get: async (hash) => {
|
|
722
|
-
const data = await transactionStore.has(hash);
|
|
723
|
-
if (data)
|
|
724
|
-
return await transactionStore.get(hash);
|
|
725
|
-
return this.requestData(hash, 'transaction');
|
|
726
|
-
},
|
|
727
|
-
put: async (hash, data) => {
|
|
728
|
-
if (await transactionStore.has(hash))
|
|
729
|
-
return;
|
|
730
|
-
return await transactionStore.put(hash, data);
|
|
731
|
-
},
|
|
732
|
-
has: async (hash) => await transactionStore.has(hash),
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
async requestData(hash, store) {
|
|
736
|
-
const providers = await this.providersFor(hash);
|
|
737
|
-
if (!providers || providers.size === 0)
|
|
738
|
-
throw nothingFoundError(hash);
|
|
739
|
-
debug(`found ${providers.size} provider(s) for ${hash}`);
|
|
740
|
-
// get closest peer on earth
|
|
741
|
-
const closestPeer = await this.dht.closestPeer(providers);
|
|
742
|
-
// get peer instance by id
|
|
743
|
-
if (!closestPeer || !closestPeer.id)
|
|
744
|
-
return this.requestData(hash, store?.name || store);
|
|
745
|
-
const id = closestPeer.id;
|
|
746
|
-
if (this.connections) {
|
|
747
|
-
let closest = this.connections.filter((peer) => {
|
|
748
|
-
if (peer.peerId === id)
|
|
749
|
-
return peer;
|
|
750
|
-
});
|
|
751
|
-
let data = await new globalThis.peernet.protos['peernet-data']({ hash, store: store?.name || store });
|
|
752
|
-
const node = await this.prepareMessage(data);
|
|
753
|
-
if (closest[0])
|
|
754
|
-
data = await closest[0].request(node.encoded);
|
|
755
|
-
else {
|
|
756
|
-
closest = this.connections.filter((peer) => {
|
|
757
|
-
if (peer.peerId === id)
|
|
758
|
-
return peer;
|
|
759
|
-
});
|
|
760
|
-
if (closest[0])
|
|
761
|
-
data = await closest[0].request(node.encoded);
|
|
762
|
-
}
|
|
763
|
-
const proto = await protoFor(data);
|
|
764
|
-
// TODO: store data automaticly or not
|
|
765
|
-
return BufferToUint8Array(proto.decoded.data);
|
|
766
|
-
// this.put(hash, proto.decoded.data)
|
|
767
|
-
}
|
|
768
|
-
return;
|
|
769
|
-
}
|
|
770
|
-
get message() {
|
|
771
|
-
return {
|
|
772
|
-
/**
|
|
773
|
-
* Get content for given message hash
|
|
774
|
-
*
|
|
775
|
-
* @param {String} hash
|
|
776
|
-
*/
|
|
777
|
-
get: async (hash) => {
|
|
778
|
-
debug(`get message ${hash}`);
|
|
779
|
-
const message = await messageStore.has(hash);
|
|
780
|
-
if (message)
|
|
781
|
-
return await messageStore.get(hash);
|
|
782
|
-
return this.requestData(hash, 'message');
|
|
783
|
-
},
|
|
784
|
-
/**
|
|
785
|
-
* put message content
|
|
786
|
-
*
|
|
787
|
-
* @param {String} hash
|
|
788
|
-
* @param {Buffer} message
|
|
789
|
-
*/
|
|
790
|
-
put: async (hash, message) => await messageStore.put(hash, message),
|
|
791
|
-
/**
|
|
792
|
-
* @param {String} hash
|
|
793
|
-
* @return {Boolean}
|
|
794
|
-
*/
|
|
795
|
-
has: async (hash) => await messageStore.has(hash),
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
get data() {
|
|
799
|
-
return {
|
|
800
|
-
/**
|
|
801
|
-
* Get content for given data hash
|
|
802
|
-
*
|
|
803
|
-
* @param {String} hash
|
|
804
|
-
*/
|
|
805
|
-
get: async (hash) => {
|
|
806
|
-
debug(`get data ${hash}`);
|
|
807
|
-
const data = await dataStore.has(hash);
|
|
808
|
-
if (data)
|
|
809
|
-
return await dataStore.get(hash);
|
|
810
|
-
return this.requestData(hash, 'data');
|
|
811
|
-
},
|
|
812
|
-
/**
|
|
813
|
-
* put data content
|
|
814
|
-
*
|
|
815
|
-
* @param {String} hash
|
|
816
|
-
* @param {Buffer} data
|
|
817
|
-
*/
|
|
818
|
-
put: async (hash, data) => await dataStore.put(hash, data),
|
|
819
|
-
/**
|
|
820
|
-
* @param {String} hash
|
|
821
|
-
* @return {Boolean}
|
|
822
|
-
*/
|
|
823
|
-
has: async (hash) => await dataStore.has(hash),
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
get folder() {
|
|
827
|
-
return {
|
|
828
|
-
/**
|
|
829
|
-
* Get content for given data hash
|
|
830
|
-
*
|
|
831
|
-
* @param {String} hash
|
|
832
|
-
*/
|
|
833
|
-
get: async (hash) => {
|
|
834
|
-
debug(`get data ${hash}`);
|
|
835
|
-
const data = await dataStore.has(hash);
|
|
836
|
-
if (data)
|
|
837
|
-
return await dataStore.get(hash);
|
|
838
|
-
return this.requestData(hash, 'data');
|
|
839
|
-
},
|
|
840
|
-
/**
|
|
841
|
-
* put data content
|
|
842
|
-
*
|
|
843
|
-
* @param {String} hash
|
|
844
|
-
* @param {Buffer} data
|
|
845
|
-
*/
|
|
846
|
-
put: async (hash, data) => await dataStore.put(hash, data),
|
|
847
|
-
/**
|
|
848
|
-
* @param {String} hash
|
|
849
|
-
* @return {Boolean}
|
|
850
|
-
*/
|
|
851
|
-
has: async (hash) => await dataStore.has(hash),
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
async addFolder(files) {
|
|
855
|
-
const links = [];
|
|
856
|
-
for (const file of files) {
|
|
857
|
-
const fileNode = await new globalThis.peernet.protos['peernet-file'](file);
|
|
858
|
-
const hash = await fileNode.hash;
|
|
859
|
-
await dataStore.put(hash, fileNode.encoded);
|
|
860
|
-
links.push({ hash, path: file.path });
|
|
861
|
-
}
|
|
862
|
-
const node = await new globalThis.peernet.protos['peernet-file']({ path: '/', links });
|
|
863
|
-
const hash = await node.hash;
|
|
864
|
-
await dataStore.put(hash, node.encoded);
|
|
865
|
-
return hash;
|
|
866
|
-
}
|
|
867
|
-
async ls(hash, options) {
|
|
868
|
-
let data;
|
|
869
|
-
const has = await dataStore.has(hash);
|
|
870
|
-
data = has ? await dataStore.get(hash) : await this.requestData(hash, 'data');
|
|
871
|
-
const node = await new peernet.protos['peernet-file'](data);
|
|
872
|
-
await node.decode();
|
|
873
|
-
console.log(data);
|
|
874
|
-
const paths = [];
|
|
875
|
-
if (node.decoded?.links.length === 0)
|
|
876
|
-
throw new Error(`${hash} is a file`);
|
|
877
|
-
for (const { path, hash } of node.decoded.links) {
|
|
878
|
-
paths.push({ path, hash });
|
|
879
|
-
}
|
|
880
|
-
if (options?.pin)
|
|
881
|
-
await dataStore.put(hash, node.encoded);
|
|
882
|
-
return paths;
|
|
883
|
-
}
|
|
884
|
-
async cat(hash, options) {
|
|
885
|
-
let data;
|
|
886
|
-
const has = await dataStore.has(hash);
|
|
887
|
-
data = has ? await dataStore.get(hash) : await this.requestData(hash, 'data');
|
|
888
|
-
const node = await new peernet.protos['peernet-file'](data);
|
|
889
|
-
if (node.decoded?.links.length > 0)
|
|
890
|
-
throw new Error(`${hash} is a directory`);
|
|
891
|
-
if (options?.pin)
|
|
892
|
-
await dataStore.put(hash, node.encoded);
|
|
893
|
-
return node.decoded.content;
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* goes trough given stores and tries to find data for given hash
|
|
897
|
-
* @param {Array} stores
|
|
898
|
-
* @param {string} hash
|
|
899
|
-
*/
|
|
900
|
-
async whichStore(stores, hash) {
|
|
901
|
-
let store = stores.pop();
|
|
902
|
-
store = globalThis[`${store}Store`];
|
|
903
|
-
if (store) {
|
|
904
|
-
const has = await store.has(hash);
|
|
905
|
-
if (has)
|
|
906
|
-
return store;
|
|
907
|
-
if (stores.length > 0)
|
|
908
|
-
return this.whichStore(stores, hash);
|
|
909
|
-
}
|
|
910
|
-
else
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
/**
|
|
914
|
-
* Get content for given hash
|
|
915
|
-
*
|
|
916
|
-
* @param {String} hash - the hash of the wanted data
|
|
917
|
-
* @param {String} store - storeName to access
|
|
918
|
-
*/
|
|
919
|
-
async get(hash, store) {
|
|
920
|
-
debug(`get ${hash}`);
|
|
921
|
-
let data;
|
|
922
|
-
if (store)
|
|
923
|
-
store = globalThis[`${store}Store`];
|
|
924
|
-
if (!store)
|
|
925
|
-
store = await this.whichStore([...this.stores], hash);
|
|
926
|
-
if (store && await store.has(hash))
|
|
927
|
-
data = await store.get(hash);
|
|
928
|
-
if (data)
|
|
929
|
-
return data;
|
|
930
|
-
return this.requestData(hash, store?.name || store);
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* put content
|
|
934
|
-
*
|
|
935
|
-
* @param {String} hash
|
|
936
|
-
* @param {Buffer} data
|
|
937
|
-
* @param {String} store - storeName to access
|
|
938
|
-
*/
|
|
939
|
-
async put(hash, data, store = 'data') {
|
|
940
|
-
store = globalThis[`${store}Store`];
|
|
941
|
-
return store.put(hash, data);
|
|
942
|
-
}
|
|
943
|
-
/**
|
|
944
|
-
* @param {String} hash
|
|
945
|
-
* @return {Boolean}
|
|
946
|
-
*/
|
|
947
|
-
async has(hash) {
|
|
948
|
-
const store = await this.whichStore([...this.stores], hash);
|
|
949
|
-
if (store) {
|
|
950
|
-
return store.private ? false : true;
|
|
951
|
-
}
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
*
|
|
956
|
-
* @param {String} topic
|
|
957
|
-
* @param {String|Object|Array|Boolean|Buffer} data
|
|
958
|
-
*/
|
|
959
|
-
async publish(topic, data) {
|
|
960
|
-
// globalSub.publish(topic, data)
|
|
961
|
-
const id = Math.random().toString(36).slice(-12);
|
|
962
|
-
data = await new globalThis.peernet.protos['peernet-ps']({ data, topic });
|
|
963
|
-
for (const peer of this.connections) {
|
|
964
|
-
if (peer.peerId !== this.peerId) {
|
|
965
|
-
const node = await this.prepareMessage(data);
|
|
966
|
-
this.sendMessage(peer, id, node.encoded);
|
|
967
|
-
}
|
|
968
|
-
// TODO: if peer subscribed
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
createHash(data, name) {
|
|
972
|
-
return new CodeHash(data, { name });
|
|
973
|
-
}
|
|
974
|
-
/**
|
|
975
|
-
*
|
|
976
|
-
* @param {String} topic
|
|
977
|
-
* @param {Method} cb
|
|
978
|
-
*/
|
|
979
|
-
async subscribe(topic, callback) {
|
|
980
|
-
// TODO: if peer subscribed
|
|
981
|
-
globalSub.subscribe(topic, callback);
|
|
982
|
-
}
|
|
983
|
-
async removePeer(peer) {
|
|
984
|
-
return this.client.removePeer(peer);
|
|
985
|
-
}
|
|
986
|
-
get Buffer() {
|
|
987
|
-
return Buffer;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
372
|
+
globalThis.LeofcoinStorage = LeofcoinStorage;
|
|
373
|
+
globalThis.leofcoin = globalThis.leofcoin || {};
|
|
374
|
+
globalThis.pubsub = globalThis.pubsub || new PubSub();
|
|
375
|
+
globalThis.globalSub = globalThis.globalSub || new PubSub(true);
|
|
376
|
+
/**
|
|
377
|
+
* @access public
|
|
378
|
+
* @example
|
|
379
|
+
* const peernet = new Peernet();
|
|
380
|
+
*/
|
|
381
|
+
class Peernet {
|
|
382
|
+
identity;
|
|
383
|
+
stores = [];
|
|
384
|
+
/**
|
|
385
|
+
* @type {Object}
|
|
386
|
+
* @property {Object} peer Instance of Peer
|
|
387
|
+
*/
|
|
388
|
+
dht = new DhtEarth();
|
|
389
|
+
/** @leofcoin/peernet-swarm/client */
|
|
390
|
+
client;
|
|
391
|
+
network;
|
|
392
|
+
stars;
|
|
393
|
+
networkVersion;
|
|
394
|
+
bw;
|
|
395
|
+
/**
|
|
396
|
+
* @access public
|
|
397
|
+
* @param {Object} options
|
|
398
|
+
* @param {String} options.network - desired network
|
|
399
|
+
* @param {String} options.stars - star list for selected network (these should match, don't mix networks)
|
|
400
|
+
* @param {String} options.root - path to root directory
|
|
401
|
+
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
402
|
+
*
|
|
403
|
+
* @return {Promise} instance of Peernet
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
407
|
+
*/
|
|
408
|
+
constructor(options, password) {
|
|
409
|
+
/**
|
|
410
|
+
* @property {String} network - current network
|
|
411
|
+
*/
|
|
412
|
+
this.network = options.network || 'leofcoin';
|
|
413
|
+
this.stars = options.stars;
|
|
414
|
+
const parts = this.network.split(':');
|
|
415
|
+
this.networkVersion = options.networkVersion || parts.length > 1 ? parts[1] : 'mainnet';
|
|
416
|
+
if (!options.storePrefix)
|
|
417
|
+
options.storePrefix = 'lfc';
|
|
418
|
+
if (!options.port)
|
|
419
|
+
options.port = 2000;
|
|
420
|
+
if (!options.root) {
|
|
421
|
+
parts[1] ? options.root = `.${parts[0]}/${parts[1]}` : options.root = `.${this.network}`;
|
|
422
|
+
}
|
|
423
|
+
globalThis.peernet = this;
|
|
424
|
+
this.bw = {
|
|
425
|
+
up: 0,
|
|
426
|
+
down: 0,
|
|
427
|
+
};
|
|
428
|
+
return this._init(options, password);
|
|
429
|
+
}
|
|
430
|
+
get id() {
|
|
431
|
+
return this.identity.id;
|
|
432
|
+
}
|
|
433
|
+
get accounts() {
|
|
434
|
+
return this.identity.accounts;
|
|
435
|
+
}
|
|
436
|
+
get defaultStores() {
|
|
437
|
+
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message'];
|
|
438
|
+
}
|
|
439
|
+
addProto(name, proto) {
|
|
440
|
+
if (!globalThis.peernet.protos[name])
|
|
441
|
+
globalThis.peernet.protos[name] = proto;
|
|
442
|
+
}
|
|
443
|
+
addCodec(codec) {
|
|
444
|
+
return utils.addCodec(codec);
|
|
445
|
+
}
|
|
446
|
+
async addStore(name, prefix, root, isPrivate = true) {
|
|
447
|
+
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
448
|
+
name === 'data' || name === 'message')
|
|
449
|
+
isPrivate = false;
|
|
450
|
+
let Storage;
|
|
451
|
+
this.hasDaemon ? Storage = LeofcoinStorageClient : Storage = LeofcoinStorage;
|
|
452
|
+
if (!globalThis[`${name}Store`]) {
|
|
453
|
+
globalThis[`${name}Store`] = new Storage(name, root);
|
|
454
|
+
await globalThis[`${name}Store`].init();
|
|
455
|
+
}
|
|
456
|
+
globalThis[`${name}Store`].private = isPrivate;
|
|
457
|
+
if (!isPrivate)
|
|
458
|
+
this.stores.push(name);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* @see MessageHandler
|
|
462
|
+
*/
|
|
463
|
+
prepareMessage(data) {
|
|
464
|
+
return this._messageHandler.prepareMessage(data);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* @access public
|
|
468
|
+
*
|
|
469
|
+
* @return {Array} peerId
|
|
470
|
+
*/
|
|
471
|
+
get peers() {
|
|
472
|
+
return Object.keys(this.client.connections);
|
|
473
|
+
}
|
|
474
|
+
get connections() {
|
|
475
|
+
return Object.values(this.client.connections);
|
|
476
|
+
}
|
|
477
|
+
get peerEntries() {
|
|
478
|
+
return Object.entries(this.client.connections);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* @return {String} id - peerId
|
|
482
|
+
*/
|
|
483
|
+
getConnection(id) {
|
|
484
|
+
return this.client.connections[id];
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* @private
|
|
488
|
+
*
|
|
489
|
+
* @param {Object} options
|
|
490
|
+
* @param {String} options.root - path to root directory
|
|
491
|
+
*
|
|
492
|
+
* @return {Promise} instance of Peernet
|
|
493
|
+
*/
|
|
494
|
+
async _init(options, password) {
|
|
495
|
+
this.requestProtos = {};
|
|
496
|
+
this.storePrefix = options.storePrefix;
|
|
497
|
+
this.root = options.root;
|
|
498
|
+
const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile
|
|
499
|
+
// FolderMessageResponse
|
|
500
|
+
} = await import(/* webpackChunkName: "messages" */ './messages-65501ef9.js');
|
|
501
|
+
/**
|
|
502
|
+
* proto Object containing protos
|
|
503
|
+
* @type {Object}
|
|
504
|
+
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
505
|
+
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
506
|
+
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
507
|
+
* @property {DataMessage} protos[peernet-data] messageNode
|
|
508
|
+
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
509
|
+
*/
|
|
510
|
+
globalThis.peernet.protos = {
|
|
511
|
+
'peernet-request': RequestMessage,
|
|
512
|
+
'peernet-response': ResponseMessage,
|
|
513
|
+
'peernet-peer': PeerMessage,
|
|
514
|
+
'peernet-peer-response': PeerMessageResponse,
|
|
515
|
+
'peernet-message': PeernetMessage,
|
|
516
|
+
'peernet-dht': DHTMessage,
|
|
517
|
+
'peernet-dht-response': DHTMessageResponse,
|
|
518
|
+
'peernet-data': DataMessage,
|
|
519
|
+
'peernet-data-response': DataMessageResponse,
|
|
520
|
+
'peernet-ps': PsMessage,
|
|
521
|
+
'chat-message': ChatMessage,
|
|
522
|
+
'peernet-file': PeernetFile
|
|
523
|
+
};
|
|
524
|
+
this._messageHandler = new MessageHandler(this.network);
|
|
525
|
+
const { daemon, environment } = await target();
|
|
526
|
+
this.hasDaemon = daemon;
|
|
527
|
+
for (const store of this.defaultStores) {
|
|
528
|
+
await this.addStore(store, options.storePrefix, options.root);
|
|
529
|
+
}
|
|
530
|
+
this.identity = new Identity(this.network);
|
|
531
|
+
await this.identity.load(password);
|
|
532
|
+
this._peerHandler = new PeerDiscovery(this.id);
|
|
533
|
+
this.peerId = this.id;
|
|
534
|
+
pubsub.subscribe('peer:connected', async (peer) => {
|
|
535
|
+
// console.log(peer);
|
|
536
|
+
// console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
537
|
+
// peer.on('peernet.data', async (message) => {
|
|
538
|
+
// const id = message.id
|
|
539
|
+
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
540
|
+
// const proto = protoFor(message.decoded.data)
|
|
541
|
+
// this._protoHandler({id, proto}, peer)
|
|
542
|
+
// })
|
|
543
|
+
});
|
|
544
|
+
/**
|
|
545
|
+
* converts data -> message -> proto
|
|
546
|
+
* @see DataHandler
|
|
547
|
+
*/
|
|
548
|
+
pubsub.subscribe('peer:data', dataHandler);
|
|
549
|
+
const importee = await import('@leofcoin/peernet-swarm/client');
|
|
550
|
+
/**
|
|
551
|
+
* @access public
|
|
552
|
+
* @type {PeernetClient}
|
|
553
|
+
*/
|
|
554
|
+
this.client = new importee.default(this.id, this.networkVersion, this.stars);
|
|
555
|
+
if (globalThis.navigator) {
|
|
556
|
+
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
process.on('SIGTERM', async () => {
|
|
560
|
+
process.stdin.resume();
|
|
561
|
+
await this.client.close();
|
|
562
|
+
process.exit();
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
return this;
|
|
566
|
+
}
|
|
567
|
+
addRequestHandler(name, method) {
|
|
568
|
+
this.requestProtos[name] = method;
|
|
569
|
+
}
|
|
570
|
+
sendMessage(peer, id, data) {
|
|
571
|
+
if (peer.readyState === 'open') {
|
|
572
|
+
peer.send(data, id);
|
|
573
|
+
this.bw.up += data.length;
|
|
574
|
+
}
|
|
575
|
+
else if (peer.readyState === 'closed') ;
|
|
576
|
+
}
|
|
577
|
+
async handleDHT(peer, id, proto) {
|
|
578
|
+
let { hash, store } = proto.decoded;
|
|
579
|
+
let has;
|
|
580
|
+
if (store) {
|
|
581
|
+
store = globalThis[`${store}Store`];
|
|
582
|
+
has = store.private ? false : await store.has(hash);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
has = await this.has(hash);
|
|
586
|
+
}
|
|
587
|
+
const data = await new globalThis.peernet.protos['peernet-dht-response']({ hash, has });
|
|
588
|
+
const node = await this.prepareMessage(data);
|
|
589
|
+
this.sendMessage(peer, id, node.encoded);
|
|
590
|
+
}
|
|
591
|
+
async handleData(peer, id, proto) {
|
|
592
|
+
let { hash, store } = proto.decoded;
|
|
593
|
+
let data;
|
|
594
|
+
store = globalThis[`${store}Store`] || await this.whichStore([...this.stores], hash);
|
|
595
|
+
if (store && !store.private) {
|
|
596
|
+
data = await store.get(hash);
|
|
597
|
+
if (data) {
|
|
598
|
+
data = await new globalThis.peernet.protos['peernet-data-response']({ hash, data });
|
|
599
|
+
const node = await this.prepareMessage(data);
|
|
600
|
+
this.sendMessage(peer, id, node.encoded);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
async handleRequest(peer, id, proto) {
|
|
605
|
+
const method = this.requestProtos[proto.decoded.request];
|
|
606
|
+
if (method) {
|
|
607
|
+
const data = await method();
|
|
608
|
+
const node = await this.prepareMessage(data);
|
|
609
|
+
this.sendMessage(peer, id, node.encoded);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* @private
|
|
614
|
+
*
|
|
615
|
+
* @param {Buffer} message - peernet message
|
|
616
|
+
* @param {PeernetPeer} peer - peernet peer
|
|
617
|
+
*/
|
|
618
|
+
async _protoHandler(message, peer, from) {
|
|
619
|
+
const { id, proto } = message;
|
|
620
|
+
this.bw.down += proto.encoded.length;
|
|
621
|
+
switch (proto.name) {
|
|
622
|
+
case 'peernet-dht': {
|
|
623
|
+
this.handleDHT(peer, id, proto);
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
case 'peernet-data': {
|
|
627
|
+
this.handleData(peer, id, proto);
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
case 'peernet-request': {
|
|
631
|
+
this.handleRequest(peer, id, proto);
|
|
632
|
+
}
|
|
633
|
+
case 'peernet-ps': {
|
|
634
|
+
if (peer.peerId !== this.id)
|
|
635
|
+
globalSub.publish(proto.decoded.topic, proto.decoded.data);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* performs a walk and resolves first encounter
|
|
641
|
+
*
|
|
642
|
+
* @param {String} hash
|
|
643
|
+
*/
|
|
644
|
+
async walk(hash) {
|
|
645
|
+
if (!hash)
|
|
646
|
+
throw new Error('hash expected, received undefined');
|
|
647
|
+
const data = await new globalThis.peernet.protos['peernet-dht']({ hash });
|
|
648
|
+
this.client.id;
|
|
649
|
+
const walk = async (peer) => {
|
|
650
|
+
const node = await this.prepareMessage(data);
|
|
651
|
+
let result = await peer.request(node.encoded);
|
|
652
|
+
result = new Uint8Array(Object.values(result));
|
|
653
|
+
const proto = await protoFor(result);
|
|
654
|
+
if (proto.name !== 'peernet-dht-response')
|
|
655
|
+
throw dhtError(proto.name);
|
|
656
|
+
// TODO: give ip and port (just used for location)
|
|
657
|
+
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
658
|
+
peer.connection.remoteFamily = 'ipv4';
|
|
659
|
+
peer.connection.remoteAddress = '127.0.0.1';
|
|
660
|
+
peer.connection.remotePort = '0000';
|
|
661
|
+
}
|
|
662
|
+
const peerInfo = {
|
|
663
|
+
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
664
|
+
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
665
|
+
port: peer.connection.remotePort || peer.connection.localPort,
|
|
666
|
+
id: peer.peerId,
|
|
667
|
+
};
|
|
668
|
+
if (proto.decoded.has)
|
|
669
|
+
this.dht.addProvider(peerInfo, proto.decoded.hash);
|
|
670
|
+
};
|
|
671
|
+
let walks = [];
|
|
672
|
+
for (const peer of this.connections) {
|
|
673
|
+
if (peer.peerId !== this.id) {
|
|
674
|
+
walks.push(walk(peer));
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return Promise.all(walks);
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Override DHT behavior, try's finding the content three times
|
|
681
|
+
*
|
|
682
|
+
* @param {String} hash
|
|
683
|
+
*/
|
|
684
|
+
async providersFor(hash) {
|
|
685
|
+
let providers = await this.dht.providersFor(hash);
|
|
686
|
+
// walk the network to find a provider
|
|
687
|
+
if (!providers || providers.length === 0) {
|
|
688
|
+
await this.walk(hash);
|
|
689
|
+
providers = await this.dht.providersFor(hash);
|
|
690
|
+
// second walk the network to find a provider
|
|
691
|
+
if (!providers || providers.length === 0) {
|
|
692
|
+
await this.walk(hash);
|
|
693
|
+
providers = await this.dht.providersFor(hash);
|
|
694
|
+
}
|
|
695
|
+
// last walk
|
|
696
|
+
if (!providers || providers.length === 0) {
|
|
697
|
+
await this.walk(hash);
|
|
698
|
+
providers = await this.dht.providersFor(hash);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// undefined if no providers given
|
|
702
|
+
return providers;
|
|
703
|
+
}
|
|
704
|
+
get block() {
|
|
705
|
+
return {
|
|
706
|
+
get: async (hash) => {
|
|
707
|
+
const data = await blockStore.has(hash);
|
|
708
|
+
if (data)
|
|
709
|
+
return blockStore.get(hash);
|
|
710
|
+
return this.requestData(hash, 'block');
|
|
711
|
+
},
|
|
712
|
+
put: async (hash, data) => {
|
|
713
|
+
if (await blockStore.has(hash))
|
|
714
|
+
return;
|
|
715
|
+
return await blockStore.put(hash, data);
|
|
716
|
+
},
|
|
717
|
+
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
get transaction() {
|
|
721
|
+
return {
|
|
722
|
+
get: async (hash) => {
|
|
723
|
+
const data = await transactionStore.has(hash);
|
|
724
|
+
if (data)
|
|
725
|
+
return await transactionStore.get(hash);
|
|
726
|
+
return this.requestData(hash, 'transaction');
|
|
727
|
+
},
|
|
728
|
+
put: async (hash, data) => {
|
|
729
|
+
if (await transactionStore.has(hash))
|
|
730
|
+
return;
|
|
731
|
+
return await transactionStore.put(hash, data);
|
|
732
|
+
},
|
|
733
|
+
has: async (hash) => await transactionStore.has(hash),
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async requestData(hash, store) {
|
|
737
|
+
const providers = await this.providersFor(hash);
|
|
738
|
+
if (!providers || providers.size === 0)
|
|
739
|
+
throw nothingFoundError(hash);
|
|
740
|
+
debug(`found ${providers.size} provider(s) for ${hash}`);
|
|
741
|
+
// get closest peer on earth
|
|
742
|
+
const closestPeer = await this.dht.closestPeer(providers);
|
|
743
|
+
// get peer instance by id
|
|
744
|
+
if (!closestPeer || !closestPeer.id)
|
|
745
|
+
return this.requestData(hash, store?.name || store);
|
|
746
|
+
const id = closestPeer.id;
|
|
747
|
+
if (this.connections) {
|
|
748
|
+
let closest = this.connections.filter((peer) => {
|
|
749
|
+
if (peer.peerId === id)
|
|
750
|
+
return peer;
|
|
751
|
+
});
|
|
752
|
+
let data = await new globalThis.peernet.protos['peernet-data']({ hash, store: store?.name || store });
|
|
753
|
+
const node = await this.prepareMessage(data);
|
|
754
|
+
if (closest[0])
|
|
755
|
+
data = await closest[0].request(node.encoded);
|
|
756
|
+
else {
|
|
757
|
+
closest = this.connections.filter((peer) => {
|
|
758
|
+
if (peer.peerId === id)
|
|
759
|
+
return peer;
|
|
760
|
+
});
|
|
761
|
+
if (closest[0])
|
|
762
|
+
data = await closest[0].request(node.encoded);
|
|
763
|
+
}
|
|
764
|
+
const proto = await protoFor(data);
|
|
765
|
+
// TODO: store data automaticly or not
|
|
766
|
+
return BufferToUint8Array(proto.decoded.data);
|
|
767
|
+
// this.put(hash, proto.decoded.data)
|
|
768
|
+
}
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
get message() {
|
|
772
|
+
return {
|
|
773
|
+
/**
|
|
774
|
+
* Get content for given message hash
|
|
775
|
+
*
|
|
776
|
+
* @param {String} hash
|
|
777
|
+
*/
|
|
778
|
+
get: async (hash) => {
|
|
779
|
+
debug(`get message ${hash}`);
|
|
780
|
+
const message = await messageStore.has(hash);
|
|
781
|
+
if (message)
|
|
782
|
+
return await messageStore.get(hash);
|
|
783
|
+
return this.requestData(hash, 'message');
|
|
784
|
+
},
|
|
785
|
+
/**
|
|
786
|
+
* put message content
|
|
787
|
+
*
|
|
788
|
+
* @param {String} hash
|
|
789
|
+
* @param {Buffer} message
|
|
790
|
+
*/
|
|
791
|
+
put: async (hash, message) => await messageStore.put(hash, message),
|
|
792
|
+
/**
|
|
793
|
+
* @param {String} hash
|
|
794
|
+
* @return {Boolean}
|
|
795
|
+
*/
|
|
796
|
+
has: async (hash) => await messageStore.has(hash),
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
get data() {
|
|
800
|
+
return {
|
|
801
|
+
/**
|
|
802
|
+
* Get content for given data hash
|
|
803
|
+
*
|
|
804
|
+
* @param {String} hash
|
|
805
|
+
*/
|
|
806
|
+
get: async (hash) => {
|
|
807
|
+
debug(`get data ${hash}`);
|
|
808
|
+
const data = await dataStore.has(hash);
|
|
809
|
+
if (data)
|
|
810
|
+
return await dataStore.get(hash);
|
|
811
|
+
return this.requestData(hash, 'data');
|
|
812
|
+
},
|
|
813
|
+
/**
|
|
814
|
+
* put data content
|
|
815
|
+
*
|
|
816
|
+
* @param {String} hash
|
|
817
|
+
* @param {Buffer} data
|
|
818
|
+
*/
|
|
819
|
+
put: async (hash, data) => await dataStore.put(hash, data),
|
|
820
|
+
/**
|
|
821
|
+
* @param {String} hash
|
|
822
|
+
* @return {Boolean}
|
|
823
|
+
*/
|
|
824
|
+
has: async (hash) => await dataStore.has(hash),
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
get folder() {
|
|
828
|
+
return {
|
|
829
|
+
/**
|
|
830
|
+
* Get content for given data hash
|
|
831
|
+
*
|
|
832
|
+
* @param {String} hash
|
|
833
|
+
*/
|
|
834
|
+
get: async (hash) => {
|
|
835
|
+
debug(`get data ${hash}`);
|
|
836
|
+
const data = await dataStore.has(hash);
|
|
837
|
+
if (data)
|
|
838
|
+
return await dataStore.get(hash);
|
|
839
|
+
return this.requestData(hash, 'data');
|
|
840
|
+
},
|
|
841
|
+
/**
|
|
842
|
+
* put data content
|
|
843
|
+
*
|
|
844
|
+
* @param {String} hash
|
|
845
|
+
* @param {Buffer} data
|
|
846
|
+
*/
|
|
847
|
+
put: async (hash, data) => await dataStore.put(hash, data),
|
|
848
|
+
/**
|
|
849
|
+
* @param {String} hash
|
|
850
|
+
* @return {Boolean}
|
|
851
|
+
*/
|
|
852
|
+
has: async (hash) => await dataStore.has(hash),
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
async addFolder(files) {
|
|
856
|
+
const links = [];
|
|
857
|
+
for (const file of files) {
|
|
858
|
+
const fileNode = await new globalThis.peernet.protos['peernet-file'](file);
|
|
859
|
+
const hash = await fileNode.hash;
|
|
860
|
+
await dataStore.put(hash, fileNode.encoded);
|
|
861
|
+
links.push({ hash, path: file.path });
|
|
862
|
+
}
|
|
863
|
+
const node = await new globalThis.peernet.protos['peernet-file']({ path: '/', links });
|
|
864
|
+
const hash = await node.hash;
|
|
865
|
+
await dataStore.put(hash, node.encoded);
|
|
866
|
+
return hash;
|
|
867
|
+
}
|
|
868
|
+
async ls(hash, options) {
|
|
869
|
+
let data;
|
|
870
|
+
const has = await dataStore.has(hash);
|
|
871
|
+
data = has ? await dataStore.get(hash) : await this.requestData(hash, 'data');
|
|
872
|
+
const node = await new peernet.protos['peernet-file'](data);
|
|
873
|
+
await node.decode();
|
|
874
|
+
console.log(data);
|
|
875
|
+
const paths = [];
|
|
876
|
+
if (node.decoded?.links.length === 0)
|
|
877
|
+
throw new Error(`${hash} is a file`);
|
|
878
|
+
for (const { path, hash } of node.decoded.links) {
|
|
879
|
+
paths.push({ path, hash });
|
|
880
|
+
}
|
|
881
|
+
if (options?.pin)
|
|
882
|
+
await dataStore.put(hash, node.encoded);
|
|
883
|
+
return paths;
|
|
884
|
+
}
|
|
885
|
+
async cat(hash, options) {
|
|
886
|
+
let data;
|
|
887
|
+
const has = await dataStore.has(hash);
|
|
888
|
+
data = has ? await dataStore.get(hash) : await this.requestData(hash, 'data');
|
|
889
|
+
const node = await new peernet.protos['peernet-file'](data);
|
|
890
|
+
if (node.decoded?.links.length > 0)
|
|
891
|
+
throw new Error(`${hash} is a directory`);
|
|
892
|
+
if (options?.pin)
|
|
893
|
+
await dataStore.put(hash, node.encoded);
|
|
894
|
+
return node.decoded.content;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* goes trough given stores and tries to find data for given hash
|
|
898
|
+
* @param {Array} stores
|
|
899
|
+
* @param {string} hash
|
|
900
|
+
*/
|
|
901
|
+
async whichStore(stores, hash) {
|
|
902
|
+
let store = stores.pop();
|
|
903
|
+
store = globalThis[`${store}Store`];
|
|
904
|
+
if (store) {
|
|
905
|
+
const has = await store.has(hash);
|
|
906
|
+
if (has)
|
|
907
|
+
return store;
|
|
908
|
+
if (stores.length > 0)
|
|
909
|
+
return this.whichStore(stores, hash);
|
|
910
|
+
}
|
|
911
|
+
else
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get content for given hash
|
|
916
|
+
*
|
|
917
|
+
* @param {String} hash - the hash of the wanted data
|
|
918
|
+
* @param {String} store - storeName to access
|
|
919
|
+
*/
|
|
920
|
+
async get(hash, store) {
|
|
921
|
+
debug(`get ${hash}`);
|
|
922
|
+
let data;
|
|
923
|
+
if (store)
|
|
924
|
+
store = globalThis[`${store}Store`];
|
|
925
|
+
if (!store)
|
|
926
|
+
store = await this.whichStore([...this.stores], hash);
|
|
927
|
+
if (store && await store.has(hash))
|
|
928
|
+
data = await store.get(hash);
|
|
929
|
+
if (data)
|
|
930
|
+
return data;
|
|
931
|
+
return this.requestData(hash, store?.name || store);
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* put content
|
|
935
|
+
*
|
|
936
|
+
* @param {String} hash
|
|
937
|
+
* @param {Buffer} data
|
|
938
|
+
* @param {String} store - storeName to access
|
|
939
|
+
*/
|
|
940
|
+
async put(hash, data, store = 'data') {
|
|
941
|
+
store = globalThis[`${store}Store`];
|
|
942
|
+
return store.put(hash, data);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* @param {String} hash
|
|
946
|
+
* @return {Boolean}
|
|
947
|
+
*/
|
|
948
|
+
async has(hash) {
|
|
949
|
+
const store = await this.whichStore([...this.stores], hash);
|
|
950
|
+
if (store) {
|
|
951
|
+
return store.private ? false : true;
|
|
952
|
+
}
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
*
|
|
957
|
+
* @param {String} topic
|
|
958
|
+
* @param {String|Object|Array|Boolean|Buffer} data
|
|
959
|
+
*/
|
|
960
|
+
async publish(topic, data) {
|
|
961
|
+
// globalSub.publish(topic, data)
|
|
962
|
+
const id = Math.random().toString(36).slice(-12);
|
|
963
|
+
data = await new globalThis.peernet.protos['peernet-ps']({ data, topic });
|
|
964
|
+
for (const peer of this.connections) {
|
|
965
|
+
if (peer.peerId !== this.peerId) {
|
|
966
|
+
const node = await this.prepareMessage(data);
|
|
967
|
+
this.sendMessage(peer, id, node.encoded);
|
|
968
|
+
}
|
|
969
|
+
// TODO: if peer subscribed
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
createHash(data, name) {
|
|
973
|
+
return new CodeHash(data, { name });
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
*
|
|
977
|
+
* @param {String} topic
|
|
978
|
+
* @param {Method} cb
|
|
979
|
+
*/
|
|
980
|
+
async subscribe(topic, callback) {
|
|
981
|
+
// TODO: if peer subscribed
|
|
982
|
+
globalSub.subscribe(topic, callback);
|
|
983
|
+
}
|
|
984
|
+
async removePeer(peer) {
|
|
985
|
+
return this.client.removePeer(peer);
|
|
986
|
+
}
|
|
987
|
+
get Buffer() {
|
|
988
|
+
return Buffer;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
990
991
|
globalThis.Peernet = Peernet;
|
|
991
992
|
|
|
992
993
|
export { Peernet as default };
|