@dignetwork/dig-sdk 0.0.1-alpha.15 → 0.0.1-alpha.150
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +11 -2
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +117 -58
- package/dist/DigNetwork/ContentServer.d.ts +12 -5
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +172 -24
- package/dist/DigNetwork/DigNetwork.d.ts +15 -9
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +146 -189
- package/dist/DigNetwork/DigPeer.d.ts +8 -3
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +68 -115
- package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -1
- package/dist/DigNetwork/IncentiveServer.js +6 -5
- package/dist/DigNetwork/PropagationServer.d.ts +83 -33
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +605 -356
- package/dist/blockchain/DataStore.d.ts +25 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +114 -159
- package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
- package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.d.ts +94 -10
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +325 -100
- package/dist/blockchain/ServerCoin.d.ts +13 -5
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +90 -28
- package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
- package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
- package/dist/blockchain/StoreMonitorRegistry.js +241 -0
- package/dist/blockchain/Wallet.d.ts +2 -2
- package/dist/blockchain/Wallet.d.ts.map +1 -1
- package/dist/blockchain/Wallet.js +50 -18
- package/dist/blockchain/coins.d.ts +1 -2
- package/dist/blockchain/coins.d.ts.map +1 -1
- package/dist/blockchain/coins.js +1 -55
- package/dist/blockchain/index.d.ts +1 -0
- package/dist/blockchain/index.d.ts.map +1 -1
- package/dist/blockchain/index.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/ContentScanner.d.ts +63 -0
- package/dist/utils/ContentScanner.d.ts.map +1 -0
- package/dist/utils/ContentScanner.js +175 -0
- package/dist/utils/Environment.d.ts +21 -0
- package/dist/utils/Environment.d.ts.map +1 -0
- package/dist/utils/Environment.js +92 -0
- package/dist/utils/FileCache.d.ts +1 -1
- package/dist/utils/FileCache.d.ts.map +1 -1
- package/dist/utils/FileCache.js +2 -2
- package/dist/utils/FileTransfer.d.ts +47 -0
- package/dist/utils/FileTransfer.d.ts.map +1 -0
- package/dist/utils/FileTransfer.js +209 -0
- package/dist/utils/NconfManager.js +2 -1
- package/dist/utils/PeerRanker.d.ts +53 -0
- package/dist/utils/PeerRanker.d.ts.map +1 -0
- package/dist/utils/PeerRanker.js +171 -0
- package/dist/utils/StoreArchiveManager.d.ts +45 -0
- package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
- package/dist/utils/StoreArchiveManager.js +153 -0
- package/dist/utils/config.d.ts +4 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -7
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/dist/utils/directoryUtils.js +9 -12
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/merkle.d.ts +2 -0
- package/dist/utils/merkle.d.ts.map +1 -0
- package/dist/utils/merkle.js +28 -0
- package/dist/utils/network.d.ts +1 -0
- package/dist/utils/network.d.ts.map +1 -1
- package/dist/utils/network.js +38 -11
- package/dist/utils/promiseUtils.d.ts +19 -0
- package/dist/utils/promiseUtils.d.ts.map +1 -0
- package/dist/utils/promiseUtils.js +43 -0
- package/dist/utils/ssl.js +2 -2
- package/package.json +17 -5
- package/dist/utils/deltaUtils.d.ts +0 -2
- package/dist/utils/deltaUtils.d.ts.map +0 -1
- package/dist/utils/deltaUtils.js +0 -83
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Readable } from "stream";
|
|
2
|
+
export declare class FileTransfer {
|
|
3
|
+
private static certPath;
|
|
4
|
+
private static keyPath;
|
|
5
|
+
constructor();
|
|
6
|
+
/** ------------------------ UPLOAD FUNCTIONALITY ------------------------ **/
|
|
7
|
+
/**
|
|
8
|
+
* Upload a file to the server using a stream.
|
|
9
|
+
* @param filePath - Local path of the file.
|
|
10
|
+
* @param uploadUrl - Server URL to upload the file.
|
|
11
|
+
* @param headers - Additional headers for the request.
|
|
12
|
+
* @returns Promise<void>
|
|
13
|
+
*/
|
|
14
|
+
uploadFile(filePath: string, uploadUrl: string, headers: Record<string, string>): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Perform a POST request.
|
|
17
|
+
* @param url - The URL to POST to.
|
|
18
|
+
* @param data - Data to send.
|
|
19
|
+
* @param headers - Headers for the POST request.
|
|
20
|
+
* @returns Promise<void>
|
|
21
|
+
*/
|
|
22
|
+
postRequest(url: string, data: string, headers: Record<string, string>): Promise<void>;
|
|
23
|
+
/** ------------------------ DOWNLOAD FUNCTIONALITY ------------------------ **/
|
|
24
|
+
/**
|
|
25
|
+
* Download data from a URL.
|
|
26
|
+
* @param url - The URL to download data from.
|
|
27
|
+
* @returns Promise<string>
|
|
28
|
+
*/
|
|
29
|
+
downloadData(url: string): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Stream data from a URL.
|
|
32
|
+
* @param url - The URL to stream data from.
|
|
33
|
+
* @returns Promise<Readable>
|
|
34
|
+
*/
|
|
35
|
+
streamData(url: string): Promise<Readable>;
|
|
36
|
+
/** ------------------------ HEAD REQUEST FUNCTIONALITY ------------------------ **/
|
|
37
|
+
/**
|
|
38
|
+
* Perform a HEAD request to check if a resource exists on the server.
|
|
39
|
+
* @param url - The URL to check.
|
|
40
|
+
* @returns Promise<{ success: boolean; headers?: Record<string, string> }>
|
|
41
|
+
*/
|
|
42
|
+
headRequest(url: string): Promise<{
|
|
43
|
+
success: boolean;
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=FileTransfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileTransfer.d.ts","sourceRoot":"","sources":["../../src/utils/FileTransfer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;;IAU/B,8EAA8E;IAE9E;;;;;;OAMG;IACU,UAAU,CACrB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IA0ChB;;;;;;OAMG;IACU,WAAW,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAsChB,gFAAgF;IAEhF;;;;OAIG;IACU,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0CvD;;;;OAIG;IACU,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuCvD,oFAAoF;IAEpF;;;;OAIG;IACU,WAAW,CACtB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CA8BnE"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileTransfer = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const https_1 = __importDefault(require("https"));
|
|
9
|
+
const url_1 = require("url");
|
|
10
|
+
const ssl_1 = require("../utils/ssl");
|
|
11
|
+
class FileTransfer {
|
|
12
|
+
constructor() {
|
|
13
|
+
if (!FileTransfer.certPath || !FileTransfer.keyPath) {
|
|
14
|
+
const { certPath, keyPath } = (0, ssl_1.getOrCreateSSLCerts)();
|
|
15
|
+
FileTransfer.certPath = certPath;
|
|
16
|
+
FileTransfer.keyPath = keyPath;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** ------------------------ UPLOAD FUNCTIONALITY ------------------------ **/
|
|
20
|
+
/**
|
|
21
|
+
* Upload a file to the server using a stream.
|
|
22
|
+
* @param filePath - Local path of the file.
|
|
23
|
+
* @param uploadUrl - Server URL to upload the file.
|
|
24
|
+
* @param headers - Additional headers for the request.
|
|
25
|
+
* @returns Promise<void>
|
|
26
|
+
*/
|
|
27
|
+
async uploadFile(filePath, uploadUrl, headers) {
|
|
28
|
+
const fileStream = fs_1.default.createReadStream(filePath);
|
|
29
|
+
const fileSize = fs_1.default.statSync(filePath).size;
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const url = new url_1.URL(uploadUrl);
|
|
32
|
+
const options = {
|
|
33
|
+
hostname: url.hostname,
|
|
34
|
+
port: url.port || 443,
|
|
35
|
+
path: url.pathname,
|
|
36
|
+
method: "PUT",
|
|
37
|
+
headers: {
|
|
38
|
+
...headers,
|
|
39
|
+
"Content-Type": "application/octet-stream",
|
|
40
|
+
"Content-Length": fileSize,
|
|
41
|
+
},
|
|
42
|
+
key: fs_1.default.readFileSync(FileTransfer.keyPath),
|
|
43
|
+
cert: fs_1.default.readFileSync(FileTransfer.certPath),
|
|
44
|
+
rejectUnauthorized: false,
|
|
45
|
+
};
|
|
46
|
+
const req = https_1.default.request(options, (res) => {
|
|
47
|
+
if (res.statusCode === 200) {
|
|
48
|
+
resolve();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
reject(new Error(`Upload failed with status ${res.statusCode}: ${res.statusMessage}`));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
req.on("error", (err) => reject(err));
|
|
55
|
+
fileStream.pipe(req);
|
|
56
|
+
fileStream.on("error", (err) => reject(err));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Perform a POST request.
|
|
61
|
+
* @param url - The URL to POST to.
|
|
62
|
+
* @param data - Data to send.
|
|
63
|
+
* @param headers - Headers for the POST request.
|
|
64
|
+
* @returns Promise<void>
|
|
65
|
+
*/
|
|
66
|
+
async postRequest(url, data, headers) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const urlObj = new url_1.URL(url);
|
|
69
|
+
const options = {
|
|
70
|
+
hostname: urlObj.hostname,
|
|
71
|
+
port: urlObj.port || 443,
|
|
72
|
+
path: urlObj.pathname,
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: {
|
|
75
|
+
...headers,
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
"Content-Length": Buffer.byteLength(data),
|
|
78
|
+
},
|
|
79
|
+
key: fs_1.default.readFileSync(FileTransfer.keyPath),
|
|
80
|
+
cert: fs_1.default.readFileSync(FileTransfer.certPath),
|
|
81
|
+
rejectUnauthorized: false,
|
|
82
|
+
};
|
|
83
|
+
const req = https_1.default.request(options, (res) => {
|
|
84
|
+
if (res.statusCode === 200) {
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
reject(new Error(`POST request failed with status ${res.statusCode}: ${res.statusMessage}`));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
req.on("error", (err) => reject(err));
|
|
92
|
+
req.write(data);
|
|
93
|
+
req.end();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** ------------------------ DOWNLOAD FUNCTIONALITY ------------------------ **/
|
|
97
|
+
/**
|
|
98
|
+
* Download data from a URL.
|
|
99
|
+
* @param url - The URL to download data from.
|
|
100
|
+
* @returns Promise<string>
|
|
101
|
+
*/
|
|
102
|
+
async downloadData(url) {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
const urlObj = new url_1.URL(url);
|
|
105
|
+
const options = {
|
|
106
|
+
hostname: urlObj.hostname,
|
|
107
|
+
port: urlObj.port || 443,
|
|
108
|
+
path: urlObj.pathname + urlObj.search,
|
|
109
|
+
method: "GET",
|
|
110
|
+
key: fs_1.default.readFileSync(FileTransfer.keyPath),
|
|
111
|
+
cert: fs_1.default.readFileSync(FileTransfer.certPath),
|
|
112
|
+
rejectUnauthorized: false,
|
|
113
|
+
};
|
|
114
|
+
const req = https_1.default.request(options, (res) => {
|
|
115
|
+
let data = "";
|
|
116
|
+
if (res.statusCode === 200) {
|
|
117
|
+
res.on("data", (chunk) => (data += chunk));
|
|
118
|
+
res.on("end", () => resolve(data));
|
|
119
|
+
}
|
|
120
|
+
else if (res.statusCode === 301 || res.statusCode === 302) {
|
|
121
|
+
const redirectUrl = res.headers.location;
|
|
122
|
+
if (redirectUrl) {
|
|
123
|
+
this.downloadData(redirectUrl).then(resolve).catch(reject);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
reject(new Error("Redirected without a location header"));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
reject(new Error(`Failed to retrieve data from ${url}. Status code: ${res.statusCode}`));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
req.on("error", (error) => reject(error));
|
|
134
|
+
req.end();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Stream data from a URL.
|
|
139
|
+
* @param url - The URL to stream data from.
|
|
140
|
+
* @returns Promise<Readable>
|
|
141
|
+
*/
|
|
142
|
+
async streamData(url) {
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
const urlObj = new url_1.URL(url);
|
|
145
|
+
const options = {
|
|
146
|
+
hostname: urlObj.hostname,
|
|
147
|
+
port: urlObj.port || 443,
|
|
148
|
+
path: urlObj.pathname + urlObj.search,
|
|
149
|
+
method: "GET",
|
|
150
|
+
key: fs_1.default.readFileSync(FileTransfer.keyPath),
|
|
151
|
+
cert: fs_1.default.readFileSync(FileTransfer.certPath),
|
|
152
|
+
rejectUnauthorized: false,
|
|
153
|
+
};
|
|
154
|
+
const req = https_1.default.request(options, (res) => {
|
|
155
|
+
if (res.statusCode === 200) {
|
|
156
|
+
resolve(res); // Return the readable stream
|
|
157
|
+
}
|
|
158
|
+
else if (res.statusCode === 301 || res.statusCode === 302) {
|
|
159
|
+
const redirectUrl = res.headers.location;
|
|
160
|
+
if (redirectUrl) {
|
|
161
|
+
this.streamData(redirectUrl).then(resolve).catch(reject);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
reject(new Error("Redirected without a location header"));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
reject(new Error(`Failed to retrieve stream from ${url}. Status code: ${res.statusCode}`));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
req.on("error", (error) => reject(error));
|
|
172
|
+
req.end();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** ------------------------ HEAD REQUEST FUNCTIONALITY ------------------------ **/
|
|
176
|
+
/**
|
|
177
|
+
* Perform a HEAD request to check if a resource exists on the server.
|
|
178
|
+
* @param url - The URL to check.
|
|
179
|
+
* @returns Promise<{ success: boolean; headers?: Record<string, string> }>
|
|
180
|
+
*/
|
|
181
|
+
async headRequest(url) {
|
|
182
|
+
return new Promise((resolve, reject) => {
|
|
183
|
+
const urlObj = new url_1.URL(url);
|
|
184
|
+
const options = {
|
|
185
|
+
hostname: urlObj.hostname,
|
|
186
|
+
port: urlObj.port || 443,
|
|
187
|
+
path: urlObj.pathname,
|
|
188
|
+
method: "HEAD",
|
|
189
|
+
key: fs_1.default.readFileSync(FileTransfer.keyPath),
|
|
190
|
+
cert: fs_1.default.readFileSync(FileTransfer.certPath),
|
|
191
|
+
rejectUnauthorized: false,
|
|
192
|
+
};
|
|
193
|
+
const req = https_1.default.request(options, (res) => {
|
|
194
|
+
if (res.statusCode === 200) {
|
|
195
|
+
resolve({
|
|
196
|
+
success: true,
|
|
197
|
+
headers: res.headers,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
resolve({ success: false });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
req.on("error", (err) => reject(err));
|
|
205
|
+
req.end();
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.FileTransfer = FileTransfer;
|
|
@@ -8,7 +8,8 @@ const nconf_1 = __importDefault(require("nconf"));
|
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const config_1 = require("./config");
|
|
11
|
-
const
|
|
11
|
+
const Environment_1 = require("./Environment");
|
|
12
|
+
const CONF_FOLDER_PATH = Environment_1.Environment.DIG_FOLDER_PATH || config_1.USER_DIR_PATH;
|
|
12
13
|
class NconfManager {
|
|
13
14
|
constructor(relativePath) {
|
|
14
15
|
this.configFilePath = path_1.default.join(CONF_FOLDER_PATH, relativePath);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface PeerMetrics {
|
|
2
|
+
ip: string;
|
|
3
|
+
latency: number;
|
|
4
|
+
bandwidth: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
|
|
8
|
+
*/
|
|
9
|
+
export declare class PeerRanker {
|
|
10
|
+
private ipAddresses;
|
|
11
|
+
private static certPath;
|
|
12
|
+
private static keyPath;
|
|
13
|
+
private timeout;
|
|
14
|
+
private uploadTestSize;
|
|
15
|
+
private sortedPeers;
|
|
16
|
+
private currentIndex;
|
|
17
|
+
/**
|
|
18
|
+
* Constructs a PeerRanker instance.
|
|
19
|
+
* @param ipAddresses - Array of IP addresses to rank.
|
|
20
|
+
*/
|
|
21
|
+
constructor(ipAddresses: string[], timeout?: number, uploadTestSize?: number);
|
|
22
|
+
/**
|
|
23
|
+
* Measures the latency of a given IP address using an HTTPS request.
|
|
24
|
+
* Tries HEAD first, then falls back to GET if HEAD is not supported.
|
|
25
|
+
* @param ip - The IP address of the peer.
|
|
26
|
+
* @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
|
|
27
|
+
*/
|
|
28
|
+
private measureLatency;
|
|
29
|
+
/**
|
|
30
|
+
* Measures the upload bandwidth of a given IP address by sending random data.
|
|
31
|
+
* @param ip - The IP address of the peer.
|
|
32
|
+
* @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
|
|
33
|
+
*/
|
|
34
|
+
private measureBandwidth;
|
|
35
|
+
/**
|
|
36
|
+
* Ranks the peers based on measured latency and upload bandwidth.
|
|
37
|
+
* Unresponsive peers are excluded from the final ranking.
|
|
38
|
+
* @param cooldown - Cooldown time in milliseconds between batches.
|
|
39
|
+
* @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
|
|
40
|
+
*/
|
|
41
|
+
rankPeers(cooldown?: number): Promise<PeerMetrics[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Returns the next best peer based on the ranking.
|
|
44
|
+
* Cycles through the list, resetting to the beginning once the end is reached.
|
|
45
|
+
* @returns The next PeerMetrics object or null if no peers are available.
|
|
46
|
+
*/
|
|
47
|
+
GetNextBestPeer(): PeerMetrics | null;
|
|
48
|
+
/**
|
|
49
|
+
* Resets the internal iterator to start from the beginning of the sorted list.
|
|
50
|
+
*/
|
|
51
|
+
resetIterator(): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=PeerRanker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PeerRanker.d.ts","sourceRoot":"","sources":["../../src/utils/PeerRanker.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAG/B,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,YAAY,CAAa;IAEjC;;;OAGG;gBACS,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,MAAa,EAAE,cAAc,GAAE,MAAoB;IAW/F;;;;;OAKG;YACW,cAAc;IA2C5B;;;;OAIG;YACW,gBAAgB;IAmC9B;;;;;OAKG;IACU,SAAS,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAmCtE;;;;OAIG;IACI,eAAe,IAAI,WAAW,GAAG,IAAI;IAW5C;;OAEG;IACI,aAAa,IAAI,IAAI;CAG7B"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PeerRanker = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const https_1 = __importDefault(require("https"));
|
|
10
|
+
const ssl_1 = require("./ssl");
|
|
11
|
+
const promiseUtils_1 = require("./promiseUtils");
|
|
12
|
+
/**
|
|
13
|
+
* Utility class to rank peers based on latency and upload bandwidth using HTTPS with mTLS.
|
|
14
|
+
*/
|
|
15
|
+
class PeerRanker {
|
|
16
|
+
/**
|
|
17
|
+
* Constructs a PeerRanker instance.
|
|
18
|
+
* @param ipAddresses - Array of IP addresses to rank.
|
|
19
|
+
*/
|
|
20
|
+
constructor(ipAddresses, timeout = 5000, uploadTestSize = 1024 * 1024) {
|
|
21
|
+
// Internal properties for iteration
|
|
22
|
+
this.sortedPeers = [];
|
|
23
|
+
this.currentIndex = 0;
|
|
24
|
+
this.ipAddresses = ipAddresses;
|
|
25
|
+
this.timeout = timeout; // Allow customizable timeout
|
|
26
|
+
this.uploadTestSize = uploadTestSize; // Default upload size: 1MB
|
|
27
|
+
// Fetch the SSL certificates used for mTLS.
|
|
28
|
+
const { certPath, keyPath } = (0, ssl_1.getOrCreateSSLCerts)();
|
|
29
|
+
PeerRanker.certPath = certPath;
|
|
30
|
+
PeerRanker.keyPath = keyPath;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Measures the latency of a given IP address using an HTTPS request.
|
|
34
|
+
* Tries HEAD first, then falls back to GET if HEAD is not supported.
|
|
35
|
+
* @param ip - The IP address of the peer.
|
|
36
|
+
* @returns Promise resolving to the latency in milliseconds or rejecting if the peer fails.
|
|
37
|
+
*/
|
|
38
|
+
async measureLatency(ip) {
|
|
39
|
+
const url = `https://${ip}:4159/diagnostics/ping`;
|
|
40
|
+
const configHead = {
|
|
41
|
+
url: url,
|
|
42
|
+
method: 'HEAD',
|
|
43
|
+
httpsAgent: new https_1.default.Agent({
|
|
44
|
+
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
45
|
+
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
46
|
+
rejectUnauthorized: false,
|
|
47
|
+
}),
|
|
48
|
+
timeout: this.timeout,
|
|
49
|
+
validateStatus: (status) => status < 500,
|
|
50
|
+
};
|
|
51
|
+
const startTime = Date.now();
|
|
52
|
+
try {
|
|
53
|
+
const response = await (0, axios_1.default)(configHead);
|
|
54
|
+
if (response.status === 405) {
|
|
55
|
+
const configGet = {
|
|
56
|
+
url: url,
|
|
57
|
+
method: 'GET',
|
|
58
|
+
httpsAgent: new https_1.default.Agent({
|
|
59
|
+
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
60
|
+
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
61
|
+
rejectUnauthorized: false,
|
|
62
|
+
}),
|
|
63
|
+
timeout: this.timeout,
|
|
64
|
+
headers: {
|
|
65
|
+
'Range': 'bytes=0-0',
|
|
66
|
+
},
|
|
67
|
+
validateStatus: (status) => status < 500,
|
|
68
|
+
};
|
|
69
|
+
await (0, axios_1.default)(configGet);
|
|
70
|
+
}
|
|
71
|
+
const latency = Date.now() - startTime;
|
|
72
|
+
return latency;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Latency measurement failed for IP ${ip}:`, error.message);
|
|
76
|
+
throw new Error(`Latency measurement failed for IP ${ip}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Measures the upload bandwidth of a given IP address by sending random data.
|
|
81
|
+
* @param ip - The IP address of the peer.
|
|
82
|
+
* @returns Promise resolving to the upload bandwidth in bytes per second or rejecting if the peer fails.
|
|
83
|
+
*/
|
|
84
|
+
async measureBandwidth(ip) {
|
|
85
|
+
const url = `https://${ip}:4159/diagnostics/bandwidth`;
|
|
86
|
+
const randomData = Buffer.alloc(this.uploadTestSize, 'a');
|
|
87
|
+
const config = {
|
|
88
|
+
url: url,
|
|
89
|
+
method: 'POST',
|
|
90
|
+
data: randomData,
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': 'application/octet-stream',
|
|
93
|
+
'Content-Length': this.uploadTestSize,
|
|
94
|
+
},
|
|
95
|
+
httpsAgent: new https_1.default.Agent({
|
|
96
|
+
cert: fs_1.default.readFileSync(PeerRanker.certPath),
|
|
97
|
+
key: fs_1.default.readFileSync(PeerRanker.keyPath),
|
|
98
|
+
rejectUnauthorized: false,
|
|
99
|
+
}),
|
|
100
|
+
timeout: this.timeout,
|
|
101
|
+
maxContentLength: Infinity,
|
|
102
|
+
maxBodyLength: Infinity,
|
|
103
|
+
};
|
|
104
|
+
const startTime = Date.now();
|
|
105
|
+
try {
|
|
106
|
+
await (0, axios_1.default)(config);
|
|
107
|
+
const timeElapsed = (Date.now() - startTime) / 1000;
|
|
108
|
+
const bandwidth = this.uploadTestSize / timeElapsed;
|
|
109
|
+
return bandwidth;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(`Bandwidth measurement failed for IP ${ip}:`, error.message);
|
|
113
|
+
throw new Error(`Bandwidth measurement failed for IP ${ip}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Ranks the peers based on measured latency and upload bandwidth.
|
|
118
|
+
* Unresponsive peers are excluded from the final ranking.
|
|
119
|
+
* @param cooldown - Cooldown time in milliseconds between batches.
|
|
120
|
+
* @returns Promise resolving to an array of PeerMetrics sorted by latency and bandwidth.
|
|
121
|
+
*/
|
|
122
|
+
async rankPeers(cooldown = 500) {
|
|
123
|
+
const limit = 5; // Limit to 5 parallel requests at a time
|
|
124
|
+
const iteratorFn = async (ip) => {
|
|
125
|
+
try {
|
|
126
|
+
const [latency, bandwidth] = await Promise.all([
|
|
127
|
+
this.measureLatency(ip),
|
|
128
|
+
this.measureBandwidth(ip),
|
|
129
|
+
]);
|
|
130
|
+
return { ip, latency, bandwidth };
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// Peer failed, skip it by returning null
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
// Process all peers with a concurrency limit and cooldown between batches
|
|
138
|
+
const peerMetrics = (await (0, promiseUtils_1.asyncPool)(limit, this.ipAddresses, iteratorFn, cooldown)).filter((metrics) => metrics !== null); // Use a type guard
|
|
139
|
+
// Sort by lowest latency first, then by highest bandwidth
|
|
140
|
+
peerMetrics.sort((a, b) => {
|
|
141
|
+
if (a.latency === b.latency) {
|
|
142
|
+
return b.bandwidth - a.bandwidth; // Higher bandwidth is better
|
|
143
|
+
}
|
|
144
|
+
return a.latency - b.latency; // Lower latency is better
|
|
145
|
+
});
|
|
146
|
+
this.sortedPeers = peerMetrics;
|
|
147
|
+
this.currentIndex = 0;
|
|
148
|
+
return peerMetrics;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Returns the next best peer based on the ranking.
|
|
152
|
+
* Cycles through the list, resetting to the beginning once the end is reached.
|
|
153
|
+
* @returns The next PeerMetrics object or null if no peers are available.
|
|
154
|
+
*/
|
|
155
|
+
GetNextBestPeer() {
|
|
156
|
+
if (this.sortedPeers.length === 0) {
|
|
157
|
+
console.warn('Peer list is empty. Please run rankPeers() first.');
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const peer = this.sortedPeers[this.currentIndex];
|
|
161
|
+
this.currentIndex = (this.currentIndex + 1) % this.sortedPeers.length;
|
|
162
|
+
return peer;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resets the internal iterator to start from the beginning of the sorted list.
|
|
166
|
+
*/
|
|
167
|
+
resetIterator() {
|
|
168
|
+
this.currentIndex = 0;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.PeerRanker = PeerRanker;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class for managing file archiving, extraction, and working with the 'data' folder.
|
|
3
|
+
*/
|
|
4
|
+
declare class StoreArchiveManager {
|
|
5
|
+
private archivePath;
|
|
6
|
+
private outputStream;
|
|
7
|
+
/**
|
|
8
|
+
* Constructor that initializes the FileArchiveManager with a given path.
|
|
9
|
+
* @param archivePath - The path where the archive will be created.
|
|
10
|
+
*/
|
|
11
|
+
constructor(archivePath: string);
|
|
12
|
+
/**
|
|
13
|
+
* Creates the archive at the specified path.
|
|
14
|
+
* @returns Promise<void>
|
|
15
|
+
*/
|
|
16
|
+
createArchive(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Streams a file into the archive.
|
|
19
|
+
* @param filePath - The file to add to the archive.
|
|
20
|
+
* @param fileNameInArchive - The name the file should have inside the archive.
|
|
21
|
+
* @returns Promise<void>
|
|
22
|
+
*/
|
|
23
|
+
addFileToArchive(filePath: string, fileNameInArchive: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Extracts a file from the archive to a specified directory.
|
|
26
|
+
* @param extractToPath - The path where the file should be extracted.
|
|
27
|
+
* @returns Promise<void>
|
|
28
|
+
*/
|
|
29
|
+
extractArchive(extractToPath: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Merges another archive into the current one.
|
|
32
|
+
* Extracts files from the given archive and adds them into the current archive.
|
|
33
|
+
* @param archiveToMergePath - The path of the archive to be merged.
|
|
34
|
+
* @returns Promise<void>
|
|
35
|
+
*/
|
|
36
|
+
mergeArchive(archiveToMergePath: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Adds extracted files to the current archive.
|
|
39
|
+
* @param extractedFolderPath - The path of the folder containing extracted files.
|
|
40
|
+
* @returns Promise<void>
|
|
41
|
+
*/
|
|
42
|
+
private addExtractedFilesToArchive;
|
|
43
|
+
}
|
|
44
|
+
export default StoreArchiveManager;
|
|
45
|
+
//# sourceMappingURL=StoreArchiveManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreArchiveManager.d.ts","sourceRoot":"","sources":["../../src/utils/StoreArchiveManager.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,cAAM,mBAAmB;IACrB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAwB;IAE5C;;;OAGG;gBACS,WAAW,EAAE,MAAM;IAK/B;;;OAGG;IACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3C;;;;;OAKG;IACU,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBzF;;;;OAIG;IACU,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjE;;;;;OAKG;IACU,YAAY,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCpE;;;;OAIG;YACW,0BAA0B;CAkB3C;AAED,eAAe,mBAAmB,CAAC"}
|