@dignetwork/dig-sdk 0.0.1-alpha.12 → 0.0.1-alpha.120

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 (77) hide show
  1. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +11 -2
  2. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
  3. package/dist/DataIntegrityTree/DataIntegrityTree.js +106 -15
  4. package/dist/DigNetwork/ContentServer.d.ts +12 -5
  5. package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
  6. package/dist/DigNetwork/ContentServer.js +172 -24
  7. package/dist/DigNetwork/DigNetwork.d.ts +3 -9
  8. package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
  9. package/dist/DigNetwork/DigNetwork.js +98 -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 +68 -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 +73 -33
  16. package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
  17. package/dist/DigNetwork/PropagationServer.js +545 -360
  18. package/dist/blockchain/DataStore.d.ts +18 -7
  19. package/dist/blockchain/DataStore.d.ts.map +1 -1
  20. package/dist/blockchain/DataStore.js +85 -101
  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 +2 -1
  24. package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
  25. package/dist/blockchain/FullNodePeer.js +39 -14
  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/StoreInfoCacheUpdater.d.ts +16 -0
  30. package/dist/blockchain/StoreInfoCacheUpdater.d.ts.map +1 -0
  31. package/dist/blockchain/StoreInfoCacheUpdater.js +242 -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/types.d.ts +2 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/utils/ContentScanner.d.ts +63 -0
  41. package/dist/utils/ContentScanner.d.ts.map +1 -0
  42. package/dist/utils/ContentScanner.js +175 -0
  43. package/dist/utils/Environment.d.ts +21 -0
  44. package/dist/utils/Environment.d.ts.map +1 -0
  45. package/dist/utils/Environment.js +92 -0
  46. package/dist/utils/FileCache.d.ts +1 -1
  47. package/dist/utils/FileCache.d.ts.map +1 -1
  48. package/dist/utils/FileCache.js +2 -2
  49. package/dist/utils/FileTransfer.d.ts +47 -0
  50. package/dist/utils/FileTransfer.d.ts.map +1 -0
  51. package/dist/utils/FileTransfer.js +209 -0
  52. package/dist/utils/NconfManager.js +2 -1
  53. package/dist/utils/StoreArchiveManager.d.ts +45 -0
  54. package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
  55. package/dist/utils/StoreArchiveManager.js +153 -0
  56. package/dist/utils/config.d.ts +4 -3
  57. package/dist/utils/config.d.ts.map +1 -1
  58. package/dist/utils/config.js +12 -7
  59. package/dist/utils/directoryUtils.d.ts.map +1 -1
  60. package/dist/utils/directoryUtils.js +32 -14
  61. package/dist/utils/index.d.ts +2 -1
  62. package/dist/utils/index.d.ts.map +1 -1
  63. package/dist/utils/index.js +2 -1
  64. package/dist/utils/merkle.d.ts +2 -0
  65. package/dist/utils/merkle.d.ts.map +1 -0
  66. package/dist/utils/merkle.js +28 -0
  67. package/dist/utils/network.d.ts +1 -0
  68. package/dist/utils/network.d.ts.map +1 -1
  69. package/dist/utils/network.js +38 -11
  70. package/dist/utils/promiseUtils.d.ts +17 -0
  71. package/dist/utils/promiseUtils.d.ts.map +1 -0
  72. package/dist/utils/promiseUtils.js +38 -0
  73. package/dist/utils/ssl.js +2 -2
  74. package/package.json +15 -5
  75. package/dist/utils/deltaUtils.d.ts +0 -2
  76. package/dist/utils/deltaUtils.d.ts.map +0 -1
  77. 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 myIp = await (0, network_1.getPublicIpAddress)();
