@dignetwork/dig-sdk 0.0.1-alpha.17 → 0.0.1-alpha.170

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.
Files changed (84) hide show
  1. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +10 -1
  2. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
  3. package/dist/DataIntegrityTree/DataIntegrityTree.js +77 -10
  4. package/dist/DigNetwork/ContentServer.d.ts +24 -5
  5. package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
  6. package/dist/DigNetwork/ContentServer.js +239 -27
  7. package/dist/DigNetwork/DigNetwork.d.ts +15 -9
  8. package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
  9. package/dist/DigNetwork/DigNetwork.js +129 -189
  10. package/dist/DigNetwork/DigPeer.d.ts +8 -3
  11. package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
  12. package/dist/DigNetwork/DigPeer.js +72 -115
  13. package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -1
  14. package/dist/DigNetwork/IncentiveServer.js +6 -5
  15. package/dist/DigNetwork/PropagationServer.d.ts +88 -33
  16. package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
  17. package/dist/DigNetwork/PropagationServer.js +636 -357
  18. package/dist/blockchain/DataStore.d.ts +25 -7
  19. package/dist/blockchain/DataStore.d.ts.map +1 -1
  20. package/dist/blockchain/DataStore.js +115 -159
  21. package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
  22. package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
  23. package/dist/blockchain/FullNodePeer.d.ts +95 -10
  24. package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
  25. package/dist/blockchain/FullNodePeer.js +324 -115
  26. package/dist/blockchain/ServerCoin.d.ts +13 -5
  27. package/dist/blockchain/ServerCoin.d.ts.map +1 -1
  28. package/dist/blockchain/ServerCoin.js +90 -28
  29. package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
  30. package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
  31. package/dist/blockchain/StoreMonitorRegistry.js +238 -0
  32. package/dist/blockchain/Wallet.d.ts +2 -2
  33. package/dist/blockchain/Wallet.d.ts.map +1 -1
  34. package/dist/blockchain/Wallet.js +50 -18
  35. package/dist/blockchain/coins.d.ts +1 -2
  36. package/dist/blockchain/coins.d.ts.map +1 -1
  37. package/dist/blockchain/coins.js +1 -55
  38. package/dist/blockchain/index.d.ts +1 -0
  39. package/dist/blockchain/index.d.ts.map +1 -1
  40. package/dist/blockchain/index.js +1 -0
  41. package/dist/types.d.ts +2 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/utils/ContentScanner.d.ts +63 -0
  44. package/dist/utils/ContentScanner.d.ts.map +1 -0
  45. package/dist/utils/ContentScanner.js +175 -0
  46. package/dist/utils/Environment.d.ts +21 -0
  47. package/dist/utils/Environment.d.ts.map +1 -0
  48. package/dist/utils/Environment.js +93 -0
  49. package/dist/utils/FileCache.d.ts +1 -1
  50. package/dist/utils/FileCache.d.ts.map +1 -1
  51. package/dist/utils/FileCache.js +2 -2
  52. package/dist/utils/FileTransfer.d.ts +47 -0
  53. package/dist/utils/FileTransfer.d.ts.map +1 -0
  54. package/dist/utils/FileTransfer.js +209 -0
  55. package/dist/utils/NconfManager.js +2 -1
  56. package/dist/utils/PeerRanker.d.ts +55 -0
  57. package/dist/utils/PeerRanker.d.ts.map +1 -0
  58. package/dist/utils/PeerRanker.js +186 -0
  59. package/dist/utils/StoreArchiveManager.d.ts +45 -0
  60. package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
  61. package/dist/utils/StoreArchiveManager.js +153 -0
  62. package/dist/utils/config.d.ts +4 -3
  63. package/dist/utils/config.d.ts.map +1 -1
  64. package/dist/utils/config.js +13 -8
  65. package/dist/utils/directoryUtils.d.ts +16 -0
  66. package/dist/utils/directoryUtils.d.ts.map +1 -1
  67. package/dist/utils/directoryUtils.js +66 -0
  68. package/dist/utils/index.d.ts +3 -1
  69. package/dist/utils/index.d.ts.map +1 -1
  70. package/dist/utils/index.js +3 -1
  71. package/dist/utils/merkle.d.ts +2 -0
  72. package/dist/utils/merkle.d.ts.map +1 -0
  73. package/dist/utils/merkle.js +28 -0
  74. package/dist/utils/network.d.ts +2 -1
  75. package/dist/utils/network.d.ts.map +1 -1
  76. package/dist/utils/network.js +40 -15
  77. package/dist/utils/promiseUtils.d.ts +19 -0
  78. package/dist/utils/promiseUtils.d.ts.map +1 -0
  79. package/dist/utils/promiseUtils.js +43 -0
  80. package/dist/utils/ssl.js +2 -2
  81. package/package.json +18 -5
  82. package/dist/utils/deltaUtils.d.ts +0 -2
  83. package/dist/utils/deltaUtils.d.ts.map +0 -1
  84. package/dist/utils/deltaUtils.js +0 -83
