@dignetwork/dig-sdk 0.0.1-alpha.18 → 0.0.1-alpha.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +10 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +77 -10
- package/dist/DigNetwork/ContentServer.d.ts +22 -5
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +226 -28
- package/dist/DigNetwork/DigNetwork.d.ts +15 -9
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +129 -189
- package/dist/DigNetwork/DigPeer.d.ts +8 -3
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +72 -115
- package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -1
- package/dist/DigNetwork/IncentiveServer.js +6 -5
- package/dist/DigNetwork/PropagationServer.d.ts +89 -33
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +625 -358
- package/dist/blockchain/DataStore.d.ts +25 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +117 -159
- package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
- package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.d.ts +95 -10
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +325 -115
- package/dist/blockchain/ServerCoin.d.ts +13 -5
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +89 -28
- package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
- package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
- package/dist/blockchain/StoreMonitorRegistry.js +238 -0
- package/dist/blockchain/Wallet.d.ts +2 -2
- package/dist/blockchain/Wallet.d.ts.map +1 -1
- package/dist/blockchain/Wallet.js +50 -18
- package/dist/blockchain/coins.d.ts +1 -2
- package/dist/blockchain/coins.d.ts.map +1 -1
- package/dist/blockchain/coins.js +1 -55
- package/dist/blockchain/index.d.ts +1 -0
- package/dist/blockchain/index.d.ts.map +1 -1
- package/dist/blockchain/index.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/ContentScanner.d.ts +63 -0
- package/dist/utils/ContentScanner.d.ts.map +1 -0
- package/dist/utils/ContentScanner.js +175 -0
- package/dist/utils/DigCache.d.ts +38 -0
- package/dist/utils/DigCache.d.ts.map +1 -0
- package/dist/utils/DigCache.js +236 -0
- package/dist/utils/Environment.d.ts +21 -0
- package/dist/utils/Environment.d.ts.map +1 -0
- package/dist/utils/Environment.js +93 -0
- package/dist/utils/FileCache.d.ts +1 -1
- package/dist/utils/FileCache.d.ts.map +1 -1
- package/dist/utils/FileCache.js +2 -2
- package/dist/utils/FileTransfer.d.ts +47 -0
- package/dist/utils/FileTransfer.d.ts.map +1 -0
- package/dist/utils/FileTransfer.js +209 -0
- package/dist/utils/NconfManager.d.ts.map +1 -1
- package/dist/utils/NconfManager.js +16 -7
- package/dist/utils/PeerRanker.d.ts +55 -0
- package/dist/utils/PeerRanker.d.ts.map +1 -0
- package/dist/utils/PeerRanker.js +186 -0
- package/dist/utils/StoreArchiveManager.d.ts +45 -0
- package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
- package/dist/utils/StoreArchiveManager.js +153 -0
- package/dist/utils/Udi.d.ts +27 -0
- package/dist/utils/Udi.d.ts.map +1 -0
- package/dist/utils/Udi.js +145 -0
- package/dist/utils/config.d.ts +4 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +13 -8
- package/dist/utils/directoryUtils.d.ts +16 -0
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/dist/utils/directoryUtils.js +66 -0
- package/dist/utils/index.d.ts +5 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/merkle.d.ts +2 -0
- package/dist/utils/merkle.d.ts.map +1 -0
- package/dist/utils/merkle.js +28 -0
- package/dist/utils/network.d.ts +2 -1
- package/dist/utils/network.d.ts.map +1 -1
- package/dist/utils/network.js +40 -15
- package/dist/utils/promiseUtils.d.ts +27 -0
- package/dist/utils/promiseUtils.d.ts.map +1 -0
- package/dist/utils/promiseUtils.js +69 -0
- package/dist/utils/ssl.js +2 -2
- package/package.json +31 -13
- package/dist/utils/deltaUtils.d.ts +0 -2
- package/dist/utils/deltaUtils.d.ts.map +0 -1
- package/dist/utils/deltaUtils.js +0 -83
|
@@ -17,6 +17,7 @@ declare class DataIntegrityTree {
|
|
|
17
17
|
files: Map<string, {
|
|
18
18
|
hash: string;
|
|
19
19
|
sha256: string;
|
|
20
|
+
bytes: string;
|
|
20
21
|
}>;
|
|
21
22
|
private tree;
|
|
22
23
|
constructor(storeId: string, options?: DataIntegrityTreeOptions);
|
|
@@ -78,6 +79,9 @@ declare class DataIntegrityTree {
|
|
|
78
79
|
* @returns The serialized Merkle tree.
|
|
79
80
|
*/
|
|
80
81
|
serialize(rootHash?: string | null): object;
|
|
82
|
+
static getRootOfForeignTree(treeData: {
|
|
83
|
+
leaves: string[];
|
|
84
|
+
}): string;
|
|
81
85
|
/**
|
|
82
86
|
* Deserialize a JSON object to a Merkle tree.
|
|
83
87
|
* @param rootHash - The root hash of the tree.
|
|
@@ -152,9 +156,14 @@ declare class DataIntegrityTree {
|
|
|
152
156
|
* @param sha256 - The SHA-256 hash of the file.
|
|
153
157
|
* @param serializedTree - The foreign serialized Merkle tree.
|
|
154
158
|
* @param expectedRootHash - The expected root hash of the Merkle tree.
|
|
159
|
+
* @param dataDir - The directory where the data is stored.
|
|
160
|
+
* @param verifiedSha256 - A boolean indicating if the SHA-256 hash has already been verified.
|
|
155
161
|
* @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
|
|
156
162
|
*/
|
|
157
|
-
static validateKeyIntegrityWithForeignTree(
|
|
163
|
+
static validateKeyIntegrityWithForeignTree(hexkey: string, sha256: string, serializedTree: object, expectedRootHash: string, dataDir: string, verifiedSha256?: boolean): Promise<boolean>;
|
|
164
|
+
private static verifyFileHash;
|
|
165
|
+
private static deserializeAndVerifyTree;
|
|
166
|
+
private static checkHashInTree;
|
|
158
167
|
}
|
|
159
168
|
export { DataIntegrityTree };
|
|
160
169
|
//# sourceMappingURL=DataIntegrityTree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataIntegrityTree.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataIntegrityTree.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAuClC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,cAAM,iBAAiB;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"DataIntegrityTree.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataIntegrityTree.ts"],"names":[],"mappings":"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"}
|
|
@@ -94,8 +94,7 @@ class DataIntegrityTree {
|
|
|
94
94
|
}
|
|
95
95
|
this.files = new Map();
|
|
96
96
|
if (options.rootHash) {
|
|
97
|
-
|
|
98
|
-
if (manifest.includes(options.rootHash)) {
|
|
97
|
+
if (fs.existsSync(path.join(this.storeDir, `${options.rootHash}.dat`))) {
|
|
99
98
|
this.tree = this.deserializeTree(options.rootHash);
|
|
100
99
|
}
|
|
101
100
|
else {
|
|
@@ -135,7 +134,10 @@ class DataIntegrityTree {
|
|
|
135
134
|
const manifest = this._loadManifest();
|
|
136
135
|
if (manifest.length > 0) {
|
|
137
136
|
const latestRootHash = manifest[manifest.length - 1];
|
|
138
|
-
|
|
137
|
+
if (latestRootHash && isHexString(latestRootHash)) {
|
|
138
|
+
return this.deserializeTree(latestRootHash);
|
|
139
|
+
}
|
|
140
|
+
return new merkletreejs_1.MerkleTree([], crypto_js_1.SHA256, { sortPairs: true });
|
|
139
141
|
}
|
|
140
142
|
else {
|
|
141
143
|
return new merkletreejs_1.MerkleTree([], crypto_js_1.SHA256, { sortPairs: true });
|
|
@@ -189,11 +191,16 @@ class DataIntegrityTree {
|
|
|
189
191
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
190
192
|
}
|
|
191
193
|
const tempFilePath = path.join(tempDir, `${crypto.randomUUID()}.gz`);
|
|
194
|
+
let totalBytes = 0;
|
|
192
195
|
return new Promise((resolve, reject) => {
|
|
193
196
|
const tempWriteStream = fs.createWriteStream(tempFilePath);
|
|
194
197
|
readStream.on("data", (chunk) => {
|
|
195
198
|
uncompressedHash.update(chunk);
|
|
196
199
|
});
|
|
200
|
+
// Now listen to the gzip stream for compressed data size
|
|
201
|
+
gzip.on("data", (chunk) => {
|
|
202
|
+
totalBytes += chunk.length; // This counts compressed bytes
|
|
203
|
+
});
|
|
197
204
|
readStream.pipe(gzip).pipe(tempWriteStream);
|
|
198
205
|
tempWriteStream.on("finish", async () => {
|
|
199
206
|
sha256 = uncompressedHash.digest("hex");
|
|
@@ -222,6 +229,7 @@ class DataIntegrityTree {
|
|
|
222
229
|
this.files.set(key, {
|
|
223
230
|
hash: combinedHash,
|
|
224
231
|
sha256: sha256,
|
|
232
|
+
bytes: totalBytes.toString(),
|
|
225
233
|
});
|
|
226
234
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
227
235
|
this._rebuildTree();
|
|
@@ -313,6 +321,11 @@ class DataIntegrityTree {
|
|
|
313
321
|
files: Object.fromEntries(this.files),
|
|
314
322
|
};
|
|
315
323
|
}
|
|
324
|
+
static getRootOfForeignTree(treeData) {
|
|
325
|
+
const leaves = treeData.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
326
|
+
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
327
|
+
return tree.getRoot().toString("hex");
|
|
328
|
+
}
|
|
316
329
|
/**
|
|
317
330
|
* Deserialize a JSON object to a Merkle tree.
|
|
318
331
|
* @param rootHash - The root hash of the tree.
|
|
@@ -603,14 +616,13 @@ class DataIntegrityTree {
|
|
|
603
616
|
decompressStream.on("end", () => {
|
|
604
617
|
const uncompressedSha256 = hash.digest("hex");
|
|
605
618
|
const isValid = uncompressedSha256 === sha256;
|
|
606
|
-
console.log(`SHA-256 of uncompressed file: ${uncompressedSha256}`);
|
|
607
619
|
if (!isValid) {
|
|
608
620
|
return resolve(false);
|
|
609
621
|
}
|
|
610
622
|
const tree = this.deserializeTree(rootHash);
|
|
611
623
|
const combinedHash = crypto
|
|
612
624
|
.createHash("sha256")
|
|
613
|
-
.update(
|
|
625
|
+
.update(sha256)
|
|
614
626
|
.digest("hex");
|
|
615
627
|
const leaf = Buffer.from(combinedHash, "hex");
|
|
616
628
|
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
@@ -632,10 +644,12 @@ class DataIntegrityTree {
|
|
|
632
644
|
* @param sha256 - The SHA-256 hash of the file.
|
|
633
645
|
* @param serializedTree - The foreign serialized Merkle tree.
|
|
634
646
|
* @param expectedRootHash - The expected root hash of the Merkle tree.
|
|
647
|
+
* @param dataDir - The directory where the data is stored.
|
|
648
|
+
* @param verifiedSha256 - A boolean indicating if the SHA-256 hash has already been verified.
|
|
635
649
|
* @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
|
|
636
650
|
*/
|
|
637
|
-
static validateKeyIntegrityWithForeignTree(
|
|
638
|
-
if (!isHexString(
|
|
651
|
+
static async validateKeyIntegrityWithForeignTree(hexkey, sha256, serializedTree, expectedRootHash, dataDir, verifiedSha256 = false) {
|
|
652
|
+
if (!isHexString(hexkey)) {
|
|
639
653
|
throw new Error("key must be a valid hex string");
|
|
640
654
|
}
|
|
641
655
|
if (!isHexString(sha256)) {
|
|
@@ -644,6 +658,56 @@ class DataIntegrityTree {
|
|
|
644
658
|
if (!isHexString(expectedRootHash)) {
|
|
645
659
|
throw new Error("expectedRootHash must be a valid hex string");
|
|
646
660
|
}
|
|
661
|
+
if (!verifiedSha256) {
|
|
662
|
+
// Verify the file's SHA-256 hash
|
|
663
|
+
const isFileValid = await this.verifyFileHash(sha256, dataDir);
|
|
664
|
+
if (!isFileValid) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Deserialize and verify the tree
|
|
669
|
+
const tree = this.deserializeAndVerifyTree(serializedTree, expectedRootHash);
|
|
670
|
+
if (!tree) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
// Check if the combined hash exists in the foreign tree's leaves
|
|
674
|
+
const isInTree = this.checkHashInTree(tree, hexkey, sha256);
|
|
675
|
+
return isInTree;
|
|
676
|
+
}
|
|
677
|
+
// Function to verify the file's SHA-256 hash
|
|
678
|
+
static async verifyFileHash(sha256, dataDir) {
|
|
679
|
+
const filePath = path.join(dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
680
|
+
// Check if the file exists
|
|
681
|
+
if (!fs.existsSync(filePath)) {
|
|
682
|
+
throw new Error(`File at path ${filePath} does not exist`);
|
|
683
|
+
}
|
|
684
|
+
const compressedReadStream = fs.createReadStream(filePath);
|
|
685
|
+
const decompressStream = zlib.createGunzip();
|
|
686
|
+
const hash = crypto.createHash("sha256");
|
|
687
|
+
// Process file decompression and hash comparison
|
|
688
|
+
return new Promise((resolve, reject) => {
|
|
689
|
+
compressedReadStream.pipe(decompressStream);
|
|
690
|
+
decompressStream.on("data", (chunk) => {
|
|
691
|
+
hash.update(chunk);
|
|
692
|
+
});
|
|
693
|
+
decompressStream.on("end", () => {
|
|
694
|
+
const uncompressedSha256 = hash.digest("hex");
|
|
695
|
+
if (uncompressedSha256 !== sha256) {
|
|
696
|
+
console.warn(`File hash mismatch. Expected: ${sha256}, got: ${uncompressedSha256}`);
|
|
697
|
+
return resolve(false);
|
|
698
|
+
}
|
|
699
|
+
resolve(true);
|
|
700
|
+
});
|
|
701
|
+
decompressStream.on("error", (err) => {
|
|
702
|
+
reject(err);
|
|
703
|
+
});
|
|
704
|
+
compressedReadStream.on("error", (err) => {
|
|
705
|
+
reject(err);
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
// Function to deserialize and verify the tree
|
|
710
|
+
static deserializeAndVerifyTree(serializedTree, expectedRootHash) {
|
|
647
711
|
// Deserialize the foreign tree
|
|
648
712
|
const leaves = serializedTree.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
649
713
|
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
@@ -651,7 +715,7 @@ class DataIntegrityTree {
|
|
|
651
715
|
const treeRootHash = tree.getRoot().toString("hex");
|
|
652
716
|
if (treeRootHash !== expectedRootHash) {
|
|
653
717
|
console.warn(`Expected root hash ${expectedRootHash}, but got ${treeRootHash}`);
|
|
654
|
-
return
|
|
718
|
+
return null;
|
|
655
719
|
}
|
|
656
720
|
// Rebuild the files map from the serialized tree
|
|
657
721
|
// @ts-ignore
|
|
@@ -659,10 +723,13 @@ class DataIntegrityTree {
|
|
|
659
723
|
key,
|
|
660
724
|
{ hash: value.hash, sha256: value.sha256 },
|
|
661
725
|
]));
|
|
662
|
-
|
|
726
|
+
return tree;
|
|
727
|
+
}
|
|
728
|
+
// Function to check if the combined hash exists in the tree
|
|
729
|
+
static checkHashInTree(tree, hexkey, sha256) {
|
|
663
730
|
const combinedHash = crypto
|
|
664
731
|
.createHash("sha256")
|
|
665
|
-
.update(`${
|
|
732
|
+
.update(`${hexkey}/${sha256}`)
|
|
666
733
|
.digest("hex");
|
|
667
734
|
const leaf = Buffer.from(combinedHash, "hex");
|
|
668
735
|
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
@@ -8,23 +8,40 @@ export declare class ContentServer {
|
|
|
8
8
|
private static readonly port;
|
|
9
9
|
constructor(ipAddress: string, storeId: string);
|
|
10
10
|
getKey(key: string, rootHash: string, challengeHex?: string): Promise<string>;
|
|
11
|
-
|
|
11
|
+
getKeyChunk(key: string, rootHash: string): Promise<Buffer>;
|
|
12
|
+
getPaymentAddress(): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* Fetches and caches the .well-known information for the store's IP address.
|
|
15
|
+
*
|
|
16
|
+
* @returns A promise that resolves to the .well-known JSON data.
|
|
17
|
+
*/
|
|
12
18
|
getWellKnown(): Promise<any>;
|
|
13
19
|
getKnownStores(): Promise<any>;
|
|
14
20
|
getStoresIndex(): Promise<any>;
|
|
15
|
-
getKeysIndex(): Promise<any>;
|
|
16
|
-
headKey(key: string): Promise<{
|
|
21
|
+
getKeysIndex(rootHash?: string): Promise<any>;
|
|
22
|
+
headKey(key: string, rootHash?: string): Promise<{
|
|
17
23
|
success: boolean;
|
|
18
24
|
headers?: http.IncomingHttpHeaders;
|
|
19
25
|
}>;
|
|
20
|
-
headStore(
|
|
26
|
+
headStore(options?: {
|
|
27
|
+
hasRootHash: string;
|
|
28
|
+
}): Promise<{
|
|
21
29
|
success: boolean;
|
|
22
30
|
headers?: http.IncomingHttpHeaders;
|
|
23
31
|
}>;
|
|
24
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Checks if the store has the specified rootHash.
|
|
34
|
+
* Utilizes caching to improve performance.
|
|
35
|
+
*
|
|
36
|
+
* @param rootHash - The root hash to check.
|
|
37
|
+
* @returns A promise that resolves to true if the root hash exists, otherwise false.
|
|
38
|
+
*/
|
|
39
|
+
hasRootHash(rootHash: string): Promise<boolean>;
|
|
40
|
+
streamKey(key: string, rootHash?: string): Promise<Readable>;
|
|
25
41
|
private head;
|
|
26
42
|
private fetchJson;
|
|
27
43
|
private fetchWithRetries;
|
|
28
44
|
private fetch;
|
|
45
|
+
private fetchFirstChunk;
|
|
29
46
|
}
|
|
30
47
|
//# sourceMappingURL=ContentServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAOlC,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;gBAExB,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;IAeL,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAS3D,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcxD;;;;OAIG;IACU,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IA6B5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAQ9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAqB7C,OAAO,CAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;KAAE,CAAC;IAqBvD,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACjE,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;KACpC,CAAC;IAmBF;;;;;;OAMG;IACU,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0BrD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAmDrD,IAAI;YA0EJ,SAAS;YAMT,gBAAgB;YAiChB,KAAK;YA+GL,eAAe;CA4G9B"}
|
|
@@ -7,13 +7,16 @@ exports.ContentServer = void 0;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const http_1 = __importDefault(require("http"));
|
|
9
9
|
const url_1 = require("url");
|
|
10
|
-
const
|
|
10
|
+
const utils_1 = require("../utils");
|
|
11
|
+
const node_cache_1 = __importDefault(require("node-cache"));
|
|
12
|
+
const hasRootHashCache = new node_cache_1.default({ stdTTL: 86400 });
|
|
13
|
+
const wellKnownCache = new node_cache_1.default({ stdTTL: 86400 });
|
|
11
14
|
class ContentServer {
|
|
12
15
|
constructor(ipAddress, storeId) {
|
|
13
16
|
this.ipAddress = ipAddress;
|
|
14
17
|
this.storeId = storeId;
|
|
15
18
|
if (!ContentServer.certPath || !ContentServer.keyPath) {
|
|
16
|
-
const { certPath, keyPath } = (0,
|
|
19
|
+
const { certPath, keyPath } = (0, utils_1.getOrCreateSSLCerts)();
|
|
17
20
|
ContentServer.certPath = certPath;
|
|
18
21
|
ContentServer.keyPath = keyPath;
|
|
19
22
|
}
|
|
@@ -21,13 +24,19 @@ class ContentServer {
|
|
|
21
24
|
// Method to get the content of a specified key from the peer, with optional challenge query
|
|
22
25
|
async getKey(key, rootHash, challengeHex) {
|
|
23
26
|
// Construct the base URL
|
|
24
|
-
let url = `https://${this.ipAddress}/chia.${this.storeId}.${rootHash}/${key}`;
|
|
27
|
+
let url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
|
|
25
28
|
// If a challenge is provided, append it as a query parameter
|
|
26
29
|
if (challengeHex) {
|
|
27
30
|
url += `?challenge=${challengeHex}`;
|
|
28
31
|
}
|
|
29
32
|
return this.fetchWithRetries(url);
|
|
30
33
|
}
|
|
34
|
+
// New method to get only the first chunk of the content
|
|
35
|
+
async getKeyChunk(key, rootHash) {
|
|
36
|
+
// Construct the base URL
|
|
37
|
+
let url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
|
|
38
|
+
return this.fetchFirstChunk(url);
|
|
39
|
+
}
|
|
31
40
|
// Method to get the payment address from the peer
|
|
32
41
|
async getPaymentAddress() {
|
|
33
42
|
console.log(`Fetching payment address from peer ${this.ipAddress}...`);
|
|
@@ -37,43 +46,128 @@ class ContentServer {
|
|
|
37
46
|
}
|
|
38
47
|
catch (error) {
|
|
39
48
|
console.error(`Failed to fetch payment address from ${this.ipAddress}: ${error.message}`);
|
|
40
|
-
|
|
49
|
+
return null;
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Fetches and caches the .well-known information for the store's IP address.
|
|
54
|
+
*
|
|
55
|
+
* @returns A promise that resolves to the .well-known JSON data.
|
|
56
|
+
*/
|
|
44
57
|
async getWellKnown() {
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
// Construct the cache key based on ipAddress
|
|
59
|
+
const cacheKey = `${this.ipAddress}-wellknown`;
|
|
60
|
+
// Check if the result is already cached
|
|
61
|
+
const cachedResult = wellKnownCache.get(cacheKey);
|
|
62
|
+
if (cachedResult !== undefined) {
|
|
63
|
+
return cachedResult;
|
|
64
|
+
}
|
|
65
|
+
// If not cached, proceed to fetch the .well-known information
|
|
66
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/.well-known`;
|
|
67
|
+
try {
|
|
68
|
+
const data = await this.fetchJson(url);
|
|
69
|
+
wellKnownCache.set(cacheKey, data);
|
|
70
|
+
return data;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`Error fetching .well-known information for ${this.ipAddress}:`, error.message);
|
|
74
|
+
throw error; // Propagate the error after logging
|
|
75
|
+
}
|
|
47
76
|
}
|
|
48
77
|
// Method to get the list of known stores
|
|
49
78
|
async getKnownStores() {
|
|
50
|
-
const url = `https://${this.ipAddress}/.well-known/stores`;
|
|
79
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/.well-known/stores`;
|
|
51
80
|
return this.fetchJson(url);
|
|
52
81
|
}
|
|
53
82
|
// Method to get the index of all stores
|
|
54
83
|
async getStoresIndex() {
|
|
55
|
-
const url = `https://${this.ipAddress}/`;
|
|
84
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/`;
|
|
56
85
|
return this.fetchJson(url);
|
|
57
86
|
}
|
|
58
87
|
// Method to get the index of keys in a store
|
|
59
|
-
async getKeysIndex() {
|
|
60
|
-
|
|
61
|
-
|
|
88
|
+
async getKeysIndex(rootHash) {
|
|
89
|
+
try {
|
|
90
|
+
let udi = `chia.${this.storeId}`;
|
|
91
|
+
if (rootHash) {
|
|
92
|
+
udi += `.${rootHash}`;
|
|
93
|
+
}
|
|
94
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/${udi}`;
|
|
95
|
+
return this.fetchJson(url);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (rootHash) {
|
|
99
|
+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
62
103
|
}
|
|
63
104
|
// Method to check if a specific key exists (HEAD request)
|
|
64
|
-
async headKey(key) {
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
async headKey(key, rootHash) {
|
|
106
|
+
try {
|
|
107
|
+
let udi = `chia.${this.storeId}`;
|
|
108
|
+
if (rootHash) {
|
|
109
|
+
udi += `.${rootHash}`;
|
|
110
|
+
}
|
|
111
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/${udi}/${key}`;
|
|
112
|
+
return this.head(url);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (rootHash) {
|
|
116
|
+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
67
120
|
}
|
|
68
121
|
// Method to check if a specific store exists (HEAD request)
|
|
69
|
-
async headStore() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
122
|
+
async headStore(options) {
|
|
123
|
+
try {
|
|
124
|
+
let url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}`;
|
|
125
|
+
if (options?.hasRootHash) {
|
|
126
|
+
url += `?hasRootHash=${options.hasRootHash}`;
|
|
127
|
+
}
|
|
128
|
+
return this.head(url);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (options?.hasRootHash) {
|
|
132
|
+
hasRootHashCache.del(`${this.storeId}-${options.hasRootHash}`);
|
|
133
|
+
}
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
73
136
|
}
|
|
74
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Checks if the store has the specified rootHash.
|
|
139
|
+
* Utilizes caching to improve performance.
|
|
140
|
+
*
|
|
141
|
+
* @param rootHash - The root hash to check.
|
|
142
|
+
* @returns A promise that resolves to true if the root hash exists, otherwise false.
|
|
143
|
+
*/
|
|
144
|
+
async hasRootHash(rootHash) {
|
|
145
|
+
// Construct the cache key using storeId and rootHash
|
|
146
|
+
const cacheKey = `${this.storeId}-${rootHash}`;
|
|
147
|
+
// Check if the result is already cached
|
|
148
|
+
const cachedResult = await hasRootHashCache.get(cacheKey);
|
|
149
|
+
if (cachedResult !== undefined) {
|
|
150
|
+
return cachedResult;
|
|
151
|
+
}
|
|
152
|
+
// If not cached, perform the headStore request
|
|
153
|
+
const { success, headers } = await this.headStore({
|
|
154
|
+
hasRootHash: rootHash,
|
|
155
|
+
});
|
|
156
|
+
// Determine if the store has the root hash
|
|
157
|
+
const hasHash = success && headers?.["x-has-root-hash"] === "true";
|
|
158
|
+
// Only cache the result if the store has the root hash
|
|
159
|
+
if (hasHash) {
|
|
160
|
+
hasRootHashCache.set(cacheKey, true);
|
|
161
|
+
}
|
|
162
|
+
return hasHash;
|
|
163
|
+
}
|
|
164
|
+
streamKey(key, rootHash) {
|
|
165
|
+
let udi = `chia.${this.storeId}`;
|
|
166
|
+
if (rootHash) {
|
|
167
|
+
udi += `.${rootHash}`;
|
|
168
|
+
}
|
|
75
169
|
return new Promise((resolve, reject) => {
|
|
76
|
-
const url = `https://${this.ipAddress}
|
|
170
|
+
const url = `https://${(0, utils_1.formatHost)(this.ipAddress)}:${ContentServer.port}/${udi}/${key}`;
|
|
77
171
|
const urlObj = new url_1.URL(url);
|
|
78
172
|
const requestOptions = {
|
|
79
173
|
hostname: urlObj.hostname,
|
|
@@ -96,11 +190,12 @@ class ContentServer {
|
|
|
96
190
|
}
|
|
97
191
|
}
|
|
98
192
|
else {
|
|
193
|
+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
|
|
99
194
|
reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
|
|
100
195
|
}
|
|
101
196
|
});
|
|
102
197
|
request.on("error", (error) => {
|
|
103
|
-
console.error(`Request error for ${url}:`, error);
|
|
198
|
+
console.error(`GET Request error for ${url}:`, error);
|
|
104
199
|
reject(error);
|
|
105
200
|
});
|
|
106
201
|
request.end();
|
|
@@ -159,7 +254,7 @@ class ContentServer {
|
|
|
159
254
|
}
|
|
160
255
|
});
|
|
161
256
|
request.on("error", (error) => {
|
|
162
|
-
console.error(`
|
|
257
|
+
console.error(`HEAD ${url}:`, error.message);
|
|
163
258
|
reject({ success: false });
|
|
164
259
|
});
|
|
165
260
|
request.end();
|
|
@@ -178,7 +273,7 @@ class ContentServer {
|
|
|
178
273
|
// Helper method to fetch content with retries and redirection handling
|
|
179
274
|
async fetchWithRetries(url) {
|
|
180
275
|
let attempt = 0;
|
|
181
|
-
const maxRetries =
|
|
276
|
+
const maxRetries = 1;
|
|
182
277
|
const initialDelay = 2000; // 2 seconds
|
|
183
278
|
const maxDelay = 10000; // 10 seconds
|
|
184
279
|
const delayMultiplier = 1.5;
|
|
@@ -202,10 +297,12 @@ class ContentServer {
|
|
|
202
297
|
}
|
|
203
298
|
throw new Error(`Failed to retrieve data from ${url} after ${maxRetries} attempts.`);
|
|
204
299
|
}
|
|
205
|
-
// Core method to fetch content from a URL
|
|
300
|
+
// Core method to fetch content from a URL with a 5-second inactivity timeout
|
|
206
301
|
async fetch(url, maxRedirects = 5) {
|
|
207
302
|
return new Promise((resolve, reject) => {
|
|
208
303
|
const urlObj = new url_1.URL(url);
|
|
304
|
+
const timeoutDuration = 5000; // 5 seconds
|
|
305
|
+
let timeout = null; // Initialize timeout
|
|
209
306
|
const requestOptions = {
|
|
210
307
|
hostname: urlObj.hostname,
|
|
211
308
|
port: urlObj.port || ContentServer.port,
|
|
@@ -217,11 +314,31 @@ class ContentServer {
|
|
|
217
314
|
};
|
|
218
315
|
const request = http_1.default.request(requestOptions, (response) => {
|
|
219
316
|
let data = "";
|
|
317
|
+
// Set timeout for inactivity
|
|
318
|
+
timeout = setTimeout(() => {
|
|
319
|
+
console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
|
|
320
|
+
request.destroy(); // Use destroy instead of abort
|
|
321
|
+
reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
|
|
322
|
+
}, timeoutDuration);
|
|
323
|
+
const resetTimeout = () => {
|
|
324
|
+
if (timeout) {
|
|
325
|
+
clearTimeout(timeout);
|
|
326
|
+
}
|
|
327
|
+
timeout = setTimeout(() => {
|
|
328
|
+
console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
|
|
329
|
+
request.destroy(); // Use destroy instead of abort
|
|
330
|
+
reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
|
|
331
|
+
}, timeoutDuration);
|
|
332
|
+
};
|
|
220
333
|
if (response.statusCode === 200) {
|
|
221
334
|
response.on("data", (chunk) => {
|
|
222
335
|
data += chunk;
|
|
336
|
+
resetTimeout(); // Reset the timeout every time data is received
|
|
223
337
|
});
|
|
224
338
|
response.on("end", () => {
|
|
339
|
+
if (timeout) {
|
|
340
|
+
clearTimeout(timeout);
|
|
341
|
+
}
|
|
225
342
|
resolve(data);
|
|
226
343
|
});
|
|
227
344
|
}
|
|
@@ -230,8 +347,9 @@ class ContentServer {
|
|
|
230
347
|
// Handle redirects
|
|
231
348
|
if (maxRedirects > 0) {
|
|
232
349
|
const redirectUrl = new url_1.URL(response.headers.location, url); // Resolve relative URLs based on the original URL
|
|
233
|
-
|
|
234
|
-
|
|
350
|
+
if (timeout) {
|
|
351
|
+
clearTimeout(timeout);
|
|
352
|
+
}
|
|
235
353
|
this.fetch(redirectUrl.toString(), maxRedirects - 1)
|
|
236
354
|
.then(resolve)
|
|
237
355
|
.catch(reject);
|
|
@@ -241,11 +359,91 @@ class ContentServer {
|
|
|
241
359
|
}
|
|
242
360
|
}
|
|
243
361
|
else {
|
|
362
|
+
if (timeout) {
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
}
|
|
365
|
+
reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
request.on("error", (error) => {
|
|
369
|
+
if (timeout) {
|
|
370
|
+
clearTimeout(timeout);
|
|
371
|
+
}
|
|
372
|
+
console.error(`GET ${url}:`, error.message);
|
|
373
|
+
reject(error);
|
|
374
|
+
});
|
|
375
|
+
request.end();
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
// New core method to fetch only the first chunk without retries
|
|
379
|
+
async fetchFirstChunk(url) {
|
|
380
|
+
return new Promise((resolve, reject) => {
|
|
381
|
+
const urlObj = new url_1.URL(url);
|
|
382
|
+
const timeoutDuration = 5000; // 5 seconds
|
|
383
|
+
let timeout = null;
|
|
384
|
+
const requestOptions = {
|
|
385
|
+
hostname: urlObj.hostname,
|
|
386
|
+
port: urlObj.port || ContentServer.port,
|
|
387
|
+
path: urlObj.pathname + urlObj.search, // Include query params
|
|
388
|
+
method: "GET",
|
|
389
|
+
key: fs_1.default.readFileSync(ContentServer.keyPath),
|
|
390
|
+
cert: fs_1.default.readFileSync(ContentServer.certPath),
|
|
391
|
+
rejectUnauthorized: false,
|
|
392
|
+
};
|
|
393
|
+
const request = http_1.default.request(requestOptions, (response) => {
|
|
394
|
+
// Set timeout for inactivity
|
|
395
|
+
timeout = setTimeout(() => {
|
|
396
|
+
console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
|
|
397
|
+
request.destroy(); // Use destroy instead of abort
|
|
398
|
+
reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
|
|
399
|
+
}, timeoutDuration);
|
|
400
|
+
const resetTimeout = () => {
|
|
401
|
+
if (timeout) {
|
|
402
|
+
clearTimeout(timeout);
|
|
403
|
+
}
|
|
404
|
+
timeout = setTimeout(() => {
|
|
405
|
+
console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
|
|
406
|
+
request.destroy(); // Use destroy instead of abort
|
|
407
|
+
reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
|
|
408
|
+
}, timeoutDuration);
|
|
409
|
+
};
|
|
410
|
+
if (response.statusCode === 200) {
|
|
411
|
+
response.once("data", (chunk) => {
|
|
412
|
+
if (timeout) {
|
|
413
|
+
clearTimeout(timeout);
|
|
414
|
+
}
|
|
415
|
+
response.destroy(); // Close the connection after receiving the first chunk
|
|
416
|
+
resolve(chunk);
|
|
417
|
+
});
|
|
418
|
+
response.on("end", () => {
|
|
419
|
+
if (timeout) {
|
|
420
|
+
clearTimeout(timeout);
|
|
421
|
+
}
|
|
422
|
+
// In case the response ends before any data is received
|
|
423
|
+
reject(new Error("No data received"));
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
else if ((response.statusCode === 301 || response.statusCode === 302) &&
|
|
427
|
+
response.headers.location) {
|
|
428
|
+
// Handle redirects
|
|
429
|
+
const redirectUrl = new url_1.URL(response.headers.location, url).toString(); // Resolve relative URLs
|
|
430
|
+
if (timeout) {
|
|
431
|
+
clearTimeout(timeout);
|
|
432
|
+
}
|
|
433
|
+
this.fetchFirstChunk(redirectUrl).then(resolve).catch(reject);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
if (timeout) {
|
|
437
|
+
clearTimeout(timeout);
|
|
438
|
+
}
|
|
244
439
|
reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
|
|
245
440
|
}
|
|
246
441
|
});
|
|
247
442
|
request.on("error", (error) => {
|
|
248
|
-
|
|
443
|
+
if (timeout) {
|
|
444
|
+
clearTimeout(timeout);
|
|
445
|
+
}
|
|
446
|
+
console.error(`GET ${url}:`, error.message);
|
|
249
447
|
reject(error);
|
|
250
448
|
});
|
|
251
449
|
request.end();
|