@dignetwork/dig-sdk 0.0.1-alpha.18 → 0.0.1-alpha.181

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