@@ -5,27 +5,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ServerCoin = void 0;
7
7
  const lodash_1 = __importDefault(require("lodash"));
8
- const datalayer_driver_1 = require("datalayer-driver");
8
+ const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
9
9
  const FullNodePeer_1 = require("./FullNodePeer");
10
- const coins_1 = require("./coins");
11
10
  const Wallet_1 = require("./Wallet");
12
11
  const NconfManager_1 = require("../utils/NconfManager");
13
12
  const DataStore_1 = require("./DataStore");
13
+ const node_cache_1 = __importDefault(require("node-cache"));
14
+ const network_1 = require("../utils/network");
15
+ const Environment_1 = require("../utils/Environment");
14
16
  const serverCoinCollateral = 300000000;
17
+ // Initialize the cache with a TTL of 300 seconds (5 minutes)
18
+ const serverCoinPeersCache = new node_cache_1.default({ stdTTL: 300 });
15
19
  class ServerCoin {
16
20
  constructor(storeId) {
17
21
  this.storeId = storeId;
18
22
  }
19
23
  // Create a new server coin for the current epoch
20
- async createForEpoch(peerIp) {
24
+ async createForEpoch(peerIp, rootHash) {
21
25
  try {
22
26
  const peer = await FullNodePeer_1.FullNodePeer.connect();
23
27
  const wallet = await Wallet_1.Wallet.load("default");
24
28
  const publicSyntheticKey = await wallet.getPublicSyntheticKey();
25
- const serverCoinCreationCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(serverCoinCollateral), BigInt(1000000));
26
- const currentEpoch = ServerCoin.getCurrentEpoch();
29
+ const serverCoinCreationCoins = await wallet.selectUnspentCoins(peer, BigInt(serverCoinCollateral), BigInt(1000000));
30
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
27
31
  const epochBasedHint = (0, datalayer_driver_1.morphLauncherId)(Buffer.from(this.storeId, "hex"), BigInt(currentEpoch));
28
- const newServerCoin = (0, datalayer_driver_1.createServerCoin)(publicSyntheticKey, serverCoinCreationCoins, epochBasedHint, [peerIp], BigInt(serverCoinCollateral), BigInt(1000000));
32
+ const newServerCoin = (0, datalayer_driver_1.createServerCoin)(publicSyntheticKey, serverCoinCreationCoins, epochBasedHint, [peerIp, publicSyntheticKey.toString("hex")], BigInt(serverCoinCollateral), BigInt(1000000));
29
33
  const combinedCoinSpends = [...newServerCoin.coinSpends];
30
34
  const sig = (0, datalayer_driver_1.signCoinSpends)(combinedCoinSpends, [await wallet.getPrivateSyntheticKey()], false);
31
35
  const err = await peer.broadcastSpend(combinedCoinSpends, [sig]);
@@ -33,12 +37,12 @@ class ServerCoin {
33
37
  if (err.includes("no spendable coins")) {
34
38
  console.log("No coins available. Will try again in 5 seconds...");
35
39
  await new Promise((resolve) => setTimeout(resolve, 5000));
36
- return this.createForEpoch(peerIp);
40
+ return this.createForEpoch(peerIp, rootHash);
37
41
  }
38
42
  throw new Error(err);
39
43
  }
40
44
  // Cache the new server coin in the NconfManager
41
- await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp);
45
+ await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp, rootHash);
42
46
  return newServerCoin.serverCoin;
43
47
  }
