@dignetwork/dig-sdk 0.0.1-alpha.125 → 0.0.1-alpha.127

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.
@@ -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;
@@ -5,4 +5,5 @@ export * from './FullNodePeer';
5
5
  export * from './DataStoreSerializer';
6
6
  export * from './ServerCoin';
7
7
  export * from "./Wallet";
8
+ export * from './StoreMonitorRegistry';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -34,3 +34,4 @@ __exportStar(require("./FullNodePeer"), exports);
34
34
  __exportStar(require("./DataStoreSerializer"), exports);
35
35
  __exportStar(require("./ServerCoin"), exports);
36
36
  __exportStar(require("./Wallet"), exports);
37
+ __exportStar(require("./StoreMonitorRegistry"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/dig-sdk",
3
- "version": "0.0.1-alpha.125",
3
+ "version": "0.0.1-alpha.127",
4
4
  "description": "",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
@@ -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;