@dignetwork/dig-sdk 0.0.1-alpha.125 → 0.0.1-alpha.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DigNetwork/DigNetwork.d.ts +1 -0
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +3 -0
- package/dist/blockchain/DataStore.d.ts +5 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +25 -135
- package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
- package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
- package/dist/blockchain/StoreMonitorRegistry.js +242 -0
- 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/package.json +1 -1
- package/dist/blockchain/StoreInfoCacheUpdater.d.ts +0 -16
- package/dist/blockchain/StoreInfoCacheUpdater.d.ts.map +0 -1
- package/dist/blockchain/StoreInfoCacheUpdater.js +0 -242
|
@@ -6,6 +6,7 @@ export declare class DigNetwork {
|
|
|
6
6
|
private peerBlacklist;
|
|
7
7
|
constructor(storeId: string);
|
|
8
8
|
static subscribeToStore(storeId: string): Promise<void>;
|
|
9
|
+
static getUdiContent(udi: string): Promise<void>;
|
|
9
10
|
static findPeerWithStoreKey(storeId: string, rootHash: string, key?: string, intialBlackList?: string[]): Promise<DigPeer | null>;
|
|
10
11
|
static unsubscribeFromStore(storeId: string): void;
|
|
11
12
|
syncStoreFromPeers(maxRootsToProcess?: number): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,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;WAOP,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;WA2DZ,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO5C,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiG7D,mBAAmB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAUvD"}
|
|
1
|
+
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,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;WAOP,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQhD,aAAa,CAAC,GAAG,EAAE,MAAM;WAIzB,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;WA2DZ,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO5C,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiG7D,mBAAmB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAUvD"}
|
|
@@ -46,6 +46,9 @@ class DigNetwork {
|
|
|
46
46
|
const digNetwork = new DigNetwork(storeId);
|
|
47
47
|
await digNetwork.syncStoreFromPeers();
|
|
48
48
|
}
|
|
49
|
+
static async getUdiContent(udi) {
|
|
50
|
+
// TODO: Implement this method
|
|
51
|
+
}
|
|
49
52
|
static async findPeerWithStoreKey(storeId, rootHash, key, intialBlackList = []) {
|
|
50
53
|
const peerBlackList = intialBlackList;
|
|
51
54
|
const serverCoin = new blockchain_1.ServerCoin(storeId);
|
|
@@ -35,13 +35,11 @@ export declare class DataStore {
|
|
|
35
35
|
private static calculateFolderSize;
|
|
36
36
|
static getAllStores(): DataStore[];
|
|
37
37
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*/
|
|
44
|
-
static monitorStoreIndefinitely(storeId: string): Promise<void>;
|
|
38
|
+
* Fetches the latest coin information using the StoreMonitorRegistry.
|
|
39
|
+
* Registers the storeId if it's not already registered, retrieves the cached value, and returns it.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<{ latestStore: DataStoreDriver; latestHeight: number; latestHash: Buffer }>}
|
|
42
|
+
*/
|
|
45
43
|
fetchCoinInfo(): Promise<{
|
|
46
44
|
latestStore: DataStoreDriver;
|
|
47
45
|
latestHeight: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataStore.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,IAAI,eAAe,EAE5B,iBAAiB,EAIlB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"DataStore.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,IAAI,eAAe,EAE5B,iBAAiB,EAIlB,MAAM,8BAA8B,CAAC;AAWtC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAcjD,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAmC;gBAEpD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,wBAAwB;IAiB/D,IAAW,OAAO,IAAI,MAAM,CAE3B;IAED,IAAW,IAAI,IAAI,iBAAiB,CAEnC;IAEM,QAAQ,IAAI,MAAM;IAIlB,QAAQ,IAAI,MAAM;IAIlB,SAAS,IAAI,MAAM;WAMN,cAAc,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;WAOtD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS;WAK1C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS;WAiBtD,MAAM,CACxB,MAAM,GAAE,qBAA0B,GACjC,OAAO,CAAC,SAAS,CAAC;mBAyCA,IAAI;IA4FzB;;;OAGG;IACU,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5C;;;OAGG;WACiB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxD;;;;OAIG;mBACkB,mBAAmB;WAqB1B,YAAY,IAAI,SAAS,EAAE;IAWzC;;;;;OAKG;IACU,aAAa,IAAI,OAAO,CAAC;QACpC,WAAW,EAAE,eAAe,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAkCW,wBAAwB,IAAI,OAAO,CAAC;QAC/C,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IAiBW,iBAAiB,IAAI,OAAO,CAAC;QACxC,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;YA0BY,iBAAiB;IAYlB,cAAc,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA4C/D,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAexD,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAKzC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAM5B,uBAAuB,CAClC,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkBN,cAAc,CACzB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,eAAe,CAAC;IAgD3B;;;;;OAKG;IACU,qBAAqB,CAChC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,GAAE,OAAe,GAClC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CA6C7C"}
|
|
@@ -20,7 +20,7 @@ const FileCache_1 = require("../utils/FileCache");
|
|
|
20
20
|
const DataStoreSerializer_1 = require("./DataStoreSerializer");
|
|
21
21
|
const node_cache_1 = __importDefault(require("node-cache"));
|
|
22
22
|
const config_2 = require("../utils/config");
|
|
23
|
-
const
|
|
23
|
+
const StoreMonitorRegistry_1 = require("./StoreMonitorRegistry");
|
|
24
24
|
// Initialize the cache with a TTL of 180 seconds (3 minutes)
|
|
25
25
|
const rootHistoryCache = new node_cache_1.default({ stdTTL: 180 });
|
|
26
26
|
const stat = (0, util_1.promisify)(fs_1.default.stat);
|
|
@@ -39,9 +39,6 @@ class DataStore {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
this.tree = new DataIntegrityTree_1.DataIntegrityTree(storeId, _options);
|
|
42
|
-
if (!utils_1.Environment.CLI_MODE) {
|
|
43
|
-
DataStore.monitorStoreIndefinitely(this.storeId);
|
|
44
|
-
}
|
|
45
42
|
}
|
|
46
43
|
get StoreId() {
|
|
47
44
|
return this.storeId;
|
|
@@ -205,143 +202,36 @@ class DataStore {
|
|
|
205
202
|
return storIds.map((storeId) => DataStore.from(storeId));
|
|
206
203
|
}
|
|
207
204
|
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
*/
|
|
214
|
-
static async monitorStoreIndefinitely(storeId) {
|
|
215
|
-
// Check if a monitor is already running for this storeId
|
|
216
|
-
if (this.activeMonitors.get(storeId)) {
|
|
217
|
-
console.log("Monitor:", `Monitor already running for storeId: ${storeId}`);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
// Set the monitor as active
|
|
221
|
-
this.activeMonitors.set(storeId, true);
|
|
222
|
-
const storeCoinCache = new FileCache_1.FileCache(`stores`);
|
|
223
|
-
// Clear the cache at the start
|
|
224
|
-
console.log("Monitor:", `Clearing cache for storeId: ${storeId}`);
|
|
225
|
-
storeCoinCache.delete(storeId);
|
|
226
|
-
while (true) {
|
|
227
|
-
try {
|
|
228
|
-
console.log("Monitor:", `Connecting to peer for storeId: ${storeId}`);
|
|
229
|
-
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
230
|
-
const cachedInfo = storeCoinCache.get(storeId);
|
|
231
|
-
if (cachedInfo) {
|
|
232
|
-
// Log cached store info retrieval
|
|
233
|
-
console.log("Monitor:", `Cached store info found for storeId: ${storeId}, syncing...`);
|
|
234
|
-
// Deserialize cached info and wait for the coin to be spent
|
|
235
|
-
const previousStore = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
236
|
-
latestStore: cachedInfo.latestStore,
|
|
237
|
-
latestHeight: cachedInfo.latestHeight.toString(),
|
|
238
|
-
latestHash: cachedInfo.latestHash,
|
|
239
|
-
});
|
|
240
|
-
console.log("Monitor:", `Waiting for coin to be spent for storeId: ${storeId}...`);
|
|
241
|
-
const dataStore = DataStore.from(storeId);
|
|
242
|
-
const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
|
|
243
|
-
await peer.waitForCoinToBeSpent((0, datalayer_driver_1.getCoinId)(previousStore.latestStore.coin), createdAtHeight, createdAtHash);
|
|
244
|
-
// Sync store and get updated details
|
|
245
|
-
console.log("Monitor:", `Syncing store for storeId: ${storeId}`);
|
|
246
|
-
const { latestStore, latestHeight } = await peer.syncStore(previousStore.latestStore, createdAtHeight, createdAtHash, false);
|
|
247
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
248
|
-
console.log("Monitor:", `Store synced for storeId: ${storeId}, coin id: ${(0, datalayer_driver_1.getCoinId)(latestStore.coin).toString("hex")}`);
|
|
249
|
-
// Serialize and cache the updated store info
|
|
250
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
251
|
-
console.log("Monitor:", `Caching updated store info for storeId: ${storeId}`);
|
|
252
|
-
storeCoinCache.set(storeId, {
|
|
253
|
-
latestStore: serializedLatestStore,
|
|
254
|
-
latestHeight,
|
|
255
|
-
latestHash: latestHash.toString("hex"),
|
|
256
|
-
});
|
|
257
|
-
continue; // Continue monitoring
|
|
258
|
-
}
|
|
259
|
-
// If no cached info exists, log and sync from the creation height
|
|
260
|
-
console.log("Monitor:", `No cached info found for storeId: ${storeId}. Retrieving creation height.`);
|
|
261
|
-
const dataStore = DataStore.from(storeId);
|
|
262
|
-
const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
|
|
263
|
-
// Sync store from the peer using launcher ID
|
|
264
|
-
console.log("Monitor:", `Syncing store from launcher ID for storeId: ${storeId}`);
|
|
265
|
-
const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId(Buffer.from(storeId, "hex"), createdAtHeight, createdAtHash, false);
|
|
266
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
267
|
-
// Serialize and cache the new store info
|
|
268
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
269
|
-
console.log("Monitor:", `Caching new store info for storeId: ${storeId}`);
|
|
270
|
-
storeCoinCache.set(storeId, {
|
|
271
|
-
latestStore: serializedLatestStore,
|
|
272
|
-
latestHeight,
|
|
273
|
-
latestHash: latestHash.toString("hex"),
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
catch (error) {
|
|
277
|
-
console.error("Monitor:", `Error in monitorStoreIndefinitely for storeId: ${storeId} - ${error.message}`);
|
|
278
|
-
// Delay before restarting to avoid rapid retries
|
|
279
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
205
|
+
* Fetches the latest coin information using the StoreMonitorRegistry.
|
|
206
|
+
* Registers the storeId if it's not already registered, retrieves the cached value, and returns it.
|
|
207
|
+
*
|
|
208
|
+
* @returns {Promise<{ latestStore: DataStoreDriver; latestHeight: number; latestHash: Buffer }>}
|
|
209
|
+
*/
|
|
283
210
|
async fetchCoinInfo() {
|
|
211
|
+
const storeMonitor = StoreMonitorRegistry_1.StoreMonitorRegistry.getInstance();
|
|
284
212
|
try {
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const { latestStore: previousInfo } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
294
|
-
latestStore: serializedStore,
|
|
295
|
-
latestHeight: previousHeight.toString(),
|
|
296
|
-
latestHash: previousHash,
|
|
297
|
-
});
|
|
298
|
-
// Sync with peer if necessary
|
|
299
|
-
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
300
|
-
const { latestStore, latestHeight } = await peer.syncStore(previousInfo, previousHeight, Buffer.from(previousHash, "hex"), false);
|
|
301
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
302
|
-
// Serialize the store data for caching
|
|
303
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
304
|
-
// Cache updated store info
|
|
305
|
-
storeCoinCache.set(this.storeId, {
|
|
306
|
-
latestStore: serializedLatestStore,
|
|
307
|
-
latestHeight,
|
|
308
|
-
latestHash: latestHash.toString("hex"),
|
|
309
|
-
});
|
|
310
|
-
return { latestStore, latestHeight, latestHash };
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
// Return cached info if sync fails
|
|
314
|
-
const { latestStore, latestHeight, latestHash } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
315
|
-
latestStore: cachedInfo.latestStore,
|
|
316
|
-
latestHeight: cachedInfo.latestHeight.toString(),
|
|
317
|
-
latestHash: cachedInfo.latestHash,
|
|
318
|
-
});
|
|
319
|
-
return {
|
|
320
|
-
latestStore,
|
|
321
|
-
latestHeight,
|
|
322
|
-
latestHash: latestHash,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
213
|
+
// Register the storeId with a no-op callback. If already registered, the registry will handle it.
|
|
214
|
+
await storeMonitor.registerStore(this.storeId, () => {
|
|
215
|
+
// No operation callback since we're fetching the cache directly
|
|
216
|
+
});
|
|
217
|
+
// Retrieve the latest cached store information
|
|
218
|
+
const cachedInfo = await storeMonitor.getLatestCache(this.storeId);
|
|
219
|
+
if (!cachedInfo) {
|
|
220
|
+
throw new Error(`No cached info found for storeId: ${this.storeId}`);
|
|
325
221
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId(Buffer.from(this.storeId, "hex"), createdAtHeight, createdAtHash, false);
|
|
331
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
332
|
-
// Serialize the latest store info for caching
|
|
333
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
334
|
-
// Cache the latest store info
|
|
335
|
-
storeCoinCache.set(this.storeId, {
|
|
336
|
-
latestStore: serializedLatestStore,
|
|
337
|
-
latestHeight,
|
|
338
|
-
latestHash: latestHash.toString("hex"),
|
|
222
|
+
const deserializedStore = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
223
|
+
latestStore: cachedInfo.latestStore,
|
|
224
|
+
latestHeight: cachedInfo.latestHeight.toString(),
|
|
225
|
+
latestHash: cachedInfo.latestHash
|
|
339
226
|
});
|
|
340
|
-
return {
|
|
227
|
+
return {
|
|
228
|
+
latestStore: deserializedStore.latestStore,
|
|
229
|
+
latestHeight: deserializedStore.latestHeight,
|
|
230
|
+
latestHash: deserializedStore.latestHash,
|
|
231
|
+
};
|
|
341
232
|
}
|
|
342
233
|
catch (error) {
|
|
343
|
-
|
|
344
|
-
throw error;
|
|
234
|
+
throw new Error(`Failed to fetch coin info for storeId ${this.storeId}: ${error.message}`);
|
|
345
235
|
}
|
|
346
236
|
}
|
|
347
237
|
async cacheStoreCreationHeight() {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { DataStoreSerializer } from "./DataStoreSerializer";
|
|
2
|
+
/**
|
|
3
|
+
* Represents the structure of cached store information.
|
|
4
|
+
*/
|
|
5
|
+
interface StoreCacheEntry {
|
|
6
|
+
latestStore: ReturnType<DataStoreSerializer["serialize"]>;
|
|
7
|
+
latestHeight: number;
|
|
8
|
+
latestHash: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Callback type to be invoked when the store is updated.
|
|
12
|
+
*/
|
|
13
|
+
type StoreUpdateCallback = (storeId: string, updatedStore: StoreCacheEntry) => void;
|
|
14
|
+
/**
|
|
15
|
+
* StoreMonitorRegistry manages monitoring of multiple storeIds.
|
|
16
|
+
* It encapsulates cache management and ensures only one monitor runs per storeId.
|
|
17
|
+
*/
|
|
18
|
+
export declare class StoreMonitorRegistry {
|
|
19
|
+
private static instance;
|
|
20
|
+
private activeMonitors;
|
|
21
|
+
private storeCoinCache;
|
|
22
|
+
private cachePopulationPromises;
|
|
23
|
+
/**
|
|
24
|
+
* Private constructor to enforce singleton pattern.
|
|
25
|
+
*/
|
|
26
|
+
private constructor();
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the singleton instance of the registry.
|
|
29
|
+
* @returns {StoreMonitorRegistry} The singleton instance.
|
|
30
|
+
*/
|
|
31
|
+
static getInstance(): StoreMonitorRegistry;
|
|
32
|
+
/**
|
|
33
|
+
* Registers a storeId with a callback to be invoked upon updates.
|
|
34
|
+
* Immediately updates the cache and invokes the callback before starting the monitor.
|
|
35
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
36
|
+
* @param {StoreUpdateCallback} callback - The callback to execute when the store is updated.
|
|
37
|
+
*/
|
|
38
|
+
registerStore(storeId: string, callback: StoreUpdateCallback): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Retrieves the latest cached entry for a given storeId.
|
|
41
|
+
* If no cache exists, it fetches and caches the store info before returning.
|
|
42
|
+
* @param {string} storeId - The store identifier.
|
|
43
|
+
* @returns {Promise<StoreCacheEntry>} The latest cached entry.
|
|
44
|
+
*/
|
|
45
|
+
getLatestCache(storeId: string): Promise<StoreCacheEntry>;
|
|
46
|
+
/**
|
|
47
|
+
* Starts the monitor for a specific storeId.
|
|
48
|
+
* Ensures that the monitor restarts upon any error.
|
|
49
|
+
* Implements an exponential backoff strategy for retries.
|
|
50
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
51
|
+
* @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
|
|
52
|
+
*/
|
|
53
|
+
private startMonitor;
|
|
54
|
+
/**
|
|
55
|
+
* Monitors a single store indefinitely.
|
|
56
|
+
* Executes a single iteration of the monitoring logic.
|
|
57
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
58
|
+
* @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
|
|
59
|
+
*/
|
|
60
|
+
private monitorStore;
|
|
61
|
+
/**
|
|
62
|
+
* Fetches the latest store information and updates the cache.
|
|
63
|
+
* @param {string} storeId - The store identifier.
|
|
64
|
+
* @returns {Promise<StoreCacheEntry>} The latest store information.
|
|
65
|
+
*/
|
|
66
|
+
private fetchAndCacheStoreInfo;
|
|
67
|
+
/**
|
|
68
|
+
* Utility method to introduce a delay.
|
|
69
|
+
* @param {number} ms - The delay duration in milliseconds.
|
|
70
|
+
* @returns {Promise<void>} Resolves after the specified delay.
|
|
71
|
+
*/
|
|
72
|
+
private delay;
|
|
73
|
+
/**
|
|
74
|
+
* Unregisters a storeId, stopping its monitor.
|
|
75
|
+
* @param {string} storeId - The store identifier to stop monitoring.
|
|
76
|
+
*/
|
|
77
|
+
unregisterStore(storeId: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Stops all active monitors and clears the registry.
|
|
80
|
+
* Useful for graceful shutdowns.
|
|
81
|
+
*/
|
|
82
|
+
stopAllMonitors(): void;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=StoreMonitorRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreMonitorRegistry.d.ts","sourceRoot":"","sources":["../../src/blockchain/StoreMonitorRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAK5D;;GAEG;AACH,UAAU,eAAe;IACvB,WAAW,EAAE,UAAU,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,KAAK,mBAAmB,GAAG,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,eAAe,KAC1B,IAAI,CAAC;AAEV;;;GAGG;AACH,qBAAa,oBAAoB;IAE/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuB;IAG9C,OAAO,CAAC,cAAc,CAAmC;IAGzD,OAAO,CAAC,cAAc,CAA6B;IAGnD,OAAO,CAAC,uBAAuB,CAAwC;IAEvE;;OAEG;IACH,OAAO;IASP;;;OAGG;WACW,WAAW,IAAI,oBAAoB;IAOjD;;;;;OAKG;IACU,aAAa,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAiChB;;;;;OAKG;IACU,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAkCtE;;;;;;OAMG;YACW,YAAY;IA4B1B;;;;;OAKG;YACW,YAAY;IAuG1B;;;;OAIG;YACW,sBAAsB;IAkDpC;;;;OAIG;YACW,KAAK;IAInB;;;OAGG;IACI,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAS7C;;;OAGG;IACI,eAAe,IAAI,IAAI;CAI/B"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StoreMonitorRegistry = void 0;
|
|
4
|
+
const FullNodePeer_1 = require("./FullNodePeer");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const DataStoreSerializer_1 = require("./DataStoreSerializer");
|
|
7
|
+
const DataStore_1 = require("./DataStore");
|
|
8
|
+
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
9
|
+
const utils_2 = require("../utils");
|
|
10
|
+
/**
|
|
11
|
+
* StoreMonitorRegistry manages monitoring of multiple storeIds.
|
|
12
|
+
* It encapsulates cache management and ensures only one monitor runs per storeId.
|
|
13
|
+
*/
|
|
14
|
+
class StoreMonitorRegistry {
|
|
15
|
+
/**
|
|
16
|
+
* Private constructor to enforce singleton pattern.
|
|
17
|
+
*/
|
|
18
|
+
constructor() {
|
|
19
|
+
this.activeMonitors = new Map();
|
|
20
|
+
this.storeCoinCache = new utils_1.FileCache("stores", utils_1.USER_DIR_PATH);
|
|
21
|
+
this.cachePopulationPromises = new Map();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Retrieves the singleton instance of the registry.
|
|
25
|
+
* @returns {StoreMonitorRegistry} The singleton instance.
|
|
26
|
+
*/
|
|
27
|
+
static getInstance() {
|
|
28
|
+
if (!StoreMonitorRegistry.instance) {
|
|
29
|
+
StoreMonitorRegistry.instance = new StoreMonitorRegistry();
|
|
30
|
+
}
|
|
31
|
+
return StoreMonitorRegistry.instance;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Registers a storeId with a callback to be invoked upon updates.
|
|
35
|
+
* Immediately updates the cache and invokes the callback before starting the monitor.
|
|
36
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
37
|
+
* @param {StoreUpdateCallback} callback - The callback to execute when the store is updated.
|
|
38
|
+
*/
|
|
39
|
+
async registerStore(storeId, callback) {
|
|
40
|
+
if (this.activeMonitors.has(storeId)) {
|
|
41
|
+
console.log(`Registry: Monitor already running for storeId: ${storeId}`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
console.log(`Registry: Registering monitor for storeId: ${storeId}`);
|
|
45
|
+
this.activeMonitors.set(storeId, callback);
|
|
46
|
+
try {
|
|
47
|
+
// Immediately fetch and cache the latest store info
|
|
48
|
+
const initialStore = await this.fetchAndCacheStoreInfo(storeId);
|
|
49
|
+
// Invoke the callback with the initial store info
|
|
50
|
+
callback(storeId, initialStore);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(`Registry: Failed to perform initial cache update for storeId: ${storeId} - ${error.message}`);
|
|
54
|
+
// Decide whether to unregister the monitor or proceed
|
|
55
|
+
// For this example, we'll proceed to start the monitor
|
|
56
|
+
}
|
|
57
|
+
// No monitoring in cli mode
|
|
58
|
+
if (!utils_2.Environment.CLI_MODE) {
|
|
59
|
+
// Start monitoring asynchronously
|
|
60
|
+
this.startMonitor(storeId, callback).catch((error) => {
|
|
61
|
+
console.error(`Registry: Unexpected error in startMonitor for storeId: ${storeId} - ${error.message}`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Retrieves the latest cached entry for a given storeId.
|
|
67
|
+
* If no cache exists, it fetches and caches the store info before returning.
|
|
68
|
+
* @param {string} storeId - The store identifier.
|
|
69
|
+
* @returns {Promise<StoreCacheEntry>} The latest cached entry.
|
|
70
|
+
*/
|
|
71
|
+
async getLatestCache(storeId) {
|
|
72
|
+
// if in CLI mode, always fetch the latest store info directly
|
|
73
|
+
if (!utils_2.Environment.CLI_MODE) {
|
|
74
|
+
let cachedInfo = this.storeCoinCache.get(storeId);
|
|
75
|
+
if (cachedInfo) {
|
|
76
|
+
return cachedInfo;
|
|
77
|
+
}
|
|
78
|
+
// If cache is missing, fetch and cache it
|
|
79
|
+
console.log(`getLatestCache: No cache found for storeId: ${storeId}. Fetching...`);
|
|
80
|
+
// Prevent duplicate fetches for the same storeId
|
|
81
|
+
if (this.cachePopulationPromises.has(storeId)) {
|
|
82
|
+
return this.cachePopulationPromises.get(storeId);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const fetchPromise = this.fetchAndCacheStoreInfo(storeId)
|
|
86
|
+
.then((store) => {
|
|
87
|
+
this.cachePopulationPromises.delete(storeId);
|
|
88
|
+
return store;
|
|
89
|
+
})
|
|
90
|
+
.catch((error) => {
|
|
91
|
+
this.cachePopulationPromises.delete(storeId);
|
|
92
|
+
throw error;
|
|
93
|
+
});
|
|
94
|
+
this.cachePopulationPromises.set(storeId, fetchPromise);
|
|
95
|
+
return fetchPromise;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Starts the monitor for a specific storeId.
|
|
99
|
+
* Ensures that the monitor restarts upon any error.
|
|
100
|
+
* Implements an exponential backoff strategy for retries.
|
|
101
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
102
|
+
* @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
|
|
103
|
+
*/
|
|
104
|
+
async startMonitor(storeId, callback) {
|
|
105
|
+
let retryCount = 0;
|
|
106
|
+
const maxRetryDelay = 60000; // 60 seconds
|
|
107
|
+
while (this.activeMonitors.has(storeId)) {
|
|
108
|
+
try {
|
|
109
|
+
await this.monitorStore(storeId, callback);
|
|
110
|
+
retryCount = 0; // Reset on success
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error(`Registry: Monitor for storeId: ${storeId} encountered an error: ${error.message}. Restarting monitor...`);
|
|
114
|
+
const delayTime = maxRetryDelay;
|
|
115
|
+
console.log(`Registry: Waiting for ${delayTime / 1000} seconds before retrying...`);
|
|
116
|
+
await this.delay(delayTime);
|
|
117
|
+
retryCount++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log(`Registry: Monitor for storeId: ${storeId} has been stopped.`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Monitors a single store indefinitely.
|
|
124
|
+
* Executes a single iteration of the monitoring logic.
|
|
125
|
+
* @param {string} storeId - The store identifier to monitor.
|
|
126
|
+
* @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
|
|
127
|
+
*/
|
|
128
|
+
async monitorStore(storeId, callback) {
|
|
129
|
+
console.log(`Monitor: Starting monitor iteration for storeId: ${storeId}`);
|
|
130
|
+
try {
|
|
131
|
+
console.log(`Monitor: Connecting to peer for storeId: ${storeId}`);
|
|
132
|
+
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
133
|
+
const cachedInfo = this.storeCoinCache.get(storeId);
|
|
134
|
+
if (cachedInfo) {
|
|
135
|
+
// Log cached store info retrieval
|
|
136
|
+
console.log(`Monitor: Cached store info found for storeId: ${storeId}, syncing...`);
|
|
137
|
+
// Deserialize cached info and wait for the coin to be spent
|
|
138
|
+
const previousStore = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
139
|
+
latestStore: cachedInfo.latestStore,
|
|
140
|
+
latestHeight: cachedInfo.latestHeight.toString(),
|
|
141
|
+
latestHash: cachedInfo.latestHash,
|
|
142
|
+
});
|
|
143
|
+
console.log(`Monitor: Waiting for coin to be spent for storeId: ${storeId}...`);
|
|
144
|
+
const dataStore = DataStore_1.DataStore.from(storeId);
|
|
145
|
+
const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
|
|
146
|
+
await peer.waitForCoinToBeSpent((0, datalayer_driver_1.getCoinId)(previousStore.latestStore.coin), createdAtHeight, createdAtHash);
|
|
147
|
+
// Sync store and get updated details
|
|
148
|
+
console.log(`Monitor: Syncing store for storeId: ${storeId}`);
|
|
149
|
+
const { latestStore, latestHeight } = await peer.syncStore(previousStore.latestStore, createdAtHeight, createdAtHash, false);
|
|
150
|
+
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
151
|
+
console.log(`Monitor: Store synced for storeId: ${storeId}, coin id: ${(0, datalayer_driver_1.getCoinId)(latestStore.coin).toString("hex")}`);
|
|
152
|
+
// Serialize and cache the updated store info
|
|
153
|
+
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
154
|
+
console.log(`Monitor: Caching updated store info for storeId: ${storeId}`);
|
|
155
|
+
this.storeCoinCache.set(storeId, {
|
|
156
|
+
latestStore: serializedLatestStore,
|
|
157
|
+
latestHeight,
|
|
158
|
+
latestHash: latestHash.toString("hex"),
|
|
159
|
+
});
|
|
160
|
+
// Invoke the callback with updated store info
|
|
161
|
+
const updatedStore = this.storeCoinCache.get(storeId);
|
|
162
|
+
if (updatedStore) {
|
|
163
|
+
callback(storeId, updatedStore);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.warn(`Monitor: Updated store info for storeId: ${storeId} is missing after caching.`);
|
|
167
|
+
}
|
|
168
|
+
return; // Successful iteration
|
|
169
|
+
}
|
|
170
|
+
// If no cached info exists, fetch initial store info
|
|
171
|
+
console.log(`Monitor: No cached info found for storeId: ${storeId}. Fetching initial store info.`);
|
|
172
|
+
// Fetch and cache the latest store info
|
|
173
|
+
const newStore = await this.fetchAndCacheStoreInfo(storeId);
|
|
174
|
+
// Invoke the callback with the new store info
|
|
175
|
+
callback(storeId, newStore);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error(`Monitor: Error monitoring storeId: ${storeId} - ${error.message}`);
|
|
179
|
+
// Propagate the error to trigger a restart in startMonitor
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Fetches the latest store information and updates the cache.
|
|
185
|
+
* @param {string} storeId - The store identifier.
|
|
186
|
+
* @returns {Promise<StoreCacheEntry>} The latest store information.
|
|
187
|
+
*/
|
|
188
|
+
async fetchAndCacheStoreInfo(storeId) {
|
|
189
|
+
console.log(`Monitor: Fetching and caching latest store info for storeId: ${storeId}`);
|
|
190
|
+
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
191
|
+
const dataStore = DataStore_1.DataStore.from(storeId);
|
|
192
|
+
const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
|
|
193
|
+
// Sync store from the peer using launcher ID
|
|
194
|
+
console.log(`Monitor: Syncing store from launcher ID for storeId: ${storeId}`);
|
|
195
|
+
const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId(Buffer.from(storeId, "hex"), createdAtHeight, createdAtHash, false);
|
|
196
|
+
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
197
|
+
// Serialize and cache the new store info
|
|
198
|
+
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
199
|
+
console.log(`Monitor: Caching new store info for storeId: ${storeId}`);
|
|
200
|
+
this.storeCoinCache.set(storeId, {
|
|
201
|
+
latestStore: serializedLatestStore,
|
|
202
|
+
latestHeight,
|
|
203
|
+
latestHash: latestHash.toString("hex"),
|
|
204
|
+
});
|
|
205
|
+
// Return the cached store info
|
|
206
|
+
const newStore = this.storeCoinCache.get(storeId);
|
|
207
|
+
if (!newStore) {
|
|
208
|
+
throw new Error(`Failed to cache store info for storeId: ${storeId} after fetching.`);
|
|
209
|
+
}
|
|
210
|
+
return newStore;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Utility method to introduce a delay.
|
|
214
|
+
* @param {number} ms - The delay duration in milliseconds.
|
|
215
|
+
* @returns {Promise<void>} Resolves after the specified delay.
|
|
216
|
+
*/
|
|
217
|
+
async delay(ms) {
|
|
218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Unregisters a storeId, stopping its monitor.
|
|
222
|
+
* @param {string} storeId - The store identifier to stop monitoring.
|
|
223
|
+
*/
|
|
224
|
+
unregisterStore(storeId) {
|
|
225
|
+
if (this.activeMonitors.has(storeId)) {
|
|
226
|
+
this.activeMonitors.delete(storeId);
|
|
227
|
+
console.log(`Registry: Unregistered monitor for storeId: ${storeId}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(`Registry: No active monitor found for storeId: ${storeId}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Stops all active monitors and clears the registry.
|
|
235
|
+
* Useful for graceful shutdowns.
|
|
236
|
+
*/
|
|
237
|
+
stopAllMonitors() {
|
|
238
|
+
this.activeMonitors.clear();
|
|
239
|
+
console.log(`Registry: All monitors have been stopped.`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.StoreMonitorRegistry = StoreMonitorRegistry;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blockchain/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAa,gBAAgB,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blockchain/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAa,gBAAgB,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,wBAAwB,CAAC"}
|
package/dist/blockchain/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export declare class StoreInfoCacheUpdater {
|
|
2
|
-
private static instance;
|
|
3
|
-
private storeCoinCache;
|
|
4
|
-
private monitors;
|
|
5
|
-
private lockFilePath;
|
|
6
|
-
private releaseLock;
|
|
7
|
-
private isMonitoring;
|
|
8
|
-
private lockRenewalInterval;
|
|
9
|
-
private constructor();
|
|
10
|
-
static initInstance(): StoreInfoCacheUpdater;
|
|
11
|
-
private startMonitors;
|
|
12
|
-
private renewLock;
|
|
13
|
-
private monitorStore;
|
|
14
|
-
private isUnrecoverableError;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=StoreInfoCacheUpdater.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StoreInfoCacheUpdater.d.ts","sourceRoot":"","sources":["../../src/blockchain/StoreInfoCacheUpdater.ts"],"names":[],"mappings":"AAcA,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAwB;IAC/C,OAAO,CAAC,cAAc,CAIsB;IAC5C,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,mBAAmB,CAA+B;IAE1D,OAAO;WAiBO,YAAY,IAAI,qBAAqB;YAQrC,aAAa;IA6E3B,OAAO,CAAC,SAAS;YA0BH,YAAY;IAyI1B,OAAO,CAAC,oBAAoB;CAG7B"}
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.StoreInfoCacheUpdater = void 0;
|
|
30
|
-
const fs_1 = __importDefault(require("fs"));
|
|
31
|
-
const FullNodePeer_1 = require("./FullNodePeer");
|
|
32
|
-
const utils_1 = require("../utils");
|
|
33
|
-
const DataStoreSerializer_1 = require("./DataStoreSerializer");
|
|
34
|
-
const utils_2 = require("../utils");
|
|
35
|
-
const lockfile = __importStar(require("proper-lockfile"));
|
|
36
|
-
const path = __importStar(require("path"));
|
|
37
|
-
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
38
|
-
class StoreInfoCacheUpdater {
|
|
39
|
-
constructor() {
|
|
40
|
-
this.storeCoinCache = new utils_1.FileCache(`stores`, utils_1.USER_DIR_PATH);
|
|
41
|
-
this.monitors = new Map();
|
|
42
|
-
this.releaseLock = null;
|
|
43
|
-
this.isMonitoring = true;
|
|
44
|
-
this.lockRenewalInterval = null;
|
|
45
|
-
console.log("Constructor: Initializing StoreInfoCacheUpdater");
|
|
46
|
-
// Construct lock file path using the path module
|
|
47
|
-
this.lockFilePath = path.join(utils_1.USER_DIR_PATH, "store-info-cache.lock");
|
|
48
|
-
console.log("Lock file path:", this.lockFilePath);
|
|
49
|
-
const lockDir = path.dirname(this.lockFilePath);
|
|
50
|
-
if (!fs_1.default.existsSync(lockDir)) {
|
|
51
|
-
console.log(`Creating lock directory: ${lockDir}`);
|
|
52
|
-
fs_1.default.mkdirSync(lockDir, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
// Start monitors for existing storeIds
|
|
55
|
-
this.startMonitors();
|
|
56
|
-
}
|
|
57
|
-
static initInstance() {
|
|
58
|
-
if (!StoreInfoCacheUpdater.instance) {
|
|
59
|
-
console.log("Initializing DataStore Monitor");
|
|
60
|
-
StoreInfoCacheUpdater.instance = new StoreInfoCacheUpdater();
|
|
61
|
-
}
|
|
62
|
-
return StoreInfoCacheUpdater.instance;
|
|
63
|
-
}
|
|
64
|
-
async startMonitors() {
|
|
65
|
-
try {
|
|
66
|
-
console.log("Checking if lockfile exists...");
|
|
67
|
-
// Check if the lock file exists
|
|
68
|
-
if (!fs_1.default.existsSync(this.lockFilePath)) {
|
|
69
|
-
console.log("Lockfile does not exist. Proceeding without lock.");
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// Check if the lockfile is already held
|
|
73
|
-
const isLocked = await lockfile.check(this.lockFilePath, {
|
|
74
|
-
realpath: false,
|
|
75
|
-
});
|
|
76
|
-
if (isLocked) {
|
|
77
|
-
console.log("Another process is already running the StoreInfoCacheUpdater.");
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// Attempt to acquire the lock
|
|
82
|
-
console.log("Attempting to acquire lock...");
|
|
83
|
-
this.releaseLock = await lockfile.lock(this.lockFilePath, {
|
|
84
|
-
retries: {
|
|
85
|
-
retries: 0, // No retries since we only need one lock
|
|
86
|
-
},
|
|
87
|
-
stale: 60000, // Lock expires after 1 minute
|
|
88
|
-
realpath: false, // Ensure lockfile uses the exact path
|
|
89
|
-
});
|
|
90
|
-
console.log("Lock acquired, starting monitors...");
|
|
91
|
-
// Renew the lock every minute by reacquiring it
|
|
92
|
-
this.renewLock();
|
|
93
|
-
const storeIds = this.storeCoinCache.getCachedKeys();
|
|
94
|
-
console.log(`Found ${storeIds.length} store IDs in cache:`, storeIds);
|
|
95
|
-
for (const storeId of storeIds) {
|
|
96
|
-
// Check if a monitor is already running for this storeId
|
|
97
|
-
if (!this.monitors.has(storeId)) {
|
|
98
|
-
console.log(`Starting monitor for storeId: ${storeId}`);
|
|
99
|
-
// Start monitoring in the background
|
|
100
|
-
const monitorPromise = this.monitorStore(storeId);
|
|
101
|
-
this.monitors.set(storeId, monitorPromise);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
console.log(`Monitor already exists for storeId: ${storeId}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
this.isMonitoring = true;
|
|
108
|
-
// Wait for all monitors to settle
|
|
109
|
-
const monitorPromises = Array.from(this.monitors.values());
|
|
110
|
-
console.log("Waiting for all monitor promises to settle...");
|
|
111
|
-
await Promise.all(monitorPromises);
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
console.error("Monitor system encountered an error:", error);
|
|
115
|
-
}
|
|
116
|
-
finally {
|
|
117
|
-
// Release the lock
|
|
118
|
-
if (this.releaseLock) {
|
|
119
|
-
try {
|
|
120
|
-
console.log("Releasing lock...");
|
|
121
|
-
await this.releaseLock();
|
|
122
|
-
console.log("Lock released successfully.");
|
|
123
|
-
}
|
|
124
|
-
catch (releaseError) {
|
|
125
|
-
console.error("Error releasing the lock:", releaseError);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
// Clear the lock renewal interval
|
|
129
|
-
if (this.lockRenewalInterval) {
|
|
130
|
-
clearInterval(this.lockRenewalInterval);
|
|
131
|
-
this.lockRenewalInterval = null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
renewLock() {
|
|
136
|
-
// Set up a renewal process that releases and reacquires the lock every minute
|
|
137
|
-
this.lockRenewalInterval = setInterval(async () => {
|
|
138
|
-
try {
|
|
139
|
-
if (this.releaseLock) {
|
|
140
|
-
console.log("Releasing the lock for renewal...");
|
|
141
|
-
await this.releaseLock();
|
|
142
|
-
console.log("Lock released for renewal.");
|
|
143
|
-
}
|
|
144
|
-
// Reacquire the lock
|
|
145
|
-
this.releaseLock = await lockfile.lock(this.lockFilePath, {
|
|
146
|
-
retries: {
|
|
147
|
-
retries: 0, // No retries since we only need one lock
|
|
148
|
-
},
|
|
149
|
-
stale: 60000, // Lock expires after 1 minute
|
|
150
|
-
realpath: false, // Ensure lockfile uses the exact path
|
|
151
|
-
});
|
|
152
|
-
console.log("Lock reacquired for renewal.");
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
console.error("Error renewing the lock:", error);
|
|
156
|
-
}
|
|
157
|
-
}, 60000); // Renew the lock every 60 seconds
|
|
158
|
-
}
|
|
159
|
-
// Monitor a single store's coin
|
|
160
|
-
async monitorStore(storeId) {
|
|
161
|
-
while (this.isMonitoring) {
|
|
162
|
-
let peer = null;
|
|
163
|
-
try {
|
|
164
|
-
console.log(`Monitoring store ${storeId}`);
|
|
165
|
-
// Connect to a peer
|
|
166
|
-
peer = await (0, utils_2.withTimeout)(FullNodePeer_1.FullNodePeer.connect(), 60000, "Timeout connecting to FullNodePeer");
|
|
167
|
-
// Get the latest store info (from cache if available)
|
|
168
|
-
const cachedInfo = this.storeCoinCache.get(storeId);
|
|
169
|
-
if (!cachedInfo) {
|
|
170
|
-
// If no cached info, skip and wait before retrying
|
|
171
|
-
console.error(`No cached info for storeId ${storeId}`);
|
|
172
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
const { latestStore: serializedStore, latestHeight, latestHash, } = cachedInfo;
|
|
176
|
-
const { latestStore } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
177
|
-
latestStore: serializedStore,
|
|
178
|
-
latestHeight: latestHeight.toString(),
|
|
179
|
-
latestHash: latestHash,
|
|
180
|
-
});
|
|
181
|
-
// Get the coinId associated with the store
|
|
182
|
-
const coinId = (0, datalayer_driver_1.getCoinId)(latestStore.coin);
|
|
183
|
-
console.log(`Waiting for coin to be spent: ${coinId.toString("hex")}`);
|
|
184
|
-
try {
|
|
185
|
-
// Wait for the coin to be spent
|
|
186
|
-
await peer.waitForCoinToBeSpent(coinId, latestHeight, Buffer.from(latestHash, "hex"));
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
const genesisChallenge = await (0, datalayer_driver_1.getMainnetGenesisChallenge)();
|
|
190
|
-
const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, null, genesisChallenge, false), 60000, `Timeout syncing store for storeId ${storeId}`);
|
|
191
|
-
const headerHash = await peer.getHeaderHash(storeInfo.latestHeight);
|
|
192
|
-
await peer.waitForCoinToBeSpent((0, datalayer_driver_1.getCoinId)(storeInfo.latestStore.coin), storeInfo.latestHeight, headerHash);
|
|
193
|
-
}
|
|
194
|
-
console.log(`Detected Coin Spend: ${coinId.toString("hex")}`);
|
|
195
|
-
let updatedStore, newHeight;
|
|
196
|
-
try {
|
|
197
|
-
// When resolved, sync the store
|
|
198
|
-
const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, latestHeight, Buffer.from(latestHash, "hex"), false // withHistory
|
|
199
|
-
), 60000, `Timeout syncing store for storeId ${storeId}`);
|
|
200
|
-
updatedStore = storeInfo.latestStore;
|
|
201
|
-
newHeight = storeInfo.latestHeight;
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
const genesisChallenge = await (0, datalayer_driver_1.getMainnetGenesisChallenge)();
|
|
205
|
-
const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, null, genesisChallenge, false), 60000, `Timeout syncing store for storeId ${storeId}`);
|
|
206
|
-
updatedStore = storeInfo.latestStore;
|
|
207
|
-
newHeight = storeInfo.latestHeight;
|
|
208
|
-
}
|
|
209
|
-
// Get the latest header hash
|
|
210
|
-
const latestHashBuffer = await (0, utils_2.withTimeout)(peer.getHeaderHash(newHeight), 60000, `Timeout getting header hash for height ${newHeight}`);
|
|
211
|
-
// Serialize the updated store data for caching
|
|
212
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(updatedStore, newHeight, latestHashBuffer).serialize();
|
|
213
|
-
// Update the cache
|
|
214
|
-
this.storeCoinCache.set(storeId, {
|
|
215
|
-
latestStore: serializedLatestStore,
|
|
216
|
-
latestHeight: newHeight,
|
|
217
|
-
latestHash: latestHashBuffer.toString("hex"),
|
|
218
|
-
});
|
|
219
|
-
peer = null;
|
|
220
|
-
// Continue monitoring
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
console.error(`Error monitoring store ${storeId}:`, error);
|
|
224
|
-
// Close the peer connection if it's open
|
|
225
|
-
if (peer) {
|
|
226
|
-
peer = null;
|
|
227
|
-
}
|
|
228
|
-
// Determine if the error is unrecoverable
|
|
229
|
-
if (this.isUnrecoverableError(error)) {
|
|
230
|
-
this.isMonitoring = false; // Signal other monitors to stop
|
|
231
|
-
throw error; // Propagate error up to stop monitoring
|
|
232
|
-
}
|
|
233
|
-
// Wait before retrying
|
|
234
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
isUnrecoverableError(error) {
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
exports.StoreInfoCacheUpdater = StoreInfoCacheUpdater;
|