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

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 (87) 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 +237 -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 +635 -358
  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 +114 -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 +325 -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 +88 -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/DigCache.d.ts +38 -0
  47. package/dist/utils/DigCache.d.ts.map +1 -0
  48. package/dist/utils/DigCache.js +231 -0
  49. package/dist/utils/Environment.d.ts +21 -0
  50. package/dist/utils/Environment.d.ts.map +1 -0
  51. package/dist/utils/Environment.js +93 -0
  52. package/dist/utils/FileCache.d.ts +1 -1
  53. package/dist/utils/FileCache.d.ts.map +1 -1
  54. package/dist/utils/FileCache.js +2 -2
  55. package/dist/utils/FileTransfer.d.ts +47 -0
  56. package/dist/utils/FileTransfer.d.ts.map +1 -0
  57. package/dist/utils/FileTransfer.js +209 -0
  58. package/dist/utils/NconfManager.js +2 -1
  59. package/dist/utils/PeerRanker.d.ts +55 -0
  60. package/dist/utils/PeerRanker.d.ts.map +1 -0
  61. package/dist/utils/PeerRanker.js +186 -0
  62. package/dist/utils/StoreArchiveManager.d.ts +45 -0
  63. package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
  64. package/dist/utils/StoreArchiveManager.js +153 -0
  65. package/dist/utils/config.d.ts +4 -3
  66. package/dist/utils/config.d.ts.map +1 -1
  67. package/dist/utils/config.js +13 -8
  68. package/dist/utils/directoryUtils.d.ts +16 -0
  69. package/dist/utils/directoryUtils.d.ts.map +1 -1
  70. package/dist/utils/directoryUtils.js +66 -0
  71. package/dist/utils/index.d.ts +4 -1
  72. package/dist/utils/index.d.ts.map +1 -1
  73. package/dist/utils/index.js +4 -1
  74. package/dist/utils/merkle.d.ts +2 -0
  75. package/dist/utils/merkle.d.ts.map +1 -0
  76. package/dist/utils/merkle.js +28 -0
  77. package/dist/utils/network.d.ts +2 -1
  78. package/dist/utils/network.d.ts.map +1 -1
  79. package/dist/utils/network.js +40 -15
  80. package/dist/utils/promiseUtils.d.ts +27 -0
  81. package/dist/utils/promiseUtils.d.ts.map +1 -0
  82. package/dist/utils/promiseUtils.js +69 -0
  83. package/dist/utils/ssl.js +2 -2
  84. package/package.json +19 -5
  85. package/dist/utils/deltaUtils.d.ts +0 -2
  86. package/dist/utils/deltaUtils.d.ts.map +0 -1
  87. package/dist/utils/deltaUtils.js +0 -83
@@ -5,27 +5,29 @@ 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 utils_1 = require("../utils");
14
14
  const serverCoinCollateral = 300000000;
15
+ // Initialize the cache with a TTL of 300 seconds (5 minutes)
16
+ const serverCoinPeersCache = new utils_1.DigCache({ stdTTL: 300 });
15
17
  class ServerCoin {
16
18
  constructor(storeId) {
17
19
  this.storeId = storeId;
18
20
  }
19
21
  // Create a new server coin for the current epoch
20
- async createForEpoch(peerIp) {
22
+ async createForEpoch(peerIp, rootHash) {
21
23
  try {
22
24
  const peer = await FullNodePeer_1.FullNodePeer.connect();
23
25
  const wallet = await Wallet_1.Wallet.load("default");
24
26
  const publicSyntheticKey = await wallet.getPublicSyntheticKey();
25
- const serverCoinCreationCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(serverCoinCollateral), BigInt(1000000));
26
- const currentEpoch = ServerCoin.getCurrentEpoch();
27
+ const serverCoinCreationCoins = await wallet.selectUnspentCoins(peer, BigInt(serverCoinCollateral), BigInt(1000000));
28
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
27
29
  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));
30
+ const newServerCoin = (0, datalayer_driver_1.createServerCoin)(publicSyntheticKey, serverCoinCreationCoins, epochBasedHint, [peerIp, publicSyntheticKey.toString("hex")], BigInt(serverCoinCollateral), BigInt(1000000));
29
31
  const combinedCoinSpends = [...newServerCoin.coinSpends];
30
32
  const sig = (0, datalayer_driver_1.signCoinSpends)(combinedCoinSpends, [await wallet.getPrivateSyntheticKey()], false);
31
33
  const err = await peer.broadcastSpend(combinedCoinSpends, [sig]);
@@ -33,12 +35,12 @@ class ServerCoin {
33
35
  if (err.includes("no spendable coins")) {
34
36
  console.log("No coins available. Will try again in 5 seconds...");
35
37
  await new Promise((resolve) => setTimeout(resolve, 5000));
36
- return this.createForEpoch(peerIp);
38
+ return this.createForEpoch(peerIp, rootHash);
37
39
  }
38
40
  throw new Error(err);
39
41
  }
40
42
  // Cache the new server coin in the NconfManager
41
- await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp);
43
+ await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp, rootHash);
42
44
  return newServerCoin.serverCoin;