44
48
  catch (error) {
@@ -46,7 +50,7 @@ class ServerCoin {
46
50
  }
47
51
  }
48
52
  // Save the server coin data to NconfManager
49
- async saveServerCoinData(serverCoin, epoch, peerIp) {
53
+ async saveServerCoinData(serverCoin, epoch, peerIp, rootHash) {
50
54
  const newServerCoinData = {
51
55
  coin: {
52
56
  amount: serverCoin.coin.amount.toString(),
@@ -54,7 +58,8 @@ class ServerCoin {
54
58
  parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"),
55
59
  },
56
60
  createdAt: new Date().toISOString(),
57
- epoch: epoch,
61
+ epoch,
62
+ rootHash,
58
63
  };
59
64
  const serverCoins = await this.getServerCoinsForStore(peerIp);
60
65
  serverCoins.push(newServerCoinData);
@@ -70,7 +75,7 @@ class ServerCoin {
70
75
  if (!serverCoin) {
71
76
  throw new Error(`No server coin found for epoch ${epoch} in store ${this.storeId}.`);
72
77
  }
73
- const feeCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(0), BigInt(1000000));
78
+ const feeCoins = await wallet.selectUnspentCoins(peer, BigInt(0), BigInt(1000000));
74
79
  const coin = {
75
80
  amount: BigInt(serverCoin.coin.amount),
76
81
  puzzleHash: Buffer.from(serverCoin.coin.puzzleHash, "hex"),
@@ -94,13 +99,13 @@ class ServerCoin {
94
99
  serverCoins = serverCoins.filter((coin) => coin.coin.parentCoinInfo !== serverCoin.parentCoinInfo);
95
100
  await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
96
101
  }
97
- // Sample server coins for the current epoch
98
- async sampleCurrentEpoch(sampleSize = 5, blacklist = []) {
99
- const epoch = ServerCoin.getCurrentEpoch();
100
- return this.sampleServerCoinsByEpoch(epoch, sampleSize, blacklist);
101
- }
102
- // Sample server coins by epoch
103
- async sampleServerCoinsByEpoch(epoch, sampleSize = 5, blacklist = []) {
102
+ async getAllEpochPeers(epoch, blacklist = []) {
103
+ const cacheKey = `serverCoinPeers-${this.storeId}-${epoch}`;
104
+ // Check if the result is already cached
105
+ const cachedPeers = serverCoinPeersCache.get(cacheKey);
106
+ if (cachedPeers) {
107
+ return cachedPeers;
108
+ }
104
109
  const epochBasedHint = (0, datalayer_driver_1.morphLauncherId)(Buffer.from(this.storeId, "hex"), BigInt(epoch));
105
110
  const peer = await FullNodePeer_1.FullNodePeer.connect();
106
111
  const maxClvmCost = BigInt(11000000000);
@@ -111,33 +116,83 @@ class ServerCoin {
111
116
  for (const coinState of filteredCoinStates) {
112
117
  const serverCoin = await peer.fetchServerCoin(coinState, maxClvmCost);
113
118
  const peerUrl = serverCoin.memoUrls[0];
119
+ // The second memo URL is the public key
120
+ // We will utilize this for future features
121
+ const publicKey = serverCoin.memoUrls[1];
114
122
  if (!blacklist.includes(peerUrl)) {
115
123
  serverCoinPeers.add(peerUrl);
116
124
  }
117
125
  }
118
- if (process.env.DIG_DEBUG === "1") {
126
+ if (Environment_1.Environment.DEBUG) {
127
+ console.log("Server Coin Peers: ", serverCoinPeers);
128
+ }
129
+ const peerList = Array.from(serverCoinPeers);
130
+ // Cache the result
131
+ serverCoinPeersCache.set(cacheKey, peerList);
132
+ return peerList;
133
+ }
134
+ async getActiveEpochPeers(blacklist = []) {
135
+ const { epoch } = ServerCoin.getCurrentEpoch();
136
+ return this.getAllEpochPeers(epoch, blacklist);
137
+ }
138
+ // Sample server coins for the current epoch
139
+ async sampleCurrentEpoch(sampleSize = 5, blacklist = []) {
140
+ const { epoch } = ServerCoin.getCurrentEpoch();
141
+ return this.sampleServerCoinsByEpoch(epoch, sampleSize, blacklist);
142
+ }
143
+ // Sample server coins by epoch
144
+ async sampleServerCoinsByEpoch(epoch, sampleSize = 5, blacklist = []) {
145
+ // We dont want our own IP to be included
146
+ const host = await (0, network_1.getPublicHost)();
147
+ if (host) {
148
+ blacklist.push(host);
149
+ }
150
+ const serverCoinPeers = await this.getAllEpochPeers(epoch, blacklist);
151
+ if (Environment_1.Environment.DEBUG) {
119
152
  console.log("Server Coin Peers: ", serverCoinPeers);
120
153
  }
121
- // Convert the Set back to an array if needed
122
- return lodash_1.default.sampleSize(Array.from(serverCoinPeers), sampleSize);
154
+ return lodash_1.default.sampleSize(serverCoinPeers, sampleSize);
123
155
  }
124
156
  // Get the current epoch based on the current timestamp
125
157
  static getCurrentEpoch() {
126
- return ServerCoin.calculateEpoch(new Date());
158
+ return ServerCoin.calculateEpochAndRound(new Date());
127
159
  }
128
160
  // Ensure server coin exists for the current epoch
129
161
  async ensureServerCoinExists(peerIp) {
130
162
  try {
131
163
  console.log(`Ensuring server coin exists for store ${this.storeId}...`);
132
- const currentEpoch = ServerCoin.getCurrentEpoch();
164
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
133
165
  const serverCoins = await this.getServerCoinsForStore(peerIp);
134
166
  // Check if a server coin already exists for the current epoch
135
167
  const existingCoin = serverCoins.find((coin) => coin.epoch === currentEpoch);
168
+ const dataStore = DataStore_1.DataStore.from(this.storeId);
169
+ const rootHistory = await dataStore.getRootHistory(true);
170
+ const lastRoot = lodash_1.default.last(rootHistory);
171
+ if (!lastRoot) {
172
+ throw new Error('Cant get the last root on chain, something is wrong.');
173
+ }
136
174
  if (existingCoin) {
175
+ // nothing to do
176
+ if (lastRoot?.synced && lastRoot?.root_hash === existingCoin.rootHash) {
177
+ return;
178
+ }
179
+ // everything is fine, lets just update the roothash for tracking
180
+ if (lastRoot?.synced && lastRoot?.root_hash !== existingCoin.rootHash) {
181
+ existingCoin.rootHash = lastRoot.root_hash;
182
+ // Update the conf with the modified server coin
183
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
184
+ return;
185
+ }
186
+ // If not synced, melt the coin, a new one will be created when synced up
187
+ // this helps prevent penalties for not having a valid peer registered
188
+ await this.melt(currentEpoch, peerIp);
189
+ }
190
+ // Don't create a server coin until you're synced up
191
+ if (!lodash_1.default.last(rootHistory)?.synced) {
137
192
  return;
138
193
  }
139
194
  console.log(`No server coin found for epoch ${currentEpoch}. Creating new server coin...`);
140
- const serverCoin = await this.createForEpoch(peerIp);
195
+ const serverCoin = await this.createForEpoch(peerIp, lastRoot?.root_hash);
141
196
  const newServerCoinData = {
142
197
  coin: {
143
198
  amount: serverCoin.coin.amount.toString(),
@@ -146,6 +201,7 @@ class ServerCoin {
146
201
  },
147
202
  createdAt: new Date().toISOString(),
148
203
  epoch: currentEpoch,
204
+ rootHash: lodash_1.default.last(rootHistory)?.root_hash || "",
149
205
  };
150
206
  await FullNodePeer_1.FullNodePeer.waitForConfirmation(serverCoin.coin.parentCoinInfo);
151
207
  serverCoins.push(newServerCoinData);
@@ -160,7 +216,7 @@ class ServerCoin {
160
216
  // Melt outdated server coins
161
217
  async meltOutdatedEpochs(peerIp) {
162
218
  try {
163
- const currentEpoch = ServerCoin.getCurrentEpoch();
219
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
164
220
  let serverCoins = await this.getServerCoinsForStore(peerIp);
165
221
  // Filter out coins that are not in the current epoch
166
222
  const outdatedCoins = serverCoins.filter((coin) => coin.epoch < currentEpoch);
@@ -244,17 +300,23 @@ class ServerCoin {
244
300
  }
245
301
  }
246
302
  // Static method to calculate the current epoch
247
- static calculateEpoch(currentTimestampUTC) {
303
+ static calculateEpochAndRound(currentTimestampUTC) {
248
304
  const firstEpochStart = new Date(Date.UTC(2024, 8, 3, 0, 0)); // Sept 3, 2024, 00:00 UTC
249
305
  // Convert the current timestamp to milliseconds
250
306
  const currentTimestampMillis = currentTimestampUTC.getTime();
251
307
  // Calculate the number of milliseconds in one epoch (7 days)
252
- const millisecondsInEpoch = 7 * 24 * 60 * 60 * 1000;
308
+ const millisecondsInEpoch = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
253
309
  // Calculate the difference in milliseconds between the current timestamp and the first epoch start
254
310
  const differenceMillis = currentTimestampMillis - firstEpochStart.getTime();
255
311
  // Calculate the current epoch number
256
312
  const epochNumber = Math.floor(differenceMillis / millisecondsInEpoch) + 1;
257
- return epochNumber;
313
+ // Calculate the milliseconds elapsed since the start of the current epoch
314
+ const elapsedMillisInCurrentEpoch = differenceMillis % millisecondsInEpoch;
315
+ // Calculate the number of milliseconds in a round (10 minutes)
316
+ const millisecondsInRound = 10 * 60 * 1000; // 10 minutes in milliseconds
317
+ // Calculate the current round number
318
+ const roundNumber = Math.floor(elapsedMillisInCurrentEpoch / millisecondsInRound) + 1;
319
+ return { epoch: epochNumber, round: roundNumber };
258
320
  }
259
321
  }
260
322
  exports.ServerCoin = ServerCoin;
@@ -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;IAgChB;;;;;OAKG;IACU,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAkCtE;;;;;;OAMG;YACW,YAAY;IA4B1B;;;;;OAKG;YACW,YAAY;IAuG1B;;;;OAIG;YACW,sBAAsB;IA2CpC;;;;OAIG;YACW,KAAK;IAInB;;;OAGG;IACI,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAS7C;;;OAGG;IACI,eAAe,IAAI,IAAI;CAI/B"}
@@ -0,0 +1,238 @@
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
+ return;
42
+ }
43
+ console.log(`Registry: Registering monitor for storeId: ${storeId}`);
44
+ this.activeMonitors.set(storeId, callback);
45
+ try {
46
+ // Immediately fetch and cache the latest store info
47
+ const initialStore = await this.fetchAndCacheStoreInfo(storeId);
48
+ // Invoke the callback with the initial store info
49
+ callback(storeId, initialStore);
50
+ }
51
+ catch (error) {
52
+ console.error(`Registry: Failed to perform initial cache update for storeId: ${storeId} - ${error.message}`);
53
+ // Decide whether to unregister the monitor or proceed
54
+ // For this example, we'll proceed to start the monitor
55
+ }
56
+ // No monitoring in cli mode
57
+ if (!utils_2.Environment.CLI_MODE) {
58
+ // Start monitoring asynchronously
59
+ this.startMonitor(storeId, callback).catch((error) => {
60
+ console.error(`Registry: Unexpected error in startMonitor for storeId: ${storeId} - ${error.message}`);
61
+ });
62
+ }
63
+ }
64
+ /**
65
+ * Retrieves the latest cached entry for a given storeId.
66
+ * If no cache exists, it fetches and caches the store info before returning.
67
+ * @param {string} storeId - The store identifier.
68
+ * @returns {Promise<StoreCacheEntry>} The latest cached entry.
69
+ */
70
+ async getLatestCache(storeId) {
71
+ // if in CLI mode, always fetch the latest store info directly
72
+ if (!utils_2.Environment.CLI_MODE) {
73
+ let cachedInfo = this.storeCoinCache.get(storeId);
74
+ if (cachedInfo) {
75
+ return cachedInfo;
76
+ }
77
+ // If cache is missing, fetch and cache it
78
+ console.log(`getLatestCache: No cache found for storeId: ${storeId}. Fetching...`);
79
+ // Prevent duplicate fetches for the same storeId
80
+ if (this.cachePopulationPromises.has(storeId)) {
81
+ return this.cachePopulationPromises.get(storeId);
82
+ }
83
+ }
84
+ const fetchPromise = this.fetchAndCacheStoreInfo(storeId)
85
+ .then((store) => {
86
+ this.cachePopulationPromises.delete(storeId);
87
+ return store;
88
+ })
89
+ .catch((error) => {
90
+ this.cachePopulationPromises.delete(storeId);
91
+ throw error;
92
+ });
93
+ this.cachePopulationPromises.set(storeId, fetchPromise);
94
+ return fetchPromise;
95
+ }
96
+ /**
97
+ * Starts the monitor for a specific storeId.
98
+ * Ensures that the monitor restarts upon any error.
99
+ * Implements an exponential backoff strategy for retries.
100
+ * @param {string} storeId - The store identifier to monitor.
101
+ * @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
102
+ */
103
+ async startMonitor(storeId, callback) {
104
+ let retryCount = 0;
105
+ const maxRetryDelay = 1000; // 60 seconds
106
+ while (this.activeMonitors.has(storeId)) {
107
+ try {
108
+ await this.monitorStore(storeId, callback);
109
+ retryCount = 0; // Reset on success
110
+ }
111
+ catch (error) {
112
+ console.error(`Registry: Monitor for storeId: ${storeId} encountered an error: ${error.message}. Restarting monitor...`);
113
+ const delayTime = maxRetryDelay;
114
+ console.log(`Registry: Waiting for ${delayTime / 1000} seconds before retrying...`);
115
+ await this.delay(delayTime);
116
+ retryCount++;
117
+ }
118
+ }
119
+ console.log(`Registry: Monitor for storeId: ${storeId} has been stopped.`);
120
+ }
121
+ /**
122
+ * Monitors a single store indefinitely.
123
+ * Executes a single iteration of the monitoring logic.
124
+ * @param {string} storeId - The store identifier to monitor.
125
+ * @param {StoreUpdateCallback} callback - The callback to invoke upon updates.
126
+ */
127
+ async monitorStore(storeId, callback) {
128
+ console.log(`Monitor: Starting monitor iteration for storeId: ${storeId}`);
129
+ try {
130
+ console.log(`Monitor: Connecting to peer for storeId: ${storeId}`);
131
+ const peer = await FullNodePeer_1.FullNodePeer.connect();
132
+ const cachedInfo = this.storeCoinCache.get(storeId);
133
+ if (cachedInfo) {
134
+ // Log cached store info retrieval
135
+ console.log(`Monitor: Cached store info found for storeId: ${storeId}, syncing...`);
136
+ // Deserialize cached info and wait for the coin to be spent
137
+ const previousStore = DataStoreSerializer_1.DataStoreSerializer.deserialize({
138
+ latestStore: cachedInfo.latestStore,
139
+ latestHeight: cachedInfo.latestHeight.toString(),
140
+ latestHash: cachedInfo.latestHash,
141
+ });
142
+ console.log(`Monitor: Waiting for coin to be spent for storeId: ${storeId}...`);
143
+ const dataStore = DataStore_1.DataStore.from(storeId);
144
+ const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
145
+ await peer.waitForCoinToBeSpent((0, datalayer_driver_1.getCoinId)(previousStore.latestStore.coin), createdAtHeight, createdAtHash);
146
+ // Sync store and get updated details
147
+ console.log(`Monitor: Syncing store for storeId: ${storeId}`);
148
+ const { latestStore, latestHeight } = await peer.syncStore(previousStore.latestStore, createdAtHeight, createdAtHash, false);
149
+ const latestHash = await peer.getHeaderHash(latestHeight);
150
+ console.log(`Monitor: Store synced for storeId: ${storeId}, coin id: ${(0, datalayer_driver_1.getCoinId)(latestStore.coin).toString("hex")}`);
151
+ // Serialize and cache the updated store info
152
+ const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
153
+ console.log(`Monitor: Caching updated store info for storeId: ${storeId}`);
154
+ this.storeCoinCache.set(storeId, {
155
+ latestStore: serializedLatestStore,
156
+ latestHeight,
157
+ latestHash: latestHash.toString("hex"),
158
+ });
159
+ // Invoke the callback with updated store info
160
+ const updatedStore = this.storeCoinCache.get(storeId);
161
+ if (updatedStore) {
162
+ callback(storeId, updatedStore);
163
+ }
164
+ else {
165
+ console.warn(`Monitor: Updated store info for storeId: ${storeId} is missing after caching.`);
166
+ }
167
+ return; // Successful iteration
168
+ }
169
+ // If no cached info exists, fetch initial store info
170
+ console.log(`Monitor: No cached info found for storeId: ${storeId}. Fetching initial store info.`);
171
+ // Fetch and cache the latest store info
172
+ const newStore = await this.fetchAndCacheStoreInfo(storeId);
173
+ // Invoke the callback with the new store info
174
+ callback(storeId, newStore);
175
+ }
176
+ catch (error) {
177
+ console.error(`Monitor: Error monitoring storeId: ${storeId} - ${error.message}`);
178
+ // Propagate the error to trigger a restart in startMonitor
179
+ throw error;
180
+ }
181
+ }
182
+ /**
183
+ * Fetches the latest store information and updates the cache.
184
+ * @param {string} storeId - The store identifier.
185
+ * @returns {Promise<StoreCacheEntry>} The latest store information.
186
+ */
187
+ async fetchAndCacheStoreInfo(storeId) {
188
+ const peer = await FullNodePeer_1.FullNodePeer.connect();
189
+ const dataStore = DataStore_1.DataStore.from(storeId);
190
+ const { createdAtHeight, createdAtHash } = await dataStore.getCreationHeight();
191
+ // Sync store from the peer using launcher ID
192
+ const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId(Buffer.from(storeId, "hex"), createdAtHeight, createdAtHash, false);
193
+ const latestHash = await peer.getHeaderHash(latestHeight);
194
+ // Serialize and cache the new store info
195
+ const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
196
+ this.storeCoinCache.set(storeId, {
197
+ latestStore: serializedLatestStore,
198
+ latestHeight,
199
+ latestHash: latestHash.toString("hex"),
200
+ });
201
+ // Return the cached store info
202
+ const newStore = this.storeCoinCache.get(storeId);
203
+ if (!newStore) {
204
+ throw new Error(`Failed to cache store info for storeId: ${storeId} after fetching.`);
205
+ }
206
+ return newStore;
207
+ }
208
+ /**
209
+ * Utility method to introduce a delay.
210
+ * @param {number} ms - The delay duration in milliseconds.
211
+ * @returns {Promise<void>} Resolves after the specified delay.
212
+ */
213
+ async delay(ms) {
214
+ return new Promise((resolve) => setTimeout(resolve, ms));
215
+ }
216
+ /**
217
+ * Unregisters a storeId, stopping its monitor.
218
+ * @param {string} storeId - The store identifier to stop monitoring.
219
+ */
220
+ unregisterStore(storeId) {
221
+ if (this.activeMonitors.has(storeId)) {
222
+ this.activeMonitors.delete(storeId);
223
+ console.log(`Registry: Unregistered monitor for storeId: ${storeId}`);
224
+ }
225
+ else {
226
+ console.log(`Registry: No active monitor found for storeId: ${storeId}`);
227
+ }
228
+ }
229
+ /**
230
+ * Stops all active monitors and clears the registry.
231
+ * Useful for graceful shutdowns.
232
+ */
233
+ stopAllMonitors() {
234
+ this.activeMonitors.clear();
235
+ console.log(`Registry: All monitors have been stopped.`);
236
+ }
237
+ }
238
+ exports.StoreMonitorRegistry = StoreMonitorRegistry;
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from "buffer";
2
- import { Peer, Coin, CoinSpend } from "datalayer-driver";
2
+ import { Peer, Coin, CoinSpend } from "@dignetwork/datalayer-driver";
3
3
  export declare const DEFAULT_FEE_COIN_COST = 64000000;
4
4
  export declare class Wallet {
5
5
  private mnemonic;
@@ -20,7 +20,7 @@ export declare class Wallet {
20
20
  private static getWalletFromKeyring;
21
21
  private static saveWalletToKeyring;
22
22
  createKeyOwnershipSignature(nonce: string): Promise<string>;
23
- verifyKeyOwnershipSignature(nonce: string, signature: string, publicKey: string): Promise<boolean>;
23
+ static verifyKeyOwnershipSignature(nonce: string, signature: string, publicKey: string): Promise<boolean>;
24
24
  selectUnspentCoins(peer: Peer, coinAmount: bigint, feeBigInt: bigint, omitCoins?: Coin[]): Promise<Coin[]>;
25
25
  static calculateFeeForCoinSpends(peer: Peer, coinSpends: CoinSpend[] | null): Promise<bigint>;
26
26
  static isCoinSpendable(peer: Peer, coinId: Buffer): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"Wallet.d.ts","sourceRoot":"","sources":["../../src/blockchain/Wallet.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EASL,IAAI,EACJ,IAAI,EAEJ,SAAS,EAEV,MAAM,kBAAkB,CAAC;AAM1B,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAOhD,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO;WAKa,IAAI,CACtB,UAAU,GAAE,MAAkB,EAC9B,iBAAiB,GAAE,OAAc,GAChC,OAAO,CAAC,MAAM,CAAC;IAsBX,WAAW,IAAI,MAAM;WAOR,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAMpD,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAShE,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6BhE,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMxC,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKzC,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMrC,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;WAK7B,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;WASlD,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;mBAU/B,oBAAoB;mBAWpB,mBAAmB;IAM3B,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO3D,2BAA2B,CACtC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC;IASN,kBAAkB,CAC7B,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,IAAI,EAAO,GACrB,OAAO,CAAC,IAAI,EAAE,CAAC;WAwCE,yBAAyB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;WAItF,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAOlF"}
1
+ {"version":3,"file":"Wallet.d.ts","sourceRoot":"","sources":["../../src/blockchain/Wallet.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EASL,IAAI,EACJ,IAAI,EAEJ,SAAS,EAEV,MAAM,8BAA8B,CAAC;AAQtC,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAOhD,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO;WAKa,IAAI,CACtB,UAAU,GAAE,MAAkB,EAC9B,iBAAiB,GAAE,OAAc,GAChC,OAAO,CAAC,MAAM,CAAC;IAsBX,WAAW,IAAI,MAAM;WAOR,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAMpD,YAAY,CAC9B,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;WASE,oBAAoB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA6BL,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKrC,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMxC,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKzC,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMrC,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;WAK7B,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;WASlD,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;mBAU/B,oBAAoB;mBAcpB,mBAAmB;IAS3B,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;WAUpD,2BAA2B,CAC7C,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC;IASN,kBAAkB,CAC7B,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,IAAI,EAAO,GACrB,OAAO,CAAC,IAAI,EAAE,CAAC;WAiFE,yBAAyB,CAC3C,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,GAC7B,OAAO,CAAC,MAAM,CAAC;WAIE,eAAe,CACjC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;CAWpB"}