@dignetwork/dig-sdk 0.0.1-alpha.45 → 0.0.1-alpha.47
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.js +11 -11
- package/dist/DigNetwork/DigNetwork.d.ts +1 -1
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +66 -31
- package/dist/DigNetwork/DigPeer.d.ts +1 -0
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +42 -0
- package/dist/DigNetwork/PropagationServer.js +4 -4
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +1 -0
- 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/package.json +1 -1
|
@@ -21,7 +21,7 @@ class ContentServer {
|
|
|
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
23
|
// Construct the base URL
|
|
24
|
-
let url = `
|
|
24
|
+
let url = `https://${this.ipAddress}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
|
|
25
25
|
// If a challenge is provided, append it as a query parameter
|
|
26
26
|
if (challengeHex) {
|
|
27
27
|
url += `?challenge=${challengeHex}`;
|
|
@@ -42,32 +42,32 @@ class ContentServer {
|
|
|
42
42
|
}
|
|
43
43
|
// Method to get the .well-known information
|
|
44
44
|
async getWellKnown() {
|
|
45
|
-
const url = `
|
|
45
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/.well-known`;
|
|
46
46
|
return this.fetchJson(url);
|
|
47
47
|
}
|
|
48
48
|
// Method to get the list of known stores
|
|
49
49
|
async getKnownStores() {
|
|
50
|
-
const url = `
|
|
50
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/.well-known/stores`;
|
|
51
51
|
return this.fetchJson(url);
|
|
52
52
|
}
|
|
53
53
|
// Method to get the index of all stores
|
|
54
54
|
async getStoresIndex() {
|
|
55
|
-
const url = `
|
|
55
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/`;
|
|
56
56
|
return this.fetchJson(url);
|
|
57
57
|
}
|
|
58
58
|
// Method to get the index of keys in a store
|
|
59
59
|
async getKeysIndex() {
|
|
60
|
-
const url = `
|
|
60
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/${this.storeId}`;
|
|
61
61
|
return this.fetchJson(url);
|
|
62
62
|
}
|
|
63
63
|
// Method to check if a specific key exists (HEAD request)
|
|
64
64
|
async headKey(key) {
|
|
65
|
-
const url = `
|
|
65
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/${this.storeId}/${key}`;
|
|
66
66
|
return this.head(url);
|
|
67
67
|
}
|
|
68
68
|
// Method to check if a specific store exists (HEAD request)
|
|
69
69
|
async headStore(options) {
|
|
70
|
-
let url = `
|
|
70
|
+
let url = `https://${this.ipAddress}:${ContentServer.port}/${this.storeId}`;
|
|
71
71
|
if (options?.hasRootHash) {
|
|
72
72
|
url += `?hasRootHash=${options.hasRootHash}`;
|
|
73
73
|
}
|
|
@@ -75,7 +75,7 @@ class ContentServer {
|
|
|
75
75
|
}
|
|
76
76
|
streamKey(key) {
|
|
77
77
|
return new Promise((resolve, reject) => {
|
|
78
|
-
const url = `
|
|
78
|
+
const url = `https://${this.ipAddress}:${ContentServer.port}/${this.storeId}/${key}`;
|
|
79
79
|
const urlObj = new url_1.URL(url);
|
|
80
80
|
const requestOptions = {
|
|
81
81
|
hostname: urlObj.hostname,
|
|
@@ -102,7 +102,7 @@ class ContentServer {
|
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
104
|
request.on("error", (error) => {
|
|
105
|
-
console.error(`Request error for ${url}:`, error);
|
|
105
|
+
console.error(`GET Request error for ${url}:`, error);
|
|
106
106
|
reject(error);
|
|
107
107
|
});
|
|
108
108
|
request.end();
|
|
@@ -161,7 +161,7 @@ class ContentServer {
|
|
|
161
161
|
}
|
|
162
162
|
});
|
|
163
163
|
request.on("error", (error) => {
|
|
164
|
-
console.error(`
|
|
164
|
+
console.error(`HEAD ${url}:`, error.message);
|
|
165
165
|
reject({ success: false });
|
|
166
166
|
});
|
|
167
167
|
request.end();
|
|
@@ -247,7 +247,7 @@ class ContentServer {
|
|
|
247
247
|
}
|
|
248
248
|
});
|
|
249
249
|
request.on("error", (error) => {
|
|
250
|
-
console.error(`
|
|
250
|
+
console.error(`GET ${url}:`, error.message);
|
|
251
251
|
reject(error);
|
|
252
252
|
});
|
|
253
253
|
request.end();
|
|
@@ -9,7 +9,7 @@ export declare class DigNetwork {
|
|
|
9
9
|
uploadStoreHead(digPeer: DigPeer): Promise<void>;
|
|
10
10
|
uploadStore(digPeer: DigPeer): Promise<void>;
|
|
11
11
|
static subscribeToStore(storeId: string): Promise<void>;
|
|
12
|
-
static findPeerWithStoreKey(storeId: string, rootHash: string, key?: string, intialBlackList?: string[]): Promise<
|
|
12
|
+
static findPeerWithStoreKey(storeId: string, rootHash: string, key?: string, intialBlackList?: string[]): Promise<DigPeer | null>;
|
|
13
13
|
static unsubscribeFromStore(storeId: string): void;
|
|
14
14
|
downloadFiles(forceDownload?: boolean, renderProgressBar?: boolean, skipData?: boolean): Promise<void>;
|
|
15
15
|
private fetchAvailablePeers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;WA6BrC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQhD,oBAAoB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,EACZ,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;WA6BrC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQhD,oBAAoB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,EACZ,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;WAyDZ,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ5C,aAAa,CACxB,aAAa,GAAE,OAAe,EAC9B,iBAAiB,GAAE,OAAc,EACjC,QAAQ,GAAE,OAAe,GACxB,OAAO,CAAC,IAAI,CAAC;YAqHF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YAsFrB,cAAc;CAoB7B"}
|
|
@@ -128,31 +128,47 @@ class DigNetwork {
|
|
|
128
128
|
static async findPeerWithStoreKey(storeId, rootHash, key, intialBlackList = []) {
|
|
129
129
|
const peerBlackList = intialBlackList;
|
|
130
130
|
const serverCoin = new blockchain_1.ServerCoin(storeId);
|
|
131
|
+
let peerIp = null;
|
|
132
|
+
// Keep sampling peers until an empty array is returned
|
|
131
133
|
while (true) {
|
|
132
134
|
try {
|
|
135
|
+
// Sample a peer from the current epoch
|
|
133
136
|
const digPeers = await serverCoin.sampleCurrentEpoch(1, peerBlackList);
|
|
134
|
-
|
|
137
|
+
// If no peers are returned, break out of the loop
|
|
138
|
+
if (digPeers.length === 0) {
|
|
139
|
+
console.log("No more peers found.");
|
|
135
140
|
break;
|
|
136
|
-
|
|
141
|
+
}
|
|
142
|
+
peerIp = digPeers[0];
|
|
137
143
|
const digPeer = new DigPeer_1.DigPeer(peerIp, storeId);
|
|
144
|
+
// Try to fetch the head store information
|
|
138
145
|
const storeResponse = await digPeer.contentServer.headStore({
|
|
139
146
|
hasRootHash: rootHash,
|
|
140
147
|
});
|
|
141
|
-
|
|
148
|
+
// If the peer has the correct root hash, check if key is required
|
|
142
149
|
if (storeResponse.headers?.["x-has-roothash"] === "true") {
|
|
143
150
|
console.log(`Found Peer at ${peerIp} for storeId: ${storeId}, root hash ${rootHash}`);
|
|
151
|
+
// If no key is provided, return the peer
|
|
144
152
|
if (!key) {
|
|
145
|
-
return
|
|
153
|
+
return digPeer;
|
|
146
154
|
}
|
|
155
|
+
// If key is provided, check if the peer has it
|
|
147
156
|
const keyResponse = await digPeer.contentServer.headKey(key);
|
|
148
157
|
if (keyResponse.headers?.["x-key-exists"] === "true") {
|
|
149
|
-
return
|
|
158
|
+
return digPeer;
|
|
150
159
|
}
|
|
151
160
|
}
|
|
161
|
+
// Add peer to blacklist if it doesn't meet criteria
|
|
152
162
|
peerBlackList.push(peerIp);
|
|
153
163
|
}
|
|
154
|
-
catch {
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error(`Error connecting to peer ${peerIp}. Resampling...`);
|
|
166
|
+
if (peerIp) {
|
|
167
|
+
peerBlackList.push(peerIp); // Add to blacklist if error occurs
|
|
168
|
+
}
|
|
169
|
+
}
|
|
155
170
|
}
|
|
171
|
+
// Return null if no valid peer was found
|
|
156
172
|
return null;
|
|
157
173
|
}
|
|
158
174
|
static unsubscribeFromStore(storeId) {
|
|
@@ -163,37 +179,54 @@ class DigNetwork {
|
|
|
163
179
|
}
|
|
164
180
|
// Downloads files from the network based on the manifest
|
|
165
181
|
async downloadFiles(forceDownload = false, renderProgressBar = true, skipData = false) {
|
|
182
|
+
console.log("Starting file download process...");
|
|
183
|
+
let peerBlackList = [];
|
|
184
|
+
let selectedPeer = null;
|
|
166
185
|
try {
|
|
167
186
|
const rootHistory = await this.dataStore.getRootHistory();
|
|
168
187
|
if (!rootHistory.length) {
|
|
169
188
|
throw new Error("No roots found in rootHistory. Cannot proceed with file download.");
|
|
170
189
|
}
|
|
171
190
|
await this.downloadHeightFile(true);
|
|
172
|
-
|
|
191
|
+
// Filter out rootInfo entries where the .dat file already exists
|
|
192
|
+
const rootHistoryFiltered = rootHistory
|
|
173
193
|
.filter((item) => item.timestamp !== undefined)
|
|
174
|
-
.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
.filter((item) => !fs.existsSync(`${this.storeDir}/${item.root_hash}.dat`))
|
|
195
|
+
.reverse();
|
|
196
|
+
if (!rootHistoryFiltered.length) {
|
|
197
|
+
console.log("All root hashes already exist locally. No need for download.");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Process filtered rootHistory sequentially
|
|
201
|
+
for (const rootInfo of rootHistoryFiltered) {
|
|
202
|
+
while (true) {
|
|
203
|
+
try {
|
|
204
|
+
selectedPeer = await DigNetwork.findPeerWithStoreKey(this.dataStore.StoreId, rootInfo.root_hash, undefined, peerBlackList);
|
|
205
|
+
if (!selectedPeer) {
|
|
206
|
+
console.error(`No peer found with root hash ${rootInfo.root_hash}. Skipping download.`);
|
|
207
|
+
break; // Exit loop if no more peers are found
|
|
208
|
+
}
|
|
209
|
+
const rootResponse = await selectedPeer.propagationServer.getStoreData(`${rootInfo.root_hash}.dat`);
|
|
210
|
+
const root = JSON.parse(rootResponse);
|
|
211
|
+
if (!skipData) {
|
|
212
|
+
// Sequential file download
|
|
213
|
+
for (const [storeKey, file] of Object.entries(root.files)) {
|
|
214
|
+
const filePath = (0, hashUtils_1.getFilePathFromSha256)(file.sha256, `${this.storeDir}/data`);
|
|
215
|
+
if (!fs.existsSync(filePath) || forceDownload) {
|
|
216
|
+
console.log(`Downloading file with sha256: ${file.sha256}...`);
|
|
217
|
+
await selectedPeer.downloadData(this.dataStore.StoreId, `data/${file.sha256.match(/.{1,2}/g).join("/")}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
fs.writeFileSync(`${this.storeDir}/${rootInfo.root_hash}.dat`, rootResponse);
|
|
222
|
+
peerBlackList = []; // Clear the blacklist upon successful download
|
|
223
|
+
// Break out of the retry loop if the download succeeds
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
console.error(`Error downloading from peer. Retrying with another peer.`, error);
|
|
228
|
+
if (selectedPeer) {
|
|
229
|
+
peerBlackList.push(selectedPeer.IpAddress); // Add peer to blacklist and try again
|
|
197
230
|
}
|
|
198
231
|
}
|
|
199
232
|
}
|
|
@@ -202,7 +235,9 @@ class DigNetwork {
|
|
|
202
235
|
console.log("Syncing store complete.");
|
|
203
236
|
}
|
|
204
237
|
catch (error) {
|
|
205
|
-
|
|
238
|
+
if (selectedPeer) {
|
|
239
|
+
peerBlackList.push(selectedPeer.IpAddress);
|
|
240
|
+
}
|
|
206
241
|
throw error;
|
|
207
242
|
}
|
|
208
243
|
}
|
|
@@ -19,5 +19,6 @@ export declare class DigPeer {
|
|
|
19
19
|
static sendBulkPayments(walletName: string, outputs: Output[]): Promise<void>;
|
|
20
20
|
sendPayment(walletName: string, amount: bigint, memos?: Buffer[]): Promise<void>;
|
|
21
21
|
static createPaymentHint(storeId: Buffer): Buffer;
|
|
22
|
+
downloadData(storeId: string, dataPath: string): Promise<void>;
|
|
22
23
|
}
|
|
23
24
|
//# sourceMappingURL=DigPeer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigPeer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigPeer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DigPeer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigPeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD,OAAO,EAKL,MAAM,EACP,MAAM,8BAA8B,CAAC;AAUtC,qBAAa,OAAO;IAClB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,kBAAkB,CAAoB;IAC9C,OAAO,CAAC,gBAAgB,CAAkB;gBAE9B,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAS9C,IAAW,aAAa,IAAI,aAAa,CAExC;IAGD,IAAW,iBAAiB,IAAI,iBAAiB,CAEhD;IAGD,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,MAAM,CAE7B;IAEY,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,OAAO,CAAC;IAgHN,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;WA2B3B,qBAAqB,CACjC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC;WAuBI,gBAAgB,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC;IAwCH,WAAW,CACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAM,EAAO,GACnB,OAAO,CAAC,IAAI,CAAC;WAYF,iBAAiB,CAAC,OAAO,EAAE,MAAM;IAqBlC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4C5E"}
|
|
@@ -4,16 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.DigPeer = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
7
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
9
|
const ContentServer_1 = require("./ContentServer");
|
|
9
10
|
const PropagationServer_1 = require("./PropagationServer");
|
|
10
11
|
const IncentiveServer_1 = require("./IncentiveServer");
|
|
11
12
|
const blockchain_1 = require("../blockchain");
|
|
12
13
|
const DataIntegrityTree_1 = require("../DataIntegrityTree");
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
15
|
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
14
16
|
const blockchain_2 = require("../blockchain");
|
|
15
17
|
const blockchain_3 = require("../blockchain");
|
|
16
18
|
const coins_1 = require("../blockchain/coins");
|
|
19
|
+
const config_1 = require("../utils/config");
|
|
20
|
+
const util_1 = require("util");
|
|
21
|
+
const rename = (0, util_1.promisify)(fs_1.default.rename);
|
|
22
|
+
const unlink = (0, util_1.promisify)(fs_1.default.unlink);
|
|
17
23
|
class DigPeer {
|
|
18
24
|
constructor(ipAddress, storeId) {
|
|
19
25
|
this.ipAddress = ipAddress;
|
|
@@ -195,5 +201,41 @@ class DigPeer {
|
|
|
195
201
|
// Return the 32-byte hash as a hex string
|
|
196
202
|
return transformedBuffer;
|
|
197
203
|
}
|
|
204
|
+
async downloadData(storeId, dataPath) {
|
|
205
|
+
const filePath = path_1.default.join(config_1.STORE_PATH, storeId, dataPath);
|
|
206
|
+
const tempFilePath = `${filePath}.tmp`;
|
|
207
|
+
try {
|
|
208
|
+
const headStoreResponse = await this.propagationServer.headStore();
|
|
209
|
+
if (!headStoreResponse.success) {
|
|
210
|
+
throw new Error("Data not accessible from store.");
|
|
211
|
+
}
|
|
212
|
+
const directory = path_1.default.dirname(tempFilePath);
|
|
213
|
+
if (!fs_1.default.existsSync(directory)) {
|
|
214
|
+
fs_1.default.mkdirSync(directory, { recursive: true });
|
|
215
|
+
}
|
|
216
|
+
const fileStream = fs_1.default.createWriteStream(tempFilePath);
|
|
217
|
+
const dataStream = await this.propagationServer.streamStoreData(dataPath);
|
|
218
|
+
await new Promise((resolve, reject) => {
|
|
219
|
+
dataStream.pipe(fileStream);
|
|
220
|
+
dataStream.on("end", resolve);
|
|
221
|
+
dataStream.on("error", reject);
|
|
222
|
+
fileStream.on("error", reject);
|
|
223
|
+
});
|
|
224
|
+
await rename(tempFilePath, filePath);
|
|
225
|
+
console.log(`Downloaded ${dataPath} from ${this.IpAddress}`);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
console.error(`Failed to download data: ${error.message}`);
|
|
229
|
+
if (fs_1.default.existsSync(tempFilePath)) {
|
|
230
|
+
await unlink(tempFilePath);
|
|
231
|
+
}
|
|
232
|
+
// Check if directory is empty and remove it if it is
|
|
233
|
+
const directory = path_1.default.dirname(tempFilePath);
|
|
234
|
+
if (fs_1.default.existsSync(directory) && fs_1.default.readdirSync(directory).length === 0) {
|
|
235
|
+
fs_1.default.rmdirSync(directory);
|
|
236
|
+
console.log(`Removed empty directory: ${directory}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
198
240
|
}
|
|
199
241
|
exports.DigPeer = DigPeer;
|
|
@@ -271,7 +271,7 @@ class PropagationServer {
|
|
|
271
271
|
}
|
|
272
272
|
});
|
|
273
273
|
request.on("error", (error) => {
|
|
274
|
-
console.error(`Request error for ${url}:`, error);
|
|
274
|
+
console.error(`GET Request error for ${url}:`, error);
|
|
275
275
|
reject(error);
|
|
276
276
|
});
|
|
277
277
|
request.end();
|
|
@@ -326,13 +326,13 @@ class PropagationServer {
|
|
|
326
326
|
}
|
|
327
327
|
});
|
|
328
328
|
request.on("error", (error) => {
|
|
329
|
-
console.error(`
|
|
329
|
+
console.error(`HEAD ${url}:`, error.message);
|
|
330
330
|
reject({ success: false });
|
|
331
331
|
});
|
|
332
332
|
request.end();
|
|
333
333
|
}
|
|
334
334
|
catch (err) {
|
|
335
|
-
console.error(`Invalid URL: ${url}`, err);
|
|
335
|
+
console.error(`Invalid URL: ${url}`, err.message);
|
|
336
336
|
reject({ success: false, message: "Invalid URL" });
|
|
337
337
|
}
|
|
338
338
|
});
|
|
@@ -432,7 +432,7 @@ class PropagationServer {
|
|
|
432
432
|
}
|
|
433
433
|
});
|
|
434
434
|
request.on("error", (error) => {
|
|
435
|
-
console.error(`
|
|
435
|
+
console.error(`GET ${url}:`, error.message);
|
|
436
436
|
reject(error);
|
|
437
437
|
});
|
|
438
438
|
request.end();
|
|
@@ -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;AAMrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DzD,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAqBH,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;IAoCP,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;
|
|
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;AAMrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DzD,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAqBH,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;IAoCP,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;WAON,eAAe,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAKpD,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDrD,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"}
|
|
@@ -126,6 +126,7 @@ class ServerCoin {
|
|
|
126
126
|
// Sample server coins by epoch
|
|
127
127
|
async sampleServerCoinsByEpoch(epoch, sampleSize = 5, blacklist = []) {
|
|
128
128
|
const serverCoinPeers = await this.getAllEpochPeers(epoch, blacklist);
|
|
129
|
+
console.log("Server Coin Peers: ", serverCoinPeers);
|
|
129
130
|
return lodash_1.default.sampleSize(serverCoinPeers, sampleSize);
|
|
130
131
|
}
|
|
131
132
|
// Get the current epoch based on the current timestamp
|
|
@@ -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;
|