147
+ if (myIp) {
148
+ blacklist.push(myIp);
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,16 @@
1
+ export declare class StoreInfoCacheUpdater {
2
+ private static instance;
3
+ private storeCoinCache;
4
+ private monitors;
5
+ private lockFilePath;
6
+ private releaseLock;
7
+ private isMonitoring;
8
+ private lockRenewalInterval;
9
+ private constructor();
10
+ static initInstance(): StoreInfoCacheUpdater;
11
+ private startMonitors;
12
+ private renewLock;
13
+ private monitorStore;
14
+ private isUnrecoverableError;
15
+ }
16
+ //# sourceMappingURL=StoreInfoCacheUpdater.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StoreInfoCacheUpdater.d.ts","sourceRoot":"","sources":["../../src/blockchain/StoreInfoCacheUpdater.ts"],"names":[],"mappings":"AAcA,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAwB;IAC/C,OAAO,CAAC,cAAc,CAIsB;IAC5C,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,mBAAmB,CAA+B;IAE1D,OAAO;WAiBO,YAAY,IAAI,qBAAqB;YAQrC,aAAa;IA6E3B,OAAO,CAAC,SAAS;YA0BH,YAAY;IAyI1B,OAAO,CAAC,oBAAoB;CAG7B"}
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.StoreInfoCacheUpdater = void 0;
30
+ const fs_1 = __importDefault(require("fs"));
31
+ const FullNodePeer_1 = require("./FullNodePeer");
32
+ const utils_1 = require("../utils");
33
+ const DataStoreSerializer_1 = require("./DataStoreSerializer");
34
+ const utils_2 = require("../utils");
35
+ const lockfile = __importStar(require("proper-lockfile"));
36
+ const path = __importStar(require("path"));
37
+ const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
38
+ class StoreInfoCacheUpdater {
39
+ constructor() {
40
+ this.storeCoinCache = new utils_1.FileCache(`stores`, utils_1.USER_DIR_PATH);
41
+ this.monitors = new Map();
42
+ this.releaseLock = null;
43
+ this.isMonitoring = true;
44
+ this.lockRenewalInterval = null;
45
+ console.log("Constructor: Initializing StoreInfoCacheUpdater");
46
+ // Construct lock file path using the path module
47
+ this.lockFilePath = path.join(utils_1.USER_DIR_PATH, "store-info-cache.lock");
48
+ console.log("Lock file path:", this.lockFilePath);
49
+ const lockDir = path.dirname(this.lockFilePath);
50
+ if (!fs_1.default.existsSync(lockDir)) {
51
+ console.log(`Creating lock directory: ${lockDir}`);
52
+ fs_1.default.mkdirSync(lockDir, { recursive: true });
53
+ }
54
+ // Start monitors for existing storeIds
55
+ this.startMonitors();
56
+ }
57
+ static initInstance() {
58
+ if (!StoreInfoCacheUpdater.instance) {
59
+ console.log("Initializing DataStore Monitor");
60
+ StoreInfoCacheUpdater.instance = new StoreInfoCacheUpdater();
61
+ }
62
+ return StoreInfoCacheUpdater.instance;
63
+ }
64
+ async startMonitors() {
65
+ try {
66
+ console.log("Checking if lockfile exists...");
67
+ // Check if the lock file exists
68
+ if (!fs_1.default.existsSync(this.lockFilePath)) {
69
+ console.log("Lockfile does not exist. Proceeding without lock.");
70
+ }
71
+ else {
72
+ // Check if the lockfile is already held
73
+ const isLocked = await lockfile.check(this.lockFilePath, {
74
+ realpath: false,
75
+ });
76
+ if (isLocked) {
77
+ console.log("Another process is already running the StoreInfoCacheUpdater.");
78
+ return;
79
+ }
80
+ }
81
+ // Attempt to acquire the lock
82
+ console.log("Attempting to acquire lock...");
83
+ this.releaseLock = await lockfile.lock(this.lockFilePath, {
84
+ retries: {
85
+ retries: 0, // No retries since we only need one lock
86
+ },
87
+ stale: 60000, // Lock expires after 1 minute
88
+ realpath: false, // Ensure lockfile uses the exact path
89
+ });
90
+ console.log("Lock acquired, starting monitors...");
91
+ // Renew the lock every minute by reacquiring it
92
+ this.renewLock();
93
+ const storeIds = this.storeCoinCache.getCachedKeys();
94
+ console.log(`Found ${storeIds.length} store IDs in cache:`, storeIds);
95
+ for (const storeId of storeIds) {
96
+ // Check if a monitor is already running for this storeId
97
+ if (!this.monitors.has(storeId)) {
98
+ console.log(`Starting monitor for storeId: ${storeId}`);
99
+ // Start monitoring in the background
100
+ const monitorPromise = this.monitorStore(storeId);
101
+ this.monitors.set(storeId, monitorPromise);
102
+ }
103
+ else {
104
+ console.log(`Monitor already exists for storeId: ${storeId}`);
105
+ }
106
+ }
107
+ this.isMonitoring = true;
108
+ // Wait for all monitors to settle
109
+ const monitorPromises = Array.from(this.monitors.values());
110
+ console.log("Waiting for all monitor promises to settle...");
111
+ await Promise.all(monitorPromises);
112
+ }
113
+ catch (error) {
114
+ console.error("Monitor system encountered an error:", error);
115
+ }
116
+ finally {
117
+ // Release the lock
118
+ if (this.releaseLock) {
119
+ try {
120
+ console.log("Releasing lock...");
121
+ await this.releaseLock();
122
+ console.log("Lock released successfully.");
123
+ }
124
+ catch (releaseError) {
125
+ console.error("Error releasing the lock:", releaseError);
126
+ }
127
+ }
128
+ // Clear the lock renewal interval
129
+ if (this.lockRenewalInterval) {
130
+ clearInterval(this.lockRenewalInterval);
131
+ this.lockRenewalInterval = null;
132
+ }
133
+ }
134
+ }
135
+ renewLock() {
136
+ // Set up a renewal process that releases and reacquires the lock every minute
137
+ this.lockRenewalInterval = setInterval(async () => {
138
+ try {
139
+ if (this.releaseLock) {
140
+ console.log("Releasing the lock for renewal...");
141
+ await this.releaseLock();
142
+ console.log("Lock released for renewal.");
143
+ }
144
+ // Reacquire the lock
145
+ this.releaseLock = await lockfile.lock(this.lockFilePath, {
146
+ retries: {
147
+ retries: 0, // No retries since we only need one lock
148
+ },
149
+ stale: 60000, // Lock expires after 1 minute
150
+ realpath: false, // Ensure lockfile uses the exact path
151
+ });
152
+ console.log("Lock reacquired for renewal.");
153
+ }
154
+ catch (error) {
155
+ console.error("Error renewing the lock:", error);
156
+ }
157
+ }, 60000); // Renew the lock every 60 seconds
158
+ }
159
+ // Monitor a single store's coin
160
+ async monitorStore(storeId) {
161
+ while (this.isMonitoring) {
162
+ let peer = null;
163
+ try {
164
+ console.log(`Monitoring store ${storeId}`);
165
+ // Connect to a peer
166
+ peer = await (0, utils_2.withTimeout)(FullNodePeer_1.FullNodePeer.connect(), 60000, "Timeout connecting to FullNodePeer");
167
+ // Get the latest store info (from cache if available)
168
+ const cachedInfo = this.storeCoinCache.get(storeId);
169
+ if (!cachedInfo) {
170
+ // If no cached info, skip and wait before retrying
171
+ console.error(`No cached info for storeId ${storeId}`);
172
+ await new Promise((resolve) => setTimeout(resolve, 5000));
173
+ continue;
174
+ }
175
+ const { latestStore: serializedStore, latestHeight, latestHash, } = cachedInfo;
176
+ const { latestStore } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
177
+ latestStore: serializedStore,
178
+ latestHeight: latestHeight.toString(),
179
+ latestHash: latestHash,
180
+ });
181
+ // Get the coinId associated with the store
182
+ const coinId = (0, datalayer_driver_1.getCoinId)(latestStore.coin);
183
+ console.log(`Waiting for coin to be spent: ${coinId.toString("hex")}`);
184
+ try {
185
+ // Wait for the coin to be spent
186
+ await peer.waitForCoinToBeSpent(coinId, latestHeight, Buffer.from(latestHash, "hex"));
187
+ }
188
+ catch {
189
+ const genesisChallenge = await (0, datalayer_driver_1.getMainnetGenesisChallenge)();
190
+ const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, null, genesisChallenge, false), 60000, `Timeout syncing store for storeId ${storeId}`);
191
+ const headerHash = await peer.getHeaderHash(storeInfo.latestHeight);
192
+ await peer.waitForCoinToBeSpent((0, datalayer_driver_1.getCoinId)(storeInfo.latestStore.coin), storeInfo.latestHeight, headerHash);
193
+ }
194
+ console.log(`Detected Coin Spend: ${coinId.toString("hex")}`);
195
+ let updatedStore, newHeight;
196
+ try {
197
+ // When resolved, sync the store
198
+ const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, latestHeight, Buffer.from(latestHash, "hex"), false // withHistory
199
+ ), 60000, `Timeout syncing store for storeId ${storeId}`);
200
+ updatedStore = storeInfo.latestStore;
201
+ newHeight = storeInfo.latestHeight;
202
+ }
203
+ catch {
204
+ const genesisChallenge = await (0, datalayer_driver_1.getMainnetGenesisChallenge)();
205
+ const storeInfo = await (0, utils_2.withTimeout)(peer.syncStore(latestStore, null, genesisChallenge, false), 60000, `Timeout syncing store for storeId ${storeId}`);
206
+ updatedStore = storeInfo.latestStore;
207
+ newHeight = storeInfo.latestHeight;
208
+ }
209
+ // Get the latest header hash
210
+ const latestHashBuffer = await (0, utils_2.withTimeout)(peer.getHeaderHash(newHeight), 60000, `Timeout getting header hash for height ${newHeight}`);
211
+ // Serialize the updated store data for caching
212
+ const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(updatedStore, newHeight, latestHashBuffer).serialize();
213
+ // Update the cache
214
+ this.storeCoinCache.set(storeId, {
215
+ latestStore: serializedLatestStore,
216
+ latestHeight: newHeight,
217
+ latestHash: latestHashBuffer.toString("hex"),
218
+ });
219
+ peer = null;
220
+ // Continue monitoring
221
+ }
222
+ catch (error) {
223
+ console.error(`Error monitoring store ${storeId}:`, error);
224
+ // Close the peer connection if it's open
225
+ if (peer) {
226
+ peer = null;
227
+ }
228
+ // Determine if the error is unrecoverable
229
+ if (this.isUnrecoverableError(error)) {
230
+ this.isMonitoring = false; // Signal other monitors to stop
231
+ throw error; // Propagate error up to stop monitoring
232
+ }
233
+ // Wait before retrying
234
+ await new Promise((resolve) => setTimeout(resolve, 5000));
235
+ }
236
+ }
237
+ }
238
+ isUnrecoverableError(error) {
239
+ return true;
240
+ }
241
+ }
242
+ exports.StoreInfoCacheUpdater = StoreInfoCacheUpdater;
@@ -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"}
@@ -38,9 +38,10 @@ const chia_root_resolver_1 = require("chia-root-resolver");
38
38
  const chia_config_loader_1 = require("chia-config-loader");
