Skip to content

Commit 1735d9e

Browse files
Merge pull request #141 from DIG-Network/release/v0.0.1-alpha.156
Release/v0.0.1 alpha.156
2 parents 147dbb1 + 47e25a2 commit 1735d9e

File tree

6 files changed

+172
-44
lines changed

6 files changed

+172
-44
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
### [0.0.1-alpha.156](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.155...v0.0.1-alpha.156) (2024-10-07)
6+
7+
8+
### Features
9+
10+
* additional caching measure to optimize requests ([e2519b1](https://github.com/DIG-Network/dig-chia-sdk/commit/e2519b1cb9fed7889b0026079cfeb3e86c72c363))
11+
512
### [0.0.1-alpha.155](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.154...v0.0.1-alpha.155) (2024-10-06)
613

714

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@dignetwork/dig-sdk",
3-
"version": "0.0.1-alpha.155",
3+
"version": "0.0.1-alpha.156",
44
"description": "",
55
"type": "commonjs",
66
"main": "./dist/index.js",

src/DigNetwork/ContentServer.ts

Lines changed: 124 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { URL } from "url";
44
import { Readable } from "stream";
55
import { getOrCreateSSLCerts } from "../utils/ssl";
66
import { formatHost } from "../utils/network";
7+
import NodeCache from "node-cache";
8+
9+
const hasRootHashCache = new NodeCache({ stdTTL: 86400 });
10+
const wellKnownCache = new NodeCache({ stdTTL: 86400 });
711

812
export class ContentServer {
913
private ipAddress: string;
@@ -30,7 +34,9 @@ export class ContentServer {
3034
challengeHex?: string
3135
): Promise<string> {
3236
// Construct the base URL
33-
let url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
37+
let url = `https://${formatHost(this.ipAddress)}:${
38+
ContentServer.port
39+
}/chia.${this.storeId}.${rootHash}/${key}`;
3440

3541
// If a challenge is provided, append it as a query parameter
3642
if (challengeHex) {
@@ -40,13 +46,12 @@ export class ContentServer {
4046
return this.fetchWithRetries(url);
4147
}
4248

43-
// New method to get only the first chunk of the content
44-
public async getKeyChunk(
45-
key: string,
46-
rootHash: string,
47-
): Promise<Buffer> {
49+
// New method to get only the first chunk of the content
50+
public async getKeyChunk(key: string, rootHash: string): Promise<Buffer> {
4851
// Construct the base URL
49-
let url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
52+
let url = `https://${formatHost(this.ipAddress)}:${
53+
ContentServer.port
54+
}/chia.${this.storeId}.${rootHash}/${key}`;
5055
return this.fetchFirstChunk(url);
5156
}
5257

@@ -65,15 +70,44 @@ export class ContentServer {
6570
}
6671
}
6772

68-
// Method to get the .well-known information
73+
/**
74+
* Fetches and caches the .well-known information for the store's IP address.
75+
*
76+
* @returns A promise that resolves to the .well-known JSON data.
77+
*/
6978
public async getWellKnown(): Promise<any> {
70-
const url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/.well-known`;
71-
return this.fetchJson(url);
79+
// Construct the cache key based on ipAddress
80+
const cacheKey = `${this.ipAddress}-wellknown`;
81+
82+
// Check if the result is already cached
83+
const cachedResult = wellKnownCache.get<any>(cacheKey);
84+
if (cachedResult !== undefined) {
85+
return cachedResult;
86+
}
87+
88+
// If not cached, proceed to fetch the .well-known information
89+
const url = `https://${formatHost(this.ipAddress)}:${
90+
ContentServer.port
91+
}/.well-known`;
92+
93+
try {
94+
const data = await this.fetchJson(url);
95+
wellKnownCache.set(cacheKey, data);
96+
return data;
97+
} catch (error: any) {
98+
console.error(
99+
`Error fetching .well-known information for ${this.ipAddress}:`,
100+
error.message
101+
);
102+
throw error; // Propagate the error after logging
103+
}
72104
}
73105

74106
// Method to get the list of known stores
75107
public async getKnownStores(): Promise<any> {
76-
const url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/.well-known/stores`;
108+
const url = `https://${formatHost(this.ipAddress)}:${
109+
ContentServer.port
110+
}/.well-known/stores`;
77111
return this.fetchJson(url);
78112
}
79113

@@ -85,53 +119,103 @@ export class ContentServer {
85119

86120
// Method to get the index of keys in a store
87121
public async getKeysIndex(rootHash?: string): Promise<any> {
88-
let udi = `chia.${this.storeId}`;
122+
try {
123+
let udi = `chia.${this.storeId}`;
89124

90-
if (rootHash) {
91-
udi += `.${rootHash}`;
92-
}
125+
if (rootHash) {
126+
udi += `.${rootHash}`;
127+
}
93128

94-
const url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/${udi}`;
95-
return this.fetchJson(url);
129+
const url = `https://${formatHost(this.ipAddress)}:${
130+
ContentServer.port
131+
}/${udi}`;
132+
return this.fetchJson(url);
133+
} catch (error: any) {
134+
if (rootHash) {
135+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
136+
}
137+
throw error;
138+
}
96139
}
97140

98141
// Method to check if a specific key exists (HEAD request)
99142
public async headKey(
100143
key: string,
101144
rootHash?: string
102145
): Promise<{ success: boolean; headers?: http.IncomingHttpHeaders }> {
103-
let udi = `chia.${this.storeId}`;
146+
try {
147+
let udi = `chia.${this.storeId}`;
104148

105-
if (rootHash) {
106-
udi += `.${rootHash}`;
107-
}
149+
if (rootHash) {
150+
udi += `.${rootHash}`;
151+
}
108152

109-
const url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/${udi}/${key}`;
110-
return this.head(url);
153+
const url = `https://${formatHost(this.ipAddress)}:${
154+
ContentServer.port
155+
}/${udi}/${key}`;
156+
return this.head(url);
157+
} catch (error: any) {
158+
if (rootHash) {
159+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
160+
}
161+
throw error;
162+
}
111163
}
112164

113165
// Method to check if a specific store exists (HEAD request)
114166
public async headStore(options?: { hasRootHash: string }): Promise<{
115167
success: boolean;
116168
headers?: http.IncomingHttpHeaders;
117169
}> {
118-
let url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/chia.${this.storeId}`;
170+
try {
171+
let url = `https://${formatHost(this.ipAddress)}:${
172+
ContentServer.port
173+
}/chia.${this.storeId}`;
119174

120-
if (options?.hasRootHash) {
121-
url += `?hasRootHash=${options.hasRootHash}`;
122-
}
175+
if (options?.hasRootHash) {
176+
url += `?hasRootHash=${options.hasRootHash}`;
177+
}
123178

124-
return this.head(url);
179+
return this.head(url);
180+
} catch (error: any) {
181+
if (options?.hasRootHash) {
182+
hasRootHashCache.del(`${this.storeId}-${options.hasRootHash}`);
183+
}
184+
throw error;
185+
}
125186
}
126187

188+
/**
189+
* Checks if the store has the specified rootHash.
190+
* Utilizes caching to improve performance.
191+
*
192+
* @param rootHash - The root hash to check.
193+
* @returns A promise that resolves to true if the root hash exists, otherwise false.
194+
*/
127195
public async hasRootHash(rootHash: string): Promise<boolean> {
196+
// Construct the cache key using storeId and rootHash
197+
const cacheKey = `${this.storeId}-${rootHash}`;
198+
199+
// Check if the result is already cached
200+
const cachedResult = hasRootHashCache.get<boolean>(cacheKey);
201+
if (cachedResult !== undefined) {
202+
return cachedResult;
203+
}
204+
205+
// If not cached, perform the headStore request
128206
const { success, headers } = await this.headStore({
129207
hasRootHash: rootHash,
130208
});
131-
if (success) {
132-
return headers?.["x-has-root-hash"] === "true";
209+
210+
// Determine if the store has the root hash
211+
const hasHash = success && headers?.["x-has-root-hash"] === "true";
212+
213+
// Only cache the result if the store has the root hash
214+
if (hasHash) {
215+
hasRootHashCache.set(cacheKey, true);
133216
}
134-
return false;
217+
218+
return hasHash;
135219
}
136220

137221
public streamKey(key: string, rootHash?: string): Promise<Readable> {
@@ -142,7 +226,9 @@ export class ContentServer {
142226
}
143227

144228
return new Promise((resolve, reject) => {
145-
const url = `https://${formatHost(this.ipAddress)}:${ContentServer.port}/${udi}/${key}`;
229+
const url = `https://${formatHost(this.ipAddress)}:${
230+
ContentServer.port
231+
}/${udi}/${key}`;
146232
const urlObj = new URL(url);
147233

148234
const requestOptions = {
@@ -164,6 +250,7 @@ export class ContentServer {
164250
reject(new Error("Redirected without a location header"));
165251
}
166252
} else {
253+
hasRootHashCache.del(`${this.storeId}-${rootHash}`);
167254
reject(
168255
new Error(
169256
`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`
@@ -483,13 +570,14 @@ export class ContentServer {
483570
response.headers.location
484571
) {
485572
// Handle redirects
486-
const redirectUrl = new URL(response.headers.location, url).toString(); // Resolve relative URLs
573+
const redirectUrl = new URL(
574+
response.headers.location,
575+
url
576+
).toString(); // Resolve relative URLs
487577
if (timeout) {
488578
clearTimeout(timeout);
489579
}
490-
this.fetchFirstChunk(redirectUrl)
491-
.then(resolve)
492-
.catch(reject);
580+
this.fetchFirstChunk(redirectUrl).then(resolve).catch(reject);
493581
} else {
494582
if (timeout) {
495583
clearTimeout(timeout);

src/DigNetwork/PropagationServer.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import { promptCredentials } from "../utils/credentialsUtils";
2020
import { STORE_PATH } from "../utils/config";
2121
import { Wallet, DataStore } from "../blockchain";
2222
import { formatHost } from "../utils/network";
23+
import NodeCache from "node-cache";
24+
25+
// Initialize cache with a TTL of 1 week (604800 seconds)
26+
const storeExistsCache = new NodeCache({ stdTTL: 86400 });
2327

2428
// Helper function to trim long filenames with ellipsis and ensure consistent padding
2529
function formatFilename(filename: string | undefined, maxLength = 30): string {
@@ -140,7 +144,10 @@ export class PropagationServer {
140144

141145
try {
142146
const response = await axios.post(url, data, config);
143-
console.log(green(`✔ Successfully pinged peer: ${this.ipAddress}`));
147+
console.log(
148+
green(`✔ Successfully pinged peer: ${this.ipAddress}`),
149+
response
150+
);
144151
return response.data;
145152
} catch (error: any) {
146153
console.error(red(`✖ Failed to ping peer: ${this.ipAddress}`));
@@ -181,14 +188,34 @@ export class PropagationServer {
181188
}
182189

183190
/**
184-
* Check if the store and optional root hash exist by making a HEAD request.
191+
* Checks if the store exists and optionally if a specific rootHash exists.
192+
* Utilizes caching to improve performance.
193+
*
194+
* @param rootHash - (Optional) The root hash to check.
195+
* @returns A promise that resolves to an object containing store existence and root hash existence.
185196
*/
186-
async checkStoreExists(
197+
public async checkStoreExists(
187198
rootHash?: string
188199
): Promise<{ storeExists: boolean; rootHashExists: boolean }> {
200+
// Construct the cache key
201+
const cacheKey = rootHash
202+
? `${this.storeId}-${rootHash}`
203+
: `${this.storeId}-nohash`;
204+
205+
// Check if the result is already cached
206+
const cachedResult = storeExistsCache.get<{
207+
storeExists: boolean;
208+
rootHashExists: boolean;
209+
}>(cacheKey);
210+
if (cachedResult !== undefined) {
211+
return cachedResult;
212+
}
213+
214+
// If not cached, proceed with the HTTP request
189215
const spinner = createSpinner(
190216
`Checking if store ${this.storeId} exists...`
191217
).start();
218+
192219
try {
193220
const config: AxiosRequestConfig = {
194221
httpsAgent: this.createHttpsAgent(),
@@ -221,7 +248,11 @@ export class PropagationServer {
221248
});
222249
}
223250

224-
return { storeExists, rootHashExists };
251+
const result = { storeExists, rootHashExists };
252+
253+
storeExistsCache.set(cacheKey, result);
254+
255+
return result;
225256
} catch (error: any) {
226257
spinner.error({ text: red("Error checking if store exists:") });
227258
console.error(red(error.message));
@@ -625,6 +656,7 @@ export class PropagationServer {
625656

626657
return Buffer.concat(dataBuffers);
627658
} catch (error) {
659+
storeExistsCache.del(`${this.storeId}-nohash`);
628660
throw error;
629661
} finally {
630662
if (progressBar) {
@@ -773,6 +805,8 @@ export class PropagationServer {
773805
console.log("integrity check");
774806
}
775807
} catch (error) {
808+
storeExistsCache.del(`${this.storeId}-nohash`);
809+
storeExistsCache.del(`${this.storeId}-${rootHash}`);
776810
throw error;
777811
} finally {
778812
if (progressBar) {

src/utils/PeerRanker.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ export class PeerRanker {
109109
private async measureBandwidth(ip: string): Promise<number> {
110110
const cachedMetrics = peerCache.get<PeerMetrics>(ip);
111111
if (cachedMetrics && cachedMetrics.bandwidth) {
112-
console.log(`Bandwidth for IP ${ip} retrieved from cache.`);
113112
return cachedMetrics.bandwidth;
114113
}
115114

0 commit comments

Comments
 (0)