@dignetwork/dig-sdk 0.0.1-alpha.97 → 0.0.1-alpha.99
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/DigNetwork/ContentServer.d.ts +4 -6
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +66 -198
- package/dist/DigNetwork/PropagationServer.d.ts +6 -0
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +45 -18
- package/dist/blockchain/FullNodePeer.d.ts +7 -1
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +35 -10
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +4 -1
- package/package.json +1 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import http from "http";
|
|
2
|
-
import { Readable } from "stream";
|
|
3
1
|
export declare class ContentServer {
|
|
4
2
|
private ipAddress;
|
|
5
3
|
private storeId;
|
|
6
4
|
private static certPath;
|
|
7
5
|
private static keyPath;
|
|
8
6
|
private static readonly port;
|
|
7
|
+
private static readonly inactivityTimeout;
|
|
9
8
|
constructor(ipAddress: string, storeId: string);
|
|
10
9
|
getKey(key: string, rootHash: string, challengeHex?: string): Promise<string>;
|
|
11
10
|
getPaymentAddress(): Promise<string | null>;
|
|
@@ -15,19 +14,18 @@ export declare class ContentServer {
|
|
|
15
14
|
getKeysIndex(rootHash?: string): Promise<any>;
|
|
16
15
|
headKey(key: string, rootHash?: string): Promise<{
|
|
17
16
|
success: boolean;
|
|
18
|
-
headers?:
|
|
17
|
+
headers?: any;
|
|
19
18
|
}>;
|
|
20
19
|
headStore(options?: {
|
|
21
20
|
hasRootHash: string;
|
|
22
21
|
}): Promise<{
|
|
23
22
|
success: boolean;
|
|
24
|
-
headers?:
|
|
23
|
+
headers?: any;
|
|
25
24
|
}>;
|
|
26
25
|
hasRootHash(rootHash: string): Promise<boolean>;
|
|
27
|
-
streamKey(key: string, rootHash?: string): Promise<Readable>;
|
|
28
26
|
private head;
|
|
29
27
|
private fetchJson;
|
|
30
|
-
private fetchWithRetries;
|
|
31
28
|
private fetch;
|
|
29
|
+
private getAxiosConfig;
|
|
32
30
|
}
|
|
33
31
|
//# sourceMappingURL=ContentServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AAMA,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,MAAM,CACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IAWL,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAc3C,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAU7C,OAAO,CAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAUlC,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACjE,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,GAAG,CAAC;KACf,CAAC;IAQW,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAW9C,IAAI;YAcJ,SAAS;YAMT,KAAK;IA+DnB,OAAO,CAAC,cAAc;CAUvB"}
|
|
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ContentServer = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const
|
|
9
|
-
const url_1 = require("url");
|
|
9
|
+
const https_1 = __importDefault(require("https"));
|
|
10
10
|
const ssl_1 = require("../utils/ssl");
|
|
11
11
|
class ContentServer {
|
|
12
12
|
constructor(ipAddress, storeId) {
|
|
@@ -20,13 +20,11 @@ class ContentServer {
|
|
|
20
20
|
}
|
|
21
21
|
// Method to get the content of a specified key from the peer, with optional challenge query
|
|
22
22
|
async getKey(key, rootHash, challengeHex) {
|
|
23
|
-
// Construct the base URL
|
|
24
23
|
let url = `https://${this.ipAddress}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
|
|
25
|
-
// If a challenge is provided, append it as a query parameter
|
|
26
24
|
if (challengeHex) {
|
|
27
25
|
url += `?challenge=${challengeHex}`;
|
|
28
26
|
}
|
|
29
|
-
return this.
|
|
27
|
+
return this.fetch(url);
|
|
30
28
|
}
|
|
31
29
|
// Method to get the payment address from the peer
|
|
32
30
|
async getPaymentAddress() {
|
|
@@ -90,219 +88,89 @@ class ContentServer {
|
|
|
90
88
|
}
|
|
91
89
|
return false;
|
|
92
90
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const requestOptions = {
|
|
102
|
-
hostname: urlObj.hostname,
|
|
103
|
-
port: urlObj.port || ContentServer.port,
|
|
104
|
-
path: urlObj.pathname + urlObj.search,
|
|
105
|
-
method: "GET",
|
|
91
|
+
// Helper method to perform HEAD requests using axios
|
|
92
|
+
async head(url) {
|
|
93
|
+
try {
|
|
94
|
+
const config = this.getAxiosConfig();
|
|
95
|
+
const response = await axios_1.default.head(url, config);
|
|
96
|
+
return {
|
|
97
|
+
success: response.status >= 200 && response.status < 300,
|
|
98
|
+
headers: response.headers,
|
|
106
99
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
else if (response.statusCode === 301 || response.statusCode === 302) {
|
|
112
|
-
// Handle redirects
|
|
113
|
-
const redirectUrl = response.headers.location;
|
|
114
|
-
if (redirectUrl) {
|
|
115
|
-
this.streamKey(redirectUrl).then(resolve).catch(reject);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
reject(new Error("Redirected without a location header"));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
request.on("error", (error) => {
|
|
126
|
-
console.error(`GET Request error for ${url}:`, error);
|
|
127
|
-
reject(error);
|
|
128
|
-
});
|
|
129
|
-
request.end();
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
// Helper method to perform HEAD requests
|
|
133
|
-
async head(url, maxRedirects = 5) {
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
try {
|
|
136
|
-
// Parse the input URL
|
|
137
|
-
const urlObj = new url_1.URL(url);
|
|
138
|
-
const requestOptions = {
|
|
139
|
-
hostname: urlObj.hostname,
|
|
140
|
-
port: urlObj.port ||
|
|
141
|
-
(urlObj.protocol === "http:" ? 80 : ContentServer.port),
|
|
142
|
-
path: urlObj.pathname + urlObj.search,
|
|
143
|
-
method: "HEAD",
|
|
144
|
-
key: fs_1.default.readFileSync(ContentServer.keyPath),
|
|
145
|
-
cert: fs_1.default.readFileSync(ContentServer.certPath),
|
|
146
|
-
rejectUnauthorized: false,
|
|
147
|
-
};
|
|
148
|
-
const request = http_1.default.request(requestOptions, (response) => {
|
|
149
|
-
const { statusCode, headers } = response;
|
|
150
|
-
// If status code is 2xx, return success
|
|
151
|
-
if (statusCode && statusCode >= 200 && statusCode < 300) {
|
|
152
|
-
resolve({ success: true, headers });
|
|
153
|
-
}
|
|
154
|
-
// Handle 3xx redirection
|
|
155
|
-
else if (statusCode &&
|
|
156
|
-
statusCode >= 300 &&
|
|
157
|
-
statusCode < 400 &&
|
|
158
|
-
headers.location) {
|
|
159
|
-
if (maxRedirects > 0) {
|
|
160
|
-
let redirectUrl = headers.location;
|
|
161
|
-
// Check if the redirect URL is relative
|
|
162
|
-
if (!/^https?:\/\//i.test(redirectUrl)) {
|
|
163
|
-
// Resolve the relative URL based on the original URL
|
|
164
|
-
redirectUrl = new url_1.URL(redirectUrl, url).toString();
|
|
165
|
-
console.log(`Resolved relative redirect to: ${redirectUrl}`);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
console.log(`Redirecting to: ${redirectUrl}`);
|
|
169
|
-
}
|
|
170
|
-
// Recursively follow the redirection
|
|
171
|
-
this.head(redirectUrl, maxRedirects - 1)
|
|
172
|
-
.then(resolve)
|
|
173
|
-
.catch(reject);
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
reject({ success: false, message: "Too many redirects" });
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
// For other status codes, consider it a failure
|
|
181
|
-
resolve({ success: false });
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
request.on("error", (error) => {
|
|
185
|
-
console.error(`HEAD ${url}:`, error.message);
|
|
186
|
-
reject({ success: false });
|
|
187
|
-
});
|
|
188
|
-
request.end();
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
console.error(`Invalid URL: ${url}`, err);
|
|
192
|
-
reject({ success: false, message: "Invalid URL" });
|
|
193
|
-
}
|
|
194
|
-
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { success: false };
|
|
103
|
+
}
|
|
195
104
|
}
|
|
196
105
|
// Helper method to fetch JSON data from a URL
|
|
197
106
|
async fetchJson(url) {
|
|
198
|
-
const response = await this.
|
|
107
|
+
const response = await this.fetch(url);
|
|
199
108
|
return JSON.parse(response);
|
|
200
109
|
}
|
|
201
|
-
//
|
|
202
|
-
async
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
const maxDelay = 10000; // 10 seconds
|
|
207
|
-
const delayMultiplier = 1.5;
|
|
208
|
-
let delay = initialDelay;
|
|
209
|
-
while (attempt < maxRetries) {
|
|
210
|
-
try {
|
|
211
|
-
return await this.fetch(url);
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
if (attempt < maxRetries - 1) {
|
|
215
|
-
console.warn(`Attempt ${attempt + 1} failed: ${error.message}. Retrying in ${delay / 1000} seconds...`);
|
|
216
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
217
|
-
delay = Math.min(maxDelay, delay * delayMultiplier);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
console.error(`Failed to retrieve data from ${url}. Aborting.`);
|
|
221
|
-
throw new Error(`Failed to retrieve data: ${error.message}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
attempt++;
|
|
225
|
-
}
|
|
226
|
-
throw new Error(`Failed to retrieve data from ${url} after ${maxRetries} attempts.`);
|
|
227
|
-
}
|
|
228
|
-
// Core method to fetch content from a URL with a 5-second inactivity timeout
|
|
229
|
-
async fetch(url, maxRedirects = 5) {
|
|
110
|
+
// Core method to fetch content from a URL with a 5-second inactivity timeout, handling redirects
|
|
111
|
+
async fetch(url) {
|
|
112
|
+
const config = this.getAxiosConfig();
|
|
113
|
+
// Create a cancel token for the inactivity timeout
|
|
114
|
+
const source = axios_1.default.CancelToken.source();
|
|
230
115
|
return new Promise((resolve, reject) => {
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
key: fs_1.default.readFileSync(ContentServer.keyPath),
|
|
240
|
-
cert: fs_1.default.readFileSync(ContentServer.certPath),
|
|
241
|
-
rejectUnauthorized: false,
|
|
116
|
+
let timeout = null;
|
|
117
|
+
const resetTimeout = () => {
|
|
118
|
+
if (timeout) {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
}
|
|
121
|
+
timeout = setTimeout(() => {
|
|
122
|
+
source.cancel(`Request timed out after ${ContentServer.inactivityTimeout / 1000} seconds of inactivity`);
|
|
123
|
+
}, ContentServer.inactivityTimeout);
|
|
242
124
|
};
|
|
243
|
-
|
|
125
|
+
axios_1.default
|
|
126
|
+
.get(url, {
|
|
127
|
+
...config,
|
|
128
|
+
cancelToken: source.token,
|
|
129
|
+
responseType: "stream", // Use stream to track data transfer
|
|
130
|
+
maxRedirects: 5, // Axios follows up to 5 redirects by default
|
|
131
|
+
})
|
|
132
|
+
.then((response) => {
|
|
244
133
|
let data = "";
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}, timeoutDuration);
|
|
251
|
-
const resetTimeout = () => {
|
|
134
|
+
response.data.on("data", (chunk) => {
|
|
135
|
+
data += chunk;
|
|
136
|
+
resetTimeout(); // Reset the timeout when data is received
|
|
137
|
+
});
|
|
138
|
+
response.data.on("end", () => {
|
|
252
139
|
if (timeout) {
|
|
253
|
-
clearTimeout(timeout);
|
|
254
|
-
}
|
|
255
|
-
timeout = setTimeout(() => {
|
|
256
|
-
console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
|
|
257
|
-
request.destroy(); // Use destroy instead of abort
|
|
258
|
-
reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
|
|
259
|
-
}, timeoutDuration);
|
|
260
|
-
};
|
|
261
|
-
if (response.statusCode === 200) {
|
|
262
|
-
response.on("data", (chunk) => {
|
|
263
|
-
data += chunk;
|
|
264
|
-
resetTimeout(); // Reset the timeout every time data is received
|
|
265
|
-
});
|
|
266
|
-
response.on("end", () => {
|
|
267
|
-
if (timeout) {
|
|
268
|
-
clearTimeout(timeout);
|
|
269
|
-
}
|
|
270
|
-
resolve(data);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
else if ((response.statusCode === 301 || response.statusCode === 302) &&
|
|
274
|
-
response.headers.location) {
|
|
275
|
-
// Handle redirects
|
|
276
|
-
if (maxRedirects > 0) {
|
|
277
|
-
const redirectUrl = new url_1.URL(response.headers.location, url); // Resolve relative URLs based on the original URL
|
|
278
|
-
if (timeout) {
|
|
279
|
-
clearTimeout(timeout);
|
|
280
|
-
}
|
|
281
|
-
this.fetch(redirectUrl.toString(), maxRedirects - 1)
|
|
282
|
-
.then(resolve)
|
|
283
|
-
.catch(reject);
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
reject(new Error("Too many redirects"));
|
|
140
|
+
clearTimeout(timeout); // Clear timeout on stream end
|
|
287
141
|
}
|
|
288
|
-
|
|
289
|
-
|
|
142
|
+
resolve(data);
|
|
143
|
+
});
|
|
144
|
+
response.data.on("error", (error) => {
|
|
290
145
|
if (timeout) {
|
|
291
146
|
clearTimeout(timeout);
|
|
292
147
|
}
|
|
293
|
-
reject(
|
|
148
|
+
reject(error);
|
|
149
|
+
});
|
|
150
|
+
})
|
|
151
|
+
.catch((error) => {
|
|
152
|
+
if (axios_1.default.isCancel(error)) {
|
|
153
|
+
console.error("Request canceled:", error.message);
|
|
294
154
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (timeout) {
|
|
298
|
-
clearTimeout(timeout);
|
|
155
|
+
else {
|
|
156
|
+
console.error(`Failed to retrieve data from ${url}:`, error.message);
|
|
299
157
|
}
|
|
300
|
-
console.error(`GET ${url}:`, error.message);
|
|
301
158
|
reject(error);
|
|
302
159
|
});
|
|
303
|
-
request.end();
|
|
304
160
|
});
|
|
305
161
|
}
|
|
162
|
+
// Helper method to configure axios with HTTPS settings
|
|
163
|
+
getAxiosConfig() {
|
|
164
|
+
return {
|
|
165
|
+
httpsAgent: new https_1.default.Agent({
|
|
166
|
+
cert: fs_1.default.readFileSync(ContentServer.certPath),
|
|
167
|
+
key: fs_1.default.readFileSync(ContentServer.keyPath),
|
|
168
|
+
rejectUnauthorized: false,
|
|
169
|
+
}),
|
|
170
|
+
timeout: 0, // Disable axios timeout (we're using inactivity timeout)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
306
173
|
}
|
|
307
174
|
exports.ContentServer = ContentServer;
|
|
308
175
|
ContentServer.port = 4161;
|
|
176
|
+
ContentServer.inactivityTimeout = 5000; // Inactivity timeout in milliseconds (5 seconds)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import https from "https";
|
|
2
|
+
import { PassThrough } from "stream";
|
|
2
3
|
export declare class PropagationServer {
|
|
3
4
|
storeId: string;
|
|
4
5
|
sessionId: string;
|
|
@@ -10,6 +11,7 @@ export declare class PropagationServer {
|
|
|
10
11
|
username: string | undefined;
|
|
11
12
|
password: string | undefined;
|
|
12
13
|
private static readonly port;
|
|
14
|
+
private static readonly inactivityTimeout;
|
|
13
15
|
constructor(ipAddress: string, storeId: string);
|
|
14
16
|
/**
|
|
15
17
|
* Initialize the Wallet instance.
|
|
@@ -19,6 +21,10 @@ export declare class PropagationServer {
|
|
|
19
21
|
* Create an Axios HTTPS Agent with self-signed certificate allowance.
|
|
20
22
|
*/
|
|
21
23
|
createHttpsAgent(): https.Agent;
|
|
24
|
+
/**
|
|
25
|
+
* Adds a custom inactivity timeout for large file transfers.
|
|
26
|
+
*/
|
|
27
|
+
addInactivityTimeout(stream: PassThrough, timeoutMs: number): PassThrough;
|
|
22
28
|
/**
|
|
23
29
|
* Check if the store and optional root hash exist by making a HEAD request.
|
|
24
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAW1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAiBrC,qBAAa,iBAAiB;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAY9C;;OAEG;IACG,gBAAgB;IAOtB;;OAEG;IACH,gBAAgB;IAQhB;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;IAyB3D;;OAEG;IACG,gBAAgB,CACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IA0C7D;;OAEG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM;IAuDzC;;OAEG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IAyBlD;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAuGhD;;OAEG;IACG,mBAAmB;IAgCzB;;OAEG;WACU,WAAW,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;IA2DnB;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgFlD;;OAEG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B7C;;;OAGG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM;IAwGjB;;OAEG;WACU,aAAa,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM;CA2FpB"}
|
|
@@ -4,26 +4,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.PropagationServer = void 0;
|
|
7
|
-
// Import necessary modules and dependencies
|
|
8
7
|
const axios_1 = __importDefault(require("axios"));
|
|
9
|
-
const
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const cli_progress_1 = __importDefault(require("cli-progress"));
|
|
11
9
|
const form_data_1 = __importDefault(require("form-data"));
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const credentialsUtils_1 = require("../utils/credentialsUtils");
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
15
12
|
const https_1 = __importDefault(require("https"));
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const config_1 = require("../utils/config");
|
|
19
|
-
const hashUtils_1 = require("../utils/hashUtils");
|
|
20
|
-
const nanospinner_1 = require("nanospinner");
|
|
13
|
+
const os_1 = __importDefault(require("os"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
21
15
|
const progress_stream_1 = __importDefault(require("progress-stream"));
|
|
22
|
-
const stream_1 = require("stream");
|
|
23
16
|
const asyncPool_1 = require("../utils/asyncPool");
|
|
24
|
-
const
|
|
25
|
-
const
|
|
17
|
+
const nanospinner_1 = require("nanospinner");
|
|
18
|
+
const hashUtils_1 = require("../utils/hashUtils");
|
|
19
|
+
const ssl_1 = require("../utils/ssl");
|
|
20
|
+
const colorette_1 = require("colorette");
|
|
26
21
|
const merkle_1 = require("../utils/merkle");
|
|
22
|
+
const stream_1 = require("stream");
|
|
23
|
+
const credentialsUtils_1 = require("../utils/credentialsUtils");
|
|
24
|
+
const config_1 = require("../utils/config");
|
|
25
|
+
const blockchain_1 = require("../blockchain");
|
|
27
26
|
// Helper function to trim long filenames with ellipsis and ensure consistent padding
|
|
28
27
|
function formatFilename(filename, maxLength = 30) {
|
|
29
28
|
if (!filename) {
|
|
@@ -62,6 +61,26 @@ class PropagationServer {
|
|
|
62
61
|
rejectUnauthorized: false,
|
|
63
62
|
});
|
|
64
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Adds a custom inactivity timeout for large file transfers.
|
|
66
|
+
*/
|
|
67
|
+
addInactivityTimeout(stream, timeoutMs) {
|
|
68
|
+
let timeoutId;
|
|
69
|
+
const resetTimeout = () => {
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
timeoutId = setTimeout(() => {
|
|
72
|
+
stream.destroy(new Error(`Inactivity timeout after ${timeoutMs / 1000} seconds`));
|
|
73
|
+
}, timeoutMs);
|
|
74
|
+
};
|
|
75
|
+
// Reset timeout every time data is received
|
|
76
|
+
stream.on("data", resetTimeout);
|
|
77
|
+
// Set the initial timeout
|
|
78
|
+
resetTimeout();
|
|
79
|
+
// Clear the timeout when the stream ends
|
|
80
|
+
stream.on("end", () => clearTimeout(timeoutId));
|
|
81
|
+
stream.on("error", () => clearTimeout(timeoutId)); // Handle errors
|
|
82
|
+
return stream;
|
|
83
|
+
}
|
|
65
84
|
/**
|
|
66
85
|
* Check if the store and optional root hash exist by making a HEAD request.
|
|
67
86
|
*/
|
|
@@ -201,8 +220,8 @@ class PropagationServer {
|
|
|
201
220
|
progressStream.on("progress", (progress) => {
|
|
202
221
|
progressBar.update(progress.transferred);
|
|
203
222
|
});
|
|
204
|
-
//
|
|
205
|
-
const passThroughStream = new stream_1.PassThrough();
|
|
223
|
+
// Add inactivity timeout to the progress stream
|
|
224
|
+
const passThroughStream = this.addInactivityTimeout(new stream_1.PassThrough(), PropagationServer.inactivityTimeout);
|
|
206
225
|
// Pipe the read stream through the progress stream into the PassThrough stream
|
|
207
226
|
fileReadStream.pipe(progressStream).pipe(passThroughStream);
|
|
208
227
|
// Use form-data to construct the request body
|
|
@@ -348,7 +367,9 @@ class PropagationServer {
|
|
|
348
367
|
progressStream.on("progress", (progress) => {
|
|
349
368
|
progressBar.update(progress.transferred);
|
|
350
369
|
});
|
|
351
|
-
|
|
370
|
+
// Add inactivity timeout to the progress stream
|
|
371
|
+
const passThroughStream = this.addInactivityTimeout(new stream_1.PassThrough(), PropagationServer.inactivityTimeout);
|
|
372
|
+
response.data.pipe(progressStream).pipe(passThroughStream);
|
|
352
373
|
progressStream.on("data", (chunk) => {
|
|
353
374
|
dataBuffers.push(chunk);
|
|
354
375
|
});
|
|
@@ -436,7 +457,12 @@ class PropagationServer {
|
|
|
436
457
|
progressStream.on("progress", (progress) => {
|
|
437
458
|
progressBar.update(progress.transferred);
|
|
438
459
|
});
|
|
439
|
-
|
|
460
|
+
// Add inactivity timeout to the progress stream
|
|
461
|
+
const passThroughStream = this.addInactivityTimeout(new stream_1.PassThrough(), PropagationServer.inactivityTimeout);
|
|
462
|
+
response.data
|
|
463
|
+
.pipe(progressStream)
|
|
464
|
+
.pipe(passThroughStream)
|
|
465
|
+
.pipe(fileWriteStream);
|
|
440
466
|
// Wait for both the file write stream and the progress stream to finish
|
|
441
467
|
await Promise.all([
|
|
442
468
|
new Promise((resolve, reject) => {
|
|
@@ -534,3 +560,4 @@ class PropagationServer {
|
|
|
534
560
|
}
|
|
535
561
|
exports.PropagationServer = PropagationServer;
|
|
536
562
|
PropagationServer.port = 4159; // Static port used for all requests
|
|
563
|
+
PropagationServer.inactivityTimeout = 5000; // Inactivity timeout in milliseconds (5 seconds)
|
|
@@ -5,7 +5,13 @@ export declare class FullNodePeer {
|
|
|
5
5
|
private peer;
|
|
6
6
|
private static deprioritizedIps;
|
|
7
7
|
private constructor();
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Connect to the best peer, optionally using custom certificate and key file paths.
|
|
10
|
+
* @param {string} [certFilePath] - Optional custom certificate file path.
|
|
11
|
+
* @param {string} [keyFilePath] - Optional custom key file path.
|
|
12
|
+
* @returns {Promise<Peer>} The connected peer.
|
|
13
|
+
*/
|
|
14
|
+
static connect(certFilePath?: string, keyFilePath?: string): Promise<Peer>;
|
|
9
15
|
private static isPortReachable;
|
|
10
16
|
private static isValidIpAddress;
|
|
11
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAqBpD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAA0B;
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAqBpD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAA0B;IAQzD,OAAO;IAIP;;;;;OAKG;WACiB,OAAO,CACzB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC;IAKhB,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBASZ,eAAe;mBA6Df,UAAU;IA2B/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBAyDT,WAAW;IA+HzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
|
|
@@ -30,8 +30,14 @@ class FullNodePeer {
|
|
|
30
30
|
constructor(peer) {
|
|
31
31
|
this.peer = peer;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Connect to the best peer, optionally using custom certificate and key file paths.
|
|
35
|
+
* @param {string} [certFilePath] - Optional custom certificate file path.
|
|
36
|
+
* @param {string} [keyFilePath] - Optional custom key file path.
|
|
37
|
+
* @returns {Promise<Peer>} The connected peer.
|
|
38
|
+
*/
|
|
39
|
+
static async connect(certFilePath, keyFilePath) {
|
|
40
|
+
const peer = await FullNodePeer.getBestPeer(certFilePath, keyFilePath);
|
|
35
41
|
return new FullNodePeer(peer).peer;
|
|
36
42
|
}
|
|
37
43
|
static isPortReachable(host, port, timeout = CONNECTION_TIMEOUT) {
|
|
@@ -158,7 +164,8 @@ class FullNodePeer {
|
|
|
158
164
|
}
|
|
159
165
|
catch (error) {
|
|
160
166
|
// If the error is WebSocket-related or timeout, reset the peer
|
|
161
|
-
if (error.message.includes("WebSocket") ||
|
|
167
|
+
if (error.message.includes("WebSocket") ||
|
|
168
|
+
error.message.includes("Operation timed out")) {
|
|
162
169
|
FullNodePeer.cachedPeer = null;
|
|
163
170
|
// @ts-ignore
|
|
164
171
|
FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
|
|
@@ -175,17 +182,33 @@ class FullNodePeer {
|
|
|
175
182
|
},
|
|
176
183
|
});
|
|
177
184
|
}
|
|
178
|
-
static async getBestPeer() {
|
|
185
|
+
static async getBestPeer(certFilePath, keyFilePath) {
|
|
179
186
|
const now = Date.now();
|
|
180
187
|
if (FullNodePeer.cachedPeer &&
|
|
181
188
|
now - FullNodePeer.cachedPeer.timestamp < CACHE_DURATION) {
|
|
182
189
|
return FullNodePeer.cachedPeer.peer;
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
191
|
+
let certFile;
|
|
192
|
+
let keyFile;
|
|
193
|
+
// If certFilePath or keyFilePath is provided, ensure both are provided
|
|
194
|
+
if (certFilePath && keyFilePath) {
|
|
195
|
+
certFile = certFilePath;
|
|
196
|
+
keyFile = keyFilePath;
|
|
197
|
+
if (!fs_1.default.existsSync(certFile)) {
|
|
198
|
+
throw new Error(`Certificate file not found: ${certFile}`);
|
|
199
|
+
}
|
|
200
|
+
if (!fs_1.default.existsSync(keyFile)) {
|
|
201
|
+
throw new Error(`Key file not found: ${keyFile}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Use default paths if no custom paths are provided
|
|
206
|
+
const sslFolder = path_1.default.resolve(os_1.default.homedir(), ".dig", "ssl");
|
|
207
|
+
certFile = path_1.default.join(sslFolder, "public_dig.crt");
|
|
208
|
+
keyFile = path_1.default.join(sslFolder, "public_dig.key");
|
|
209
|
+
if (!fs_1.default.existsSync(sslFolder)) {
|
|
210
|
+
throw new Error(`SSL folder not found: ${sslFolder}`);
|
|
211
|
+
}
|
|
189
212
|
}
|
|
190
213
|
new chia_server_coin_1.Tls(certFile, keyFile);
|
|
191
214
|
const peerIPs = await FullNodePeer.getPeerIPs();
|
|
@@ -230,7 +253,9 @@ class FullNodePeer {
|
|
|
230
253
|
// Prioritize LOCALHOST, TRUSTED_NODE_IP, and CHIA_NODES_HOST if they have the highest peak height
|
|
231
254
|
let bestPeerIndex = validHeights.findIndex((height, index) => height === highestPeak &&
|
|
232
255
|
!FullNodePeer.deprioritizedIps.has(peerIPs[index]) && // Exclude deprioritized IPs
|
|
233
|
-
(peerIPs[index] === LOCALHOST ||
|
|
256
|
+
(peerIPs[index] === LOCALHOST ||
|
|
257
|
+
peerIPs[index] === trustedNodeIp ||
|
|
258
|
+
peerIPs[index] === CHIA_NODES_HOST));
|
|
234
259
|
// If LOCALHOST, TRUSTED_NODE_IP, or CHIA_NODES_HOST don't have the highest peak, select any peer with the highest peak
|
|
235
260
|
if (bestPeerIndex === -1) {
|
|
236
261
|
bestPeerIndex = validHeights.findIndex((height) => height === highestPeak);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerCoin.d.ts","sourceRoot":"","sources":["../../src/blockchain/ServerCoin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,UAAU,IAAI,gBAAgB,EAE/B,MAAM,8BAA8B,CAAC;AAItC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4D3E,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsBH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoDjD,oBAAoB;IAerB,gBAAgB,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ServerCoin.d.ts","sourceRoot":"","sources":["../../src/blockchain/ServerCoin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,UAAU,IAAI,gBAAgB,EAE/B,MAAM,8BAA8B,CAAC;AAItC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4D3E,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsBH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoDjD,oBAAoB;IAerB,gBAAgB,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAoDP,mBAAmB,CAC9B,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAMP,kBAAkB,CAC7B,UAAU,GAAE,MAAU,EACtB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAMP,wBAAwB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAU,EACtB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;WAeN,eAAe,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAKpD,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiFrD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WA+B1C,uBAAuB;YAkE7B,sBAAsB;IAWvB,uBAAuB,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;WAuBL,sBAAsB,CAAC,mBAAmB,EAAE,IAAI,GAAG;QAC/D,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf;CA2BF"}
|
|
@@ -30,7 +30,7 @@ class ServerCoin {
|
|
|
30
30
|
const serverCoinCreationCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(serverCoinCollateral), BigInt(1000000));
|
|
31
31
|
const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
|
|
32
32
|
const epochBasedHint = (0, datalayer_driver_1.morphLauncherId)(Buffer.from(this.storeId, "hex"), BigInt(currentEpoch));
|
|
33
|
-
const newServerCoin = (0, datalayer_driver_1.createServerCoin)(publicSyntheticKey, serverCoinCreationCoins, epochBasedHint, [peerIp], BigInt(serverCoinCollateral), BigInt(1000000));
|
|
33
|
+
const newServerCoin = (0, datalayer_driver_1.createServerCoin)(publicSyntheticKey, serverCoinCreationCoins, epochBasedHint, [peerIp, publicSyntheticKey.toString("hex")], BigInt(serverCoinCollateral), BigInt(1000000));
|
|
34
34
|
const combinedCoinSpends = [...newServerCoin.coinSpends];
|
|
35
35
|
const sig = (0, datalayer_driver_1.signCoinSpends)(combinedCoinSpends, [await wallet.getPrivateSyntheticKey()], false);
|
|
36
36
|
const err = await peer.broadcastSpend(combinedCoinSpends, [sig]);
|
|
@@ -117,6 +117,9 @@ class ServerCoin {
|
|
|
117
117
|
for (const coinState of filteredCoinStates) {
|
|
118
118
|
const serverCoin = await peer.fetchServerCoin(coinState, maxClvmCost);
|
|
119
119
|
const peerUrl = serverCoin.memoUrls[0];
|
|
120
|
+
// The second memo URL is the public key
|
|
121
|
+
// We will utilize this for future features
|
|
122
|
+
const publicKey = serverCoin.memoUrls[1];
|
|
120
123
|
if (!blacklist.includes(peerUrl)) {
|
|
121
124
|
serverCoinPeers.add(peerUrl);
|
|
122
125
|
}
|