39
39
  const encryption_1 = require("../utils/encryption");
40
40
  const buffer_1 = require("buffer");
41
- const datalayer_driver_1 = require("datalayer-driver");
41
+ const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
42
42
  const config_1 = require("../utils/config");
43
43
  const FileCache_1 = require("../utils/FileCache");
44
+ const config_2 = require("../utils/config");
44
45
  const KEYRING_FILE = "keyring.json";
45
46
  const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
46
47
  exports.DEFAULT_FEE_COIN_COST = 64000000;
@@ -174,31 +175,62 @@ class Wallet {
174
175
  const signature = (0, datalayer_driver_1.signMessage)(buffer_1.Buffer.from(message, "utf-8"), privateSyntheticKey);
175
176
  return signature.toString("hex");
176
177
  }
177
- async verifyKeyOwnershipSignature(nonce, signature, publicKey) {
178
+ static async verifyKeyOwnershipSignature(nonce, signature, publicKey) {
178
179
  const message = `Signing this message to prove ownership of key.\n\nNonce: ${nonce}`;
179
180
  return (0, datalayer_driver_1.verifySignedMessage)(buffer_1.Buffer.from(signature, "hex"), buffer_1.Buffer.from(publicKey, "hex"), buffer_1.Buffer.from(message, "utf-8"));
180
181
  }
181
182
  async selectUnspentCoins(peer, coinAmount, feeBigInt, omitCoins = []) {
182
- const cache = new FileCache_1.FileCache("reserved_coins");
183
- const cachedReservedCoins = cache.getCachedKeys();
184
- const now = Date.now();
185
- const omitCoinIds = omitCoins.map((coin) => (0, datalayer_driver_1.getCoinId)(coin).toString("hex"));
186
- cachedReservedCoins.forEach((coinId) => {
187
- const reservation = cache.get(coinId);
188
- if (reservation && reservation.expiry > now) {
189
- omitCoinIds.push(coinId);
183
+ const cache = new FileCache_1.FileCache("reserved_coins", config_2.USER_DIR_PATH);
184
+ const ownerPuzzleHash = await this.getOwnerPuzzleHash();
185
+ // Define a function to attempt selecting unspent coins
186
+ const trySelectCoins = async () => {
187
+ const now = Date.now();
188
+ const omitCoinIds = omitCoins.map((coin) => (0, datalayer_driver_1.getCoinId)(coin).toString("hex"));
189
+ // Update omitCoinIds with currently valid reserved coins
190
+ const cachedReservedCoins = cache.getCachedKeys();
191
+ cachedReservedCoins.forEach((coinId) => {
192
+ const reservation = cache.get(coinId);
193
+ if (reservation && reservation.expiry > now) {
194
+ if (!omitCoinIds.includes(coinId)) {
195
+ omitCoinIds.push(coinId);
196
+ }
197
+ }
198
+ else {
199
+ cache.delete(coinId);
200
+ }
201
+ });
202
+ const coinsResp = await peer.getAllUnspentCoins(ownerPuzzleHash, config_1.MIN_HEIGHT, buffer_1.Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
203
+ const unspentCoins = coinsResp.coins.filter((coin) => !omitCoinIds.includes((0, datalayer_driver_1.getCoinId)(coin).toString("hex")));
204
+ const selectedCoins = (0, datalayer_driver_1.selectCoins)(unspentCoins, feeBigInt + coinAmount);
205
+ return selectedCoins;
206
+ };
207
+ let selectedCoins = [];
208
+ let retry = true;
209
+ while (retry) {
210
+ selectedCoins = await trySelectCoins();
211
+ if (selectedCoins.length > 0) {
212
+ // Coins have been successfully selected
213
+ retry = false;
190
214
  }
191
215
  else {
192
- cache.delete(coinId);
216
+ const now = Date.now();
217
+ // Check if there are any valid cached reserved coins left
218
+ const cachedReservedCoins = cache.getCachedKeys().filter((coinId) => {
219
+ const reservation = cache.get(coinId);
220
+ return reservation && reservation.expiry > now;
221
+ });
222
+ if (cachedReservedCoins.length > 0) {
223
+ // Wait 10 seconds and try again
224
+ console.log("No unspent coins available. Waiting 10 seconds...");
225
+ await new Promise((resolve) => setTimeout(resolve, 10000));
226
+ }
227
+ else {
228
+ // No unspent coins and no reserved coins
229
+ throw new Error("No unspent coins available.");
230
+ }
193
231
  }
194
- });
195
- const ownerPuzzleHash = await this.getOwnerPuzzleHash();
196
- const coinsResp = await peer.getAllUnspentCoins(ownerPuzzleHash, config_1.MIN_HEIGHT, buffer_1.Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
197
- const unspentCoins = coinsResp.coins.filter((coin) => !omitCoinIds.includes((0, datalayer_driver_1.getCoinId)(coin).toString("hex")));
198
- const selectedCoins = (0, datalayer_driver_1.selectCoins)(unspentCoins, feeBigInt + coinAmount);
199
- if (selectedCoins.length === 0) {
200
- throw new Error("No unspent coins available.");
201
232
  }
233
+ // Reserve the selected coins
202
234
  selectedCoins.forEach((coin) => {
203
235
  const coinId = (0, datalayer_driver_1.getCoinId)(coin).toString("hex");
204
236
  cache.set(coinId, { coinId, expiry: Date.now() + CACHE_DURATION });
@@ -1,6 +1,5 @@
1
- import { Peer, Coin, CoinSpend } from "datalayer-driver";
1
+ import { Peer, CoinSpend } from "@dignetwork/datalayer-driver";
2
2
  export declare const DEFAULT_FEE_COIN_COST = 64000000;
3
3
  export declare const calculateFeeForCoinSpends: (peer: Peer, coinSpends: CoinSpend[] | null) => Promise<bigint>;
4
- export declare const selectUnspentCoins: (peer: Peer, coinAmount: bigint, feeBigInt: bigint, omitCoins?: Coin[], walletName?: string) => Promise<Coin[]>;
5
4
  export declare const isCoinSpendable: (peer: Peer, coinId: Buffer) => Promise<boolean>;
6
5
  //# sourceMappingURL=coins.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"coins.d.ts","sourceRoot":"","sources":["../../src/blockchain/coins.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,EACJ,IAAI,EAEJ,SAAS,EAEV,MAAM,kBAAkB,CAAC;AAK1B,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAWhD,eAAO,MAAM,yBAAyB,SAC9B,IAAI,cACE,SAAS,EAAE,GAAG,IAAI,KAC7B,OAAO,CAAC,MAAM,CA4BhB,CAAC;AAEF,eAAO,MAAM,kBAAkB,SACvB,IAAI,cACE,MAAM,aACP,MAAM,cACN,IAAI,EAAE,eACL,MAAM,KACjB,OAAO,CAAC,IAAI,EAAE,CAoEhB,CAAC;AAGF,eAAO,MAAM,eAAe,SACpB,IAAI,UACF,MAAM,KACb,OAAO,CAAC,OAAO,CAWjB,CAAC"}
1
+ {"version":3,"file":"coins.d.ts","sourceRoot":"","sources":["../../src/blockchain/coins.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,IAAI,EAGJ,SAAS,EAEV,MAAM,8BAA8B,CAAC;AAMtC,eAAO,MAAM,qBAAqB,WAAa,CAAC;AAWhD,eAAO,MAAM,yBAAyB,SAC9B,IAAI,cACE,SAAS,EAAE,GAAG,IAAI,KAC7B,OAAO,CAAC,MAAM,CA4BhB,CAAC;AAEF,eAAO,MAAM,eAAe,SACpB,IAAI,UACF,MAAM,KACb,OAAO,CAAC,OAAO,CAWjB,CAAC"}