43
45
  }
44
46
  catch (error) {
@@ -46,7 +48,7 @@ class ServerCoin {
46
48
  }
47
49
  }
48
50
  // Save the server coin data to NconfManager
49
- async saveServerCoinData(serverCoin, epoch, peerIp) {
51
+ async saveServerCoinData(serverCoin, epoch, peerIp, rootHash) {
50
52
  const newServerCoinData = {
51
53
  coin: {
52
54
  amount: serverCoin.coin.amount.toString(),
@@ -54,7 +56,8 @@ class ServerCoin {
54
56
  parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"),
55
57
  },
56
58
  createdAt: new Date().toISOString(),
57
- epoch: epoch,
59
+ epoch,
60
+ rootHash,
58
61
  };
59
62
  const serverCoins = await this.getServerCoinsForStore(peerIp);
60
63
  serverCoins.push(newServerCoinData);
@@ -70,7 +73,7 @@ class ServerCoin {
70
73
  if (!serverCoin) {
71
74
  throw new Error(`No server coin found for epoch ${epoch} in store ${this.storeId}.`);
72
75
  }
73
- const feeCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(0), BigInt(1000000));
76
+ const feeCoins = await wallet.selectUnspentCoins(peer, BigInt(0), BigInt(1000000));
74
77
  const coin = {
75
78
  amount: BigInt(serverCoin.coin.amount),
76
79
  puzzleHash: Buffer.from(serverCoin.coin.puzzleHash, "hex"),
@@ -94,13 +97,13 @@ class ServerCoin {
94
97
  serverCoins = serverCoins.filter((coin) => coin.coin.parentCoinInfo !== serverCoin.parentCoinInfo);
95
98
  await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
96
99
  }
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 = []) {
100
+ async getAllEpochPeers(epoch, blacklist = []) {
101
+ const cacheKey = `serverCoinPeers-${this.storeId}-${epoch}`;
102
+ // Check if the result is already cached
103
+ const cachedPeers = await serverCoinPeersCache.get(cacheKey);
104
+ if (cachedPeers) {
105
+ return cachedPeers;
106
+ }
104
107
  const epochBasedHint = (0, datalayer_driver_1.morphLauncherId)(Buffer.from(this.storeId, "hex"), BigInt(epoch));
105
108
  const peer = await FullNodePeer_1.FullNodePeer.connect();
106
109
  const maxClvmCost = BigInt(11000000000);
@@ -111,33 +114,83 @@ class ServerCoin {
111
114
  for (const coinState of filteredCoinStates) {
112
115
  const serverCoin = await peer.fetchServerCoin(coinState, maxClvmCost);
113
116
  const peerUrl = serverCoin.memoUrls[0];
117
+ // The second memo URL is the public key
118
+ // We will utilize this for future features
119
+ const publicKey = serverCoin.memoUrls[1];
114
120
  if (!blacklist.includes(peerUrl)) {
115
121
  serverCoinPeers.add(peerUrl);
116
122
  }
117
123
  }
118
- if (process.env.DIG_DEBUG === "1") {
124
+ if (utils_1.Environment.DEBUG) {
125
+ console.log("Server Coin Peers: ", serverCoinPeers);
126
+ }
127
+ const peerList = Array.from(serverCoinPeers);
128
+ // Cache the result
129
+ serverCoinPeersCache.set(cacheKey, peerList);
130
+ return peerList;
131
+ }
132
+ async getActiveEpochPeers(blacklist = []) {
133
+ const { epoch } = ServerCoin.getCurrentEpoch();
134
+ return this.getAllEpochPeers(epoch, blacklist);
135
+ }
136
+ // Sample server coins for the current epoch
137
+ async sampleCurrentEpoch(sampleSize = 5, blacklist = []) {
138
+ const { epoch } = ServerCoin.getCurrentEpoch();
139
+ return this.sampleServerCoinsByEpoch(epoch, sampleSize, blacklist);
140
+ }
141
+ // Sample server coins by epoch
142
+ async sampleServerCoinsByEpoch(epoch, sampleSize = 5, blacklist = []) {
143
+ // We dont want our own IP to be included
144
+ const host = await (0, utils_1.getPublicHost)();
145
+ if (host) {
146
+ blacklist.push(host);
147
+ }
148
+ const serverCoinPeers = await this.getAllEpochPeers(epoch, blacklist);
149
+ if (utils_1.Environment.DEBUG) {
119
150
  console.log("Server Coin Peers: ", serverCoinPeers);
120
151
  }
121
- // Convert the Set back to an array if needed
122
- return lodash_1.default.sampleSize(Array.from(serverCoinPeers), sampleSize);
152
+ return lodash_1.default.sampleSize(serverCoinPeers, sampleSize);
123
153
  }
124
154
  // Get the current epoch based on the current timestamp
125
155
  static getCurrentEpoch() {
126
- return ServerCoin.calculateEpoch(new Date());
156
+ return ServerCoin.calculateEpochAndRound(new Date());
127
157
  }
128
158
  // Ensure server coin exists for the current epoch
129
159
  async ensureServerCoinExists(peerIp) {
130
160
  try {
131
161
  console.log(`Ensuring server coin exists for store ${this.storeId}...`);
132
- const currentEpoch = ServerCoin.getCurrentEpoch();
162
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
133
163
  const serverCoins = await this.getServerCoinsForStore(peerIp);
134
164
  // Check if a server coin already exists for the current epoch
135
165
  const existingCoin = serverCoins.find((coin) => coin.epoch === currentEpoch);
166
+ const dataStore = DataStore_1.DataStore.from(this.storeId);
167
+ const rootHistory = await dataStore.getRootHistory(true);
168
+ const lastRoot = lodash_1.default.last(rootHistory);
169
+ if (!lastRoot) {
170
+ throw new Error('Cant get the last root on chain, something is wrong.');
171
+ }
136
172
  if (existingCoin) {
173
+ // nothing to do
174
+ if (lastRoot?.synced && lastRoot?.root_hash === existingCoin.rootHash) {
175
+ return;
176
+ }
177
+ // everything is fine, lets just update the roothash for tracking
178
+ if (lastRoot?.synced && lastRoot?.root_hash !== existingCoin.rootHash) {
179
+ existingCoin.rootHash = lastRoot.root_hash;
180
+ // Update the conf with the modified server coin
181
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
182
+ return;
183
+ }
184
+ // If not synced, melt the coin, a new one will be created when synced up
185
+ // this helps prevent penalties for not having a valid peer registered
186
+ await this.melt(currentEpoch, peerIp);
187
+ }
188
+ // Don't create a server coin until you're synced up
189
+ if (!lodash_1.default.last(rootHistory)?.synced) {
137
190
  return;
138
191
  }
139
192
  console.log(`No server coin found for epoch ${currentEpoch}. Creating new server coin...`);
140
- const serverCoin = await this.createForEpoch(peerIp);
193
+ const serverCoin = await this.createForEpoch(peerIp, lastRoot?.root_hash);
141
194
  const newServerCoinData = {
142
195
  coin: {
143
196
  amount: serverCoin.coin.amount.toString(),
@@ -146,6 +199,7 @@ class ServerCoin {
146
199
  },
147
200
  createdAt: new Date().toISOString(),
148
201
  epoch: currentEpoch,
202
+ rootHash: lodash_1.default.last(rootHistory)?.root_hash || "",
149
203
  };
150
204
  await FullNodePeer_1.FullNodePeer.waitForConfirmation(serverCoin.coin.parentCoinInfo);
151
205
  serverCoins.push(newServerCoinData);
@@ -160,7 +214,7 @@ class ServerCoin {
160
214
  // Melt outdated server coins
161
215
  async meltOutdatedEpochs(peerIp) {
162
216
  try {
163
- const currentEpoch = ServerCoin.getCurrentEpoch();
217
+ const { epoch: currentEpoch } = ServerCoin.getCurrentEpoch();
164
218
  let serverCoins = await this.getServerCoinsForStore(peerIp);
165
219
  // Filter out coins that are not in the current epoch
166
220
  const outdatedCoins = serverCoins.filter((coin) => coin.epoch < currentEpoch);
@@ -244,17 +298,23 @@ class ServerCoin {
244
298
  }
245
299
  }
246
300
  // Static method to calculate the current epoch
247
- static calculateEpoch(currentTimestampUTC) {
301
+ static calculateEpochAndRound(currentTimestampUTC) {
248
302
  const firstEpochStart = new Date(Date.UTC(2024, 8, 3, 0, 0)); // Sept 3, 2024, 00:00 UTC
249
303
  // Convert the current timestamp to milliseconds
250
304
  const currentTimestampMillis = currentTimestampUTC.getTime();
251
305
  // Calculate the number of milliseconds in one epoch (7 days)
252
- const millisecondsInEpoch = 7 * 24 * 60 * 60 * 1000;
306
+ const millisecondsInEpoch = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
253
307
  // Calculate the difference in milliseconds between the current timestamp and the first epoch start
254
308
  const differenceMillis = currentTimestampMillis - firstEpochStart.getTime();
255
309
  // Calculate the current epoch number
256
310
  const epochNumber = Math.floor(differenceMillis / millisecondsInEpoch) + 1;
257
- return epochNumber;
311
+ // Calculate the milliseconds elapsed since the start of the current epoch
312
+ const elapsedMillisInCurrentEpoch = differenceMillis % millisecondsInEpoch;
313
+ // Calculate the number of milliseconds in a round (10 minutes)
314
+ const millisecondsInRound = 10 * 60 * 1000; // 10 minutes in milliseconds
315
+ // Calculate the current round number
316
+ const roundNumber = Math.floor(elapsedMillisInCurrentEpoch / millisecondsInRound) + 1;
317
+ return { epoch: epochNumber, round: roundNumber };
258
318
  }
259
319
  }
260
320
  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"}