@dignetwork/dig-sdk 0.0.1-alpha.46 → 0.0.1-alpha.48

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.
@@ -154,7 +154,7 @@ declare class DataIntegrityTree {
154
154
  * @param expectedRootHash - The expected root hash of the Merkle tree.
155
155
  * @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
156
156
  */
157
- static validateKeyIntegrityWithForeignTree(key: string, sha256: string, serializedTree: object, expectedRootHash: string): boolean;
157
+ static validateKeyIntegrityWithForeignTree(hexkey: string, sha256: string, serializedTree: object, expectedRootHash: string, dataDir: string): Promise<boolean>;
158
158
  }
159
159
  export { DataIntegrityTree };
160
160
  //# sourceMappingURL=DataIntegrityTree.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DataIntegrityTree.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataIntegrityTree.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DataIntegrityTree.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataIntegrityTree.ts"],"names":[],"mappings":"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"}
@@ -634,8 +634,8 @@ class DataIntegrityTree {
634
634
  * @param expectedRootHash - The expected root hash of the Merkle tree.
635
635
  * @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
636
636
  */
637
- static validateKeyIntegrityWithForeignTree(key, sha256, serializedTree, expectedRootHash) {
638
- if (!isHexString(key)) {
637
+ static async validateKeyIntegrityWithForeignTree(hexkey, sha256, serializedTree, expectedRootHash, dataDir) {
638
+ if (!isHexString(hexkey)) {
639
639
  throw new Error("key must be a valid hex string");
640
640
  }
641
641
  if (!isHexString(sha256)) {
@@ -644,29 +644,59 @@ class DataIntegrityTree {
644
644
  if (!isHexString(expectedRootHash)) {
645
645
  throw new Error("expectedRootHash must be a valid hex string");
646
646
  }
647
- // Deserialize the foreign tree
648
- const leaves = serializedTree.leaves.map((leaf) => Buffer.from(leaf, "hex"));
649
- const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
650
- // Verify that the deserialized tree's root matches the expected root hash
651
- const treeRootHash = tree.getRoot().toString("hex");
652
- if (treeRootHash !== expectedRootHash) {
653
- console.warn(`Expected root hash ${expectedRootHash}, but got ${treeRootHash}`);
654
- return false;
647
+ // File path based on sha256
648
+ const filePath = path.join(dataDir, sha256.match(/.{1,2}/g).join("/"));
649
+ // Check if the file exists
650
+ if (!fs.existsSync(filePath)) {
651
+ throw new Error(`File at path ${filePath} does not exist`);
655
652
  }
656
- // Rebuild the files map from the serialized tree
657
- // @ts-ignore
658
- tree.files = new Map(Object.entries(serializedTree.files).map(([key, value]) => [
659
- key,
660
- { hash: value.hash, sha256: value.sha256 },
661
- ]));
662
- // Check if the SHA-256 exists in the foreign tree's files
663
- const combinedHash = crypto
664
- .createHash("sha256")
665
- .update(`${toHex(key)}/${sha256}`)
666
- .digest("hex");
667
- const leaf = Buffer.from(combinedHash, "hex");
668
- const isInTree = tree.getLeafIndex(leaf) !== -1;
669
- return isInTree;
653
+ const compressedReadStream = fs.createReadStream(filePath);
654
+ const decompressStream = zlib.createGunzip();
655
+ const hash = crypto.createHash("sha256");
656
+ // Process file decompression and hash comparison
657
+ return new Promise((resolve, reject) => {
658
+ compressedReadStream.pipe(decompressStream);
659
+ decompressStream.on("data", (chunk) => {
660
+ hash.update(chunk);
661
+ });
662
+ decompressStream.on("end", () => {
663
+ const uncompressedSha256 = hash.digest("hex");
664
+ console.log(`SHA-256 of uncompressed file: ${uncompressedSha256}`);
665
+ if (uncompressedSha256 !== sha256) {
666
+ console.warn(`File hash mismatch. Expected: ${sha256}, got: ${uncompressedSha256}`);
667
+ return resolve(false);
668
+ }
669
+ // Deserialize the foreign tree
670
+ const leaves = serializedTree.leaves.map((leaf) => Buffer.from(leaf, "hex"));
671
+ const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
672
+ // Verify that the deserialized tree's root matches the expected root hash
673
+ const treeRootHash = tree.getRoot().toString("hex");
674
+ if (treeRootHash !== expectedRootHash) {
675
+ console.warn(`Expected root hash ${expectedRootHash}, but got ${treeRootHash}`);
676
+ return resolve(false);
677
+ }
678
+ // Rebuild the files map from the serialized tree
679
+ // @ts-ignore
680
+ tree.files = new Map(Object.entries(serializedTree.files).map(([key, value]) => [
681
+ key,
682
+ { hash: value.hash, sha256: value.sha256 },
683
+ ]));
684
+ // Check if the SHA-256 exists in the foreign tree's files
685
+ const combinedHash = crypto
686
+ .createHash("sha256")
687
+ .update(`${hexkey}/${sha256}`)
688
+ .digest("hex");
689
+ const leaf = Buffer.from(combinedHash, "hex");
690
+ const isInTree = tree.getLeafIndex(leaf) !== -1;
691
+ resolve(isInTree);
692
+ });
693
+ decompressStream.on("error", (err) => {
694
+ reject(err);
695
+ });
696
+ compressedReadStream.on("error", (err) => {
697
+ reject(err);
698
+ });
699
+ });
670
700
  }
671
701
  }
672
702
  exports.DataIntegrityTree = DataIntegrityTree;
@@ -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 = `http://${this.ipAddress}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
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 = `http://${this.ipAddress}:${ContentServer.port}/.well-known`;
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 = `http://${this.ipAddress}:${ContentServer.port}/.well-known/stores`;
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 = `http://${this.ipAddress}:${ContentServer.port}/`;
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 = `http://${this.ipAddress}:${ContentServer.port}/${this.storeId}`;
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 = `http://${this.ipAddress}:${ContentServer.port}/${this.storeId}/${key}`;
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 = `http://${this.ipAddress}:${ContentServer.port}/${this.storeId}`;
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 = `http://${this.ipAddress}:${ContentServer.port}/${this.storeId}/${key}`;
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(`Request error for ${url}:`, 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(`Request error for ${url}:`, 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<string | null>;
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,MAAM,GAAG,IAAI,CAAC;WAuCX,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;YAsFF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YAsFrB,cAAc;CAoB7B"}
1
+ {"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,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;YAkJF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YAsFrB,cAAc;CAoB7B"}
@@ -33,6 +33,7 @@ const hashUtils_1 = require("../utils/hashUtils");
33
33
  const blockchain_1 = require("../blockchain");
34
34
  const config_1 = require("../utils/config");
35
35
  const util_1 = require("util");
36
+ const DataIntegrityTree_1 = require("../DataIntegrityTree");
36
37
  const rename = (0, util_1.promisify)(fs.rename);
37
38
  const unlink = (0, util_1.promisify)(fs.unlink);
38
39
  class DigNetwork {
@@ -128,31 +129,47 @@ class DigNetwork {
128
129
  static async findPeerWithStoreKey(storeId, rootHash, key, intialBlackList = []) {
129
130
  const peerBlackList = intialBlackList;
130
131
  const serverCoin = new blockchain_1.ServerCoin(storeId);
132
+ let peerIp = null;
133
+ // Keep sampling peers until an empty array is returned
131
134
  while (true) {
132
135
  try {
136
+ // Sample a peer from the current epoch
133
137
  const digPeers = await serverCoin.sampleCurrentEpoch(1, peerBlackList);
134
- if (digPeers.length === 0)
138
+ // If no peers are returned, break out of the loop
139
+ if (digPeers.length === 0) {
140
+ console.log("No more peers found.");
135
141
  break;
136
- const peerIp = digPeers[0];
142
+ }
143
+ peerIp = digPeers[0];
137
144
  const digPeer = new DigPeer_1.DigPeer(peerIp, storeId);
138
- const storeResponse = await digPeer.propagationServer.headStore({
145
+ // Try to fetch the head store information
146
+ const storeResponse = await digPeer.contentServer.headStore({
139
147
  hasRootHash: rootHash,
140
148
  });
141
- console.log(peerIp, storeResponse.headers);
149
+ // If the peer has the correct root hash, check if key is required
142
150
  if (storeResponse.headers?.["x-has-roothash"] === "true") {
143
151
  console.log(`Found Peer at ${peerIp} for storeId: ${storeId}, root hash ${rootHash}`);
152
+ // If no key is provided, return the peer
144
153
  if (!key) {
145
- return peerIp;
154
+ return digPeer;
146
155
  }
156
+ // If key is provided, check if the peer has it
147
157
  const keyResponse = await digPeer.contentServer.headKey(key);
148
158
  if (keyResponse.headers?.["x-key-exists"] === "true") {
149
- return peerIp;
159
+ return digPeer;
150
160
  }
151
161
  }
162
+ // Add peer to blacklist if it doesn't meet criteria
152
163
  peerBlackList.push(peerIp);
153
164
  }
154
- catch { }
165
+ catch (error) {
166
+ console.error(`Error connecting to DIG Peer ${peerIp}. Resampling...`);
167
+ if (peerIp) {
168
+ peerBlackList.push(peerIp); // Add to blacklist if error occurs
169
+ }
170
+ }
155
171
  }
172
+ // Return null if no valid peer was found
156
173
  return null;
157
174
  }
158
175
  static unsubscribeFromStore(storeId) {
@@ -163,45 +180,76 @@ class DigNetwork {
163
180
  }
164
181
  // Downloads files from the network based on the manifest
165
182
  async downloadFiles(forceDownload = false, renderProgressBar = true, skipData = false) {
183
+ console.log("Starting file download process...");
184
+ let peerBlackList = [];
185
+ let selectedPeer = null;
166
186
  try {
167
187
  const rootHistory = await this.dataStore.getRootHistory();
168
188
  if (!rootHistory.length) {
169
189
  throw new Error("No roots found in rootHistory. Cannot proceed with file download.");
170
190
  }
171
191
  await this.downloadHeightFile(true);
172
- const rootHistorySorted = rootHistory
192
+ // Filter out rootInfo entries where the .dat file already exists
193
+ const rootHistoryFiltered = rootHistory
173
194
  .filter((item) => item.timestamp !== undefined)
174
- .sort((a, b) => b.timestamp - a.timestamp);
175
- // Process rootHistory sequentially
176
- for (const rootInfo of rootHistorySorted) {
177
- const peerIp = await DigNetwork.findPeerWithStoreKey(this.dataStore.StoreId, rootInfo.root_hash);
178
- if (fs.existsSync(`${this.storeDir}/${rootInfo.root_hash}.dat`)) {
179
- console.log(`Root hash ${rootInfo.root_hash} already exists locally. Skipping download.`);
180
- continue; // Skip to the next rootInfo
181
- }
182
- if (!peerIp) {
183
- console.error(`No peer found with root hash ${rootInfo.root_hash}. Skipping download.`);
184
- continue; // Skip to the next rootInfo
185
- }
186
- const digPeer = new DigPeer_1.DigPeer(peerIp, this.dataStore.StoreId);
187
- const rootResponse = await digPeer.propagationServer.getStoreData(`${rootInfo.root_hash}.dat`);
188
- fs.writeFileSync(`${this.storeDir}/${rootInfo.root_hash}.dat`, rootResponse);
189
- const root = JSON.parse(rootResponse);
190
- if (!skipData) {
191
- // Sequential file download
192
- for (const [storeKey, file] of Object.entries(root.files)) {
193
- const filePath = (0, hashUtils_1.getFilePathFromSha256)(file.sha256, `${this.storeDir}/data`);
194
- if (!fs.existsSync(filePath) || forceDownload) {
195
- console.log(`Downloading file with sha256: ${file.sha256}...`);
196
- await this.downloadFileFromPeers(`data/${file.sha256.match(/.{1,2}/g).join("/")}`, filePath, forceDownload);
195
+ .filter((item) => !fs.existsSync(`${this.storeDir}/${item.root_hash}.dat`))
196
+ // Reverse to download the latest first
197
+ .reverse();
198
+ if (!rootHistoryFiltered.length) {
199
+ console.log("All root hashes already exist locally. No need for download.");
200
+ return;
201
+ }
202
+ // Process filtered rootHistory sequentially
203
+ for (const rootInfo of rootHistoryFiltered) {
204
+ while (true) {
205
+ try {
206
+ selectedPeer = await DigNetwork.findPeerWithStoreKey(this.dataStore.StoreId, rootInfo.root_hash, undefined, peerBlackList);
207
+ if (!selectedPeer) {
208
+ console.error(`No peer found with root hash ${rootInfo.root_hash}. Skipping download.`);
209
+ break; // Exit loop if no more peers are found
210
+ }
211
+ const rootResponse = await selectedPeer.propagationServer.getStoreData(`${rootInfo.root_hash}.dat`);
212
+ const root = JSON.parse(rootResponse);
213
+ if (!skipData) {
214
+ // Sequential file download
215
+ for (const [storeKey, file] of Object.entries(root.files)) {
216
+ const filePath = (0, hashUtils_1.getFilePathFromSha256)(file.sha256, `${this.storeDir}/data`);
217
+ console.log(`Downloading file with sha256: ${file.sha256}...`);
218
+ await selectedPeer.downloadData(this.dataStore.StoreId, `data/${file.sha256.match(/.{1,2}/g).join("/")}`);
219
+ const integrityCheck = await DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(storeKey, file.sha256, root, rootInfo.root_hash, `${this.storeDir}/data`);
220
+ if (integrityCheck) {
221
+ console.log(`\x1b[32mIntegrity check passed for file with sha256: ${file.sha256}.\x1b[0m`);
222
+ continue;
223
+ }
224
+ console.error(`\x1b[31mIntegrity check failed for file with sha256: ${file.sha256}.\x1b[0m`);
225
+ await unlink(filePath);
226
+ throw new Error(`Store Integrity check failed. Syncing file from another peer.`);
227
+ }
228
+ }
229
+ fs.writeFileSync(`${this.storeDir}/${rootInfo.root_hash}.dat`, rootResponse);
230
+ peerBlackList = []; // Clear the blacklist upon successful download
231
+ // Break out of the retry loop if the download succeeds
232
+ break;
233
+ }
234
+ catch (error) {
235
+ console.error(`Error downloading from peer. Retrying with another peer.`, error);
236
+ if (selectedPeer) {
237
+ peerBlackList.push(selectedPeer.IpAddress); // Add peer to blacklist and try again
197
238
  }
198
239
  }
199
240
  }
241
+ // Only process the first root hash so other stores can sync the latest.
242
+ // This has an effect where the latest roothash will always be synced first, even if new ones come in.
243
+ // Then it will backfill historical roothashes
244
+ break;
200
245
  }
201
246
  await this.downloadManifestFile(true);
202
247
  console.log("Syncing store complete.");
203
248
  }
204
249
  catch (error) {
250
+ if (selectedPeer) {
251
+ peerBlackList.push(selectedPeer.IpAddress);
252
+ }
205
253
  console.trace(error);
206
254
  throw error;
207
255
  }
@@ -229,13 +277,13 @@ class DigNetwork {
229
277
  return;
230
278
  const blacklist = this.peerBlacklist.get(dataPath) || new Set();
231
279
  for (const digPeer of digPeers) {
232
- if (blacklist.has(digPeer.IpAddress))
233
- continue;
234
- const response = await digPeer.propagationServer.headStore();
235
- if (!response.success) {
236
- continue;
237
- }
238
280
  try {
281
+ if (blacklist.has(digPeer.IpAddress))
282
+ continue;
283
+ const response = await digPeer.propagationServer.headStore();
284
+ if (!response.success) {
285
+ continue;
286
+ }
239
287
  // Create directory if it doesn't exist
240
288
  const directory = path.dirname(tempFilePath);
241
289
  if (!fs.existsSync(directory)) {
@@ -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":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAKL,MAAM,EACP,MAAM,8BAA8B,CAAC;AAKtC,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;CAoBhD"}
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;AAKpD,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;IAiHN,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,23 @@ 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 config_1 = require("../utils/config");
15
+ const fs_1 = __importDefault(require("fs"));
13
16
  const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
14
17
  const blockchain_2 = require("../blockchain");
15
18
  const blockchain_3 = require("../blockchain");
16
19
  const coins_1 = require("../blockchain/coins");
20
+ const config_2 = require("../utils/config");
21
+ const util_1 = require("util");
22
+ const rename = (0, util_1.promisify)(fs_1.default.rename);
23
+ const unlink = (0, util_1.promisify)(fs_1.default.unlink);
17
24
  class DigPeer {
18
25
  constructor(ipAddress, storeId) {
19
26
  this.ipAddress = ipAddress;
@@ -97,7 +104,7 @@ class DigPeer {
97
104
  });
98
105
  });
99
106
  // Perform tree integrity validation using the datFileContent and the root hash
100
- const treeCheck = DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(key, fileData.sha256, datFileContent, rootHash);
107
+ const treeCheck = DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(key, fileData.sha256, datFileContent, rootHash, path_1.default.resolve(config_1.DIG_FOLDER_PATH, "stores", this.storeId, 'data'));
101
108
  if (!treeCheck) {
102
109
  console.error(`Tree validation failed for file ${key}.`);
103
110
  filesIntegrityIntact = false;
@@ -195,5 +202,41 @@ class DigPeer {
195
202
  // Return the 32-byte hash as a hex string
196
203
  return transformedBuffer;
197
204
  }
205
+ async downloadData(storeId, dataPath) {
206
+ const filePath = path_1.default.join(config_2.STORE_PATH, storeId, dataPath);
207
+ const tempFilePath = `${filePath}.tmp`;
208
+ try {
209
+ const headStoreResponse = await this.propagationServer.headStore();
210
+ if (!headStoreResponse.success) {
211
+ throw new Error("Data not accessible from store.");
212
+ }
213
+ const directory = path_1.default.dirname(tempFilePath);
214
+ if (!fs_1.default.existsSync(directory)) {
215
+ fs_1.default.mkdirSync(directory, { recursive: true });
216
+ }
217
+ const fileStream = fs_1.default.createWriteStream(tempFilePath);
218
+ const dataStream = await this.propagationServer.streamStoreData(dataPath);
219
+ await new Promise((resolve, reject) => {
220
+ dataStream.pipe(fileStream);
221
+ dataStream.on("end", resolve);
222
+ dataStream.on("error", reject);
223
+ fileStream.on("error", reject);
224
+ });
225
+ await rename(tempFilePath, filePath);
226
+ console.log(`Downloaded ${dataPath} from ${this.IpAddress}`);
227
+ }
228
+ catch (error) {
229
+ console.error(`Failed to download data: ${error.message}`);
230
+ if (fs_1.default.existsSync(tempFilePath)) {
231
+ await unlink(tempFilePath);
232
+ }
233
+ // Check if directory is empty and remove it if it is
234
+ const directory = path_1.default.dirname(tempFilePath);
235
+ if (fs_1.default.existsSync(directory) && fs_1.default.readdirSync(directory).length === 0) {
236
+ fs_1.default.rmdirSync(directory);
237
+ console.log(`Removed empty directory: ${directory}`);
238
+ }
239
+ }
240
+ }
198
241
  }
199
242
  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(`Request error for ${url}:`, 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(`Request error for ${url}:`, error);
435
+ console.error(`GET ${url}:`, error.message);
436
436
  reject(error);
437
437
  });
438
438
  request.end();
@@ -3,6 +3,7 @@ export declare class FullNodePeer {
3
3
  private static cachedPeer;
4
4
  private static memoizedFetchNewPeerIPs;
5
5
  private peer;
6
+ private static deprioritizedIps;
6
7
  private constructor();
7
8
  static connect(): Promise<Peer>;
8
9
  private static isPortReachable;
@@ -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;AAmBpD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IAQnB,OAAO;WAIa,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBASZ,eAAe;mBAiDf,UAAU;IAyB/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBAgDT,WAAW;IAgGzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
1
+ {"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAoBpD,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;IAMzD,OAAO;WAIa,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBASZ,eAAe;mBA2Df,UAAU;IA2B/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBAkDT,WAAW;IA+FzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
@@ -16,6 +16,7 @@ const nanospinner_1 = require("nanospinner");
16
16
  const config_1 = require("../utils/config");
17
17
  const FULLNODE_PORT = 8444;
18
18
  const LOCALHOST = "127.0.0.1";
19
+ const CHIA_NODES_HOST = "chia-nodes";
19
20
  const DNS_HOSTS = [
20
21
  "dns-introducer.chia.net",
21
22
  "chia.ctrlaltdel.ch",
@@ -64,15 +65,22 @@ class FullNodePeer {
64
65
  static async fetchNewPeerIPs() {
65
66
  const trustedNodeIp = FullNodePeer.getTrustedFullNode();
66
67
  const priorityIps = [];
67
- // Prioritize trustedNodeIp
68
+ // Prioritize trustedNodeIp unless it's deprioritized
68
69
  if (trustedNodeIp &&
70
+ !FullNodePeer.deprioritizedIps.has(trustedNodeIp) &&
69
71
  (await FullNodePeer.isPortReachable(trustedNodeIp, FULLNODE_PORT))) {
70
72
  priorityIps.push(trustedNodeIp);
71
73
  }
72
- // Prioritize LOCALHOST
73
- if (await FullNodePeer.isPortReachable(LOCALHOST, FULLNODE_PORT)) {
74
+ // Prioritize LOCALHOST unless it's deprioritized
75
+ if (!FullNodePeer.deprioritizedIps.has(LOCALHOST) &&
76
+ (await FullNodePeer.isPortReachable(LOCALHOST, FULLNODE_PORT))) {
74
77
  priorityIps.push(LOCALHOST);
75
78
  }
79
+ // Prioritize CHIA_NODES_HOST unless it's deprioritized
80
+ if (!FullNodePeer.deprioritizedIps.has(CHIA_NODES_HOST) &&
81
+ (await FullNodePeer.isPortReachable(CHIA_NODES_HOST, FULLNODE_PORT))) {
82
+ priorityIps.push(CHIA_NODES_HOST);
83
+ }
76
84
  if (priorityIps.length > 0) {
77
85
  return priorityIps;
78
86
  }
@@ -114,6 +122,8 @@ class FullNodePeer {
114
122
  }
115
123
  // @ts-ignore
116
124
  if (FullNodePeer.memoizedFetchNewPeerIPs?.cache?.clear) {
125
+ // Clear cache and reset deprioritized IPs when cache is cleared
126
+ FullNodePeer.deprioritizedIps.clear();
117
127
  // @ts-ignore
118
128
  FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
119
129
  }
@@ -151,6 +161,8 @@ class FullNodePeer {
151
161
  FullNodePeer.cachedPeer = null;
152
162
  // @ts-ignore
153
163
  FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
164
+ FullNodePeer.deprioritizedIps.clear();
165
+ console.info(`Fullnode Peer error, reconnecting to a new peer...`);
154
166
  const newPeer = await FullNodePeer.getBestPeer();
155
167
  return newPeer[prop](...args);
156
168
  }
@@ -206,10 +218,11 @@ class FullNodePeer {
206
218
  throw new Error("No valid peak heights obtained from any peer.");
207
219
  }
208
220
  const highestPeak = Math.max(...validHeights);
209
- // Prioritize LOCALHOST and TRUSTED_NODE_IP if they have the highest peak height
221
+ // Prioritize LOCALHOST, TRUSTED_NODE_IP, and CHIA_NODES_HOST if they have the highest peak height
210
222
  let bestPeerIndex = validHeights.findIndex((height, index) => height === highestPeak &&
211
- (peerIPs[index] === LOCALHOST || peerIPs[index] === trustedNodeIp));
212
- // If LOCALHOST or TRUSTED_NODE_IP don't have the highest peak, select any peer with the highest peak
223
+ !FullNodePeer.deprioritizedIps.has(peerIPs[index]) && // Exclude deprioritized IPs
224
+ (peerIPs[index] === LOCALHOST || peerIPs[index] === trustedNodeIp || peerIPs[index] === CHIA_NODES_HOST));
225
+ // If LOCALHOST, TRUSTED_NODE_IP, or CHIA_NODES_HOST don't have the highest peak, select any peer with the highest peak
213
226
  if (bestPeerIndex === -1) {
214
227
  bestPeerIndex = validHeights.findIndex((height) => height === highestPeak);
215
228
  }
@@ -242,6 +255,7 @@ class FullNodePeer {
242
255
  }
243
256
  exports.FullNodePeer = FullNodePeer;
244
257
  FullNodePeer.cachedPeer = null;
258
+ FullNodePeer.deprioritizedIps = new Set(); // New set for deprioritized IPs
245
259
  (() => {
246
260
  FullNodePeer.memoizedFetchNewPeerIPs = (0, lodash_1.memoize)(FullNodePeer.fetchNewPeerIPs);
247
261
  })();
@@ -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;WAMN,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"}
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;WASN,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,9 @@ 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
+ if (process.env.DIG_DEBUG === "1") {
130
+ console.log("Server Coin Peers: ", serverCoinPeers);
131
+ }
129
132
  return lodash_1.default.sampleSize(serverCoinPeers, sampleSize);
130
133
  }
131
134
  // 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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/dig-sdk",
3
- "version": "0.0.1-alpha.46",
3
+ "version": "0.0.1-alpha.48",
4
4
  "description": "",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",