@dignetwork/dig-sdk 0.0.1-alpha.2

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 (104) hide show
  1. package/LICENSE +7 -0
  2. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +160 -0
  3. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -0
  4. package/dist/DataIntegrityTree/DataIntegrityTree.js +648 -0
  5. package/dist/DataIntegrityTree/DataLayerError.d.ts +6 -0
  6. package/dist/DataIntegrityTree/DataLayerError.d.ts.map +1 -0
  7. package/dist/DataIntegrityTree/DataLayerError.js +17 -0
  8. package/dist/DataIntegrityTree/index.d.ts +2 -0
  9. package/dist/DataIntegrityTree/index.d.ts.map +1 -0
  10. package/dist/DataIntegrityTree/index.js +17 -0
  11. package/dist/DigNetwork/ContentServer.d.ts +28 -0
  12. package/dist/DigNetwork/ContentServer.d.ts.map +1 -0
  13. package/dist/DigNetwork/ContentServer.js +243 -0
  14. package/dist/DigNetwork/DigChallenge.d.ts +82 -0
  15. package/dist/DigNetwork/DigChallenge.d.ts.map +1 -0
  16. package/dist/DigNetwork/DigChallenge.js +144 -0
  17. package/dist/DigNetwork/DigNetwork.d.ts +19 -0
  18. package/dist/DigNetwork/DigNetwork.d.ts.map +1 -0
  19. package/dist/DigNetwork/DigNetwork.js +222 -0
  20. package/dist/DigNetwork/DigPeer.d.ts +19 -0
  21. package/dist/DigNetwork/DigPeer.d.ts.map +1 -0
  22. package/dist/DigNetwork/DigPeer.js +164 -0
  23. package/dist/DigNetwork/IncentiveServer.d.ts +13 -0
  24. package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -0
  25. package/dist/DigNetwork/IncentiveServer.js +76 -0
  26. package/dist/DigNetwork/PropagationServer.d.ts +41 -0
  27. package/dist/DigNetwork/PropagationServer.d.ts.map +1 -0
  28. package/dist/DigNetwork/PropagationServer.js +415 -0
  29. package/dist/DigNetwork/index.d.ts +4 -0
  30. package/dist/DigNetwork/index.d.ts.map +1 -0
  31. package/dist/DigNetwork/index.js +19 -0
  32. package/dist/blockchain/DataStore.d.ts +54 -0
  33. package/dist/blockchain/DataStore.d.ts.map +1 -0
  34. package/dist/blockchain/DataStore.js +410 -0
  35. package/dist/blockchain/DataStoreSerializer.d.ts +52 -0
  36. package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -0
  37. package/dist/blockchain/DataStoreSerializer.js +109 -0
  38. package/dist/blockchain/FullNodePeer.d.ts +17 -0
  39. package/dist/blockchain/FullNodePeer.d.ts.map +1 -0
  40. package/dist/blockchain/FullNodePeer.js +215 -0
  41. package/dist/blockchain/ServerCoin.d.ts +21 -0
  42. package/dist/blockchain/ServerCoin.d.ts.map +1 -0
  43. package/dist/blockchain/ServerCoin.js +261 -0
  44. package/dist/blockchain/Wallet.d.ts +28 -0
  45. package/dist/blockchain/Wallet.d.ts.map +1 -0
  46. package/dist/blockchain/Wallet.js +220 -0
  47. package/dist/blockchain/coins.d.ts +6 -0
  48. package/dist/blockchain/coins.d.ts.map +1 -0
  49. package/dist/blockchain/coins.js +101 -0
  50. package/dist/blockchain/index.d.ts +8 -0
  51. package/dist/blockchain/index.d.ts.map +1 -0
  52. package/dist/blockchain/index.js +36 -0
  53. package/dist/index.d.ts +5 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +20 -0
  56. package/dist/prompts.d.ts +21 -0
  57. package/dist/prompts.d.ts.map +1 -0
  58. package/dist/prompts.js +149 -0
  59. package/dist/types.d.ts +62 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +2 -0
  62. package/dist/utils/FileCache.d.ts +11 -0
  63. package/dist/utils/FileCache.d.ts.map +1 -0
  64. package/dist/utils/FileCache.js +58 -0
  65. package/dist/utils/NconfManager.d.ts +11 -0
  66. package/dist/utils/NconfManager.d.ts.map +1 -0
  67. package/dist/utils/NconfManager.js +55 -0
  68. package/dist/utils/config.d.ts +67 -0
  69. package/dist/utils/config.d.ts.map +1 -0
  70. package/dist/utils/config.js +223 -0
  71. package/dist/utils/connectionUtils.d.ts +10 -0
  72. package/dist/utils/connectionUtils.d.ts.map +1 -0
  73. package/dist/utils/connectionUtils.js +18 -0
  74. package/dist/utils/credentialsUtils.d.ts +8 -0
  75. package/dist/utils/credentialsUtils.d.ts.map +1 -0
  76. package/dist/utils/credentialsUtils.js +122 -0
  77. package/dist/utils/deltaUtils.d.ts +2 -0
  78. package/dist/utils/deltaUtils.d.ts.map +1 -0
  79. package/dist/utils/deltaUtils.js +83 -0
  80. package/dist/utils/directoryUtils.d.ts +15 -0
  81. package/dist/utils/directoryUtils.d.ts.map +1 -0
  82. package/dist/utils/directoryUtils.js +91 -0
  83. package/dist/utils/encryption.d.ts +8 -0
  84. package/dist/utils/encryption.d.ts.map +1 -0
  85. package/dist/utils/encryption.js +55 -0
  86. package/dist/utils/hashUtils.d.ts +8 -0
  87. package/dist/utils/hashUtils.d.ts.map +1 -0
  88. package/dist/utils/hashUtils.js +44 -0
  89. package/dist/utils/index.d.ts +14 -0
  90. package/dist/utils/index.d.ts.map +1 -0
  91. package/dist/utils/index.js +29 -0
  92. package/dist/utils/network.d.ts +2 -0
  93. package/dist/utils/network.d.ts.map +1 -0
  94. package/dist/utils/network.js +38 -0
  95. package/dist/utils/spinnerUtils.d.ts +2 -0
  96. package/dist/utils/spinnerUtils.d.ts.map +1 -0
  97. package/dist/utils/spinnerUtils.js +47 -0
  98. package/dist/utils/ssl.d.ts +5 -0
  99. package/dist/utils/ssl.d.ts.map +1 -0
  100. package/dist/utils/ssl.js +50 -0
  101. package/dist/utils/validationUtils.d.ts +9 -0
  102. package/dist/utils/validationUtils.d.ts.map +1 -0
  103. package/dist/utils/validationUtils.js +55 -0
  104. package/package.json +64 -0
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FullNodePeer = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const datalayer_driver_1 = require("datalayer-driver");
11
+ const chia_server_coin_1 = require("chia-server-coin");
12
+ const promises_1 = require("dns/promises");
13
+ const net_1 = __importDefault(require("net"));
14
+ const lodash_1 = require("lodash");
15
+ const nanospinner_1 = require("nanospinner");
16
+ const config_1 = require("../utils/config");
17
+ const FULLNODE_PORT = 8444;
18
+ const LOCALHOST = "127.0.0.1";
19
+ const DNS_HOSTS = [
20
+ "dns-introducer.chia.net",
21
+ "chia.ctrlaltdel.ch",
22
+ "seeder.dexie.space",
23
+ "chia.hoffmang.com",
24
+ ];
25
+ const CONNECTION_TIMEOUT = 2000;
26
+ const CACHE_DURATION = 30000; // Cache duration in milliseconds
27
+ class FullNodePeer {
28
+ constructor(peer) {
29
+ this.peer = peer;
30
+ }
31
+ static async connect() {
32
+ const peer = await FullNodePeer.getBestPeer();
33
+ return new FullNodePeer(peer).peer;
34
+ }
35
+ static isPortReachable(host, port, timeout = CONNECTION_TIMEOUT) {
36
+ return new Promise((resolve) => {
37
+ const socket = new net_1.default.Socket()
38
+ .setTimeout(timeout)
39
+ .once("error", () => resolve(false))
40
+ .once("timeout", () => resolve(false))
41
+ .connect(port, host, () => {
42
+ socket.end();
43
+ resolve(true);
44
+ });
45
+ });
46
+ }
47
+ static isValidIpAddress(ip) {
48
+ const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
49
+ return ipv4Regex.test(ip);
50
+ }
51
+ static async fetchNewPeerIPs() {
52
+ const trustedNodeIp = process.env.TRUSTED_FULLNODE || null;
53
+ const priorityIps = [];
54
+ // Prioritize trustedNodeIp
55
+ if (trustedNodeIp &&
56
+ FullNodePeer.isValidIpAddress(trustedNodeIp) &&
57
+ (await FullNodePeer.isPortReachable(trustedNodeIp, FULLNODE_PORT))) {
58
+ priorityIps.push(trustedNodeIp);
59
+ }
60
+ // Prioritize LOCALHOST
61
+ if (await FullNodePeer.isPortReachable(LOCALHOST, FULLNODE_PORT)) {
62
+ priorityIps.push(LOCALHOST);
63
+ }
64
+ if (priorityIps.length > 0) {
65
+ return priorityIps;
66
+ }
67
+ // Fetch peers from DNS introducers
68
+ for (const DNS_HOST of DNS_HOSTS) {
69
+ try {
70
+ const ips = await (0, promises_1.resolve4)(DNS_HOST);
71
+ if (ips && ips.length > 0) {
72
+ const shuffledIps = ips.sort(() => 0.5 - Math.random());
73
+ const reachableIps = [];
74
+ for (const ip of shuffledIps) {
75
+ if (await FullNodePeer.isPortReachable(ip, FULLNODE_PORT)) {
76
+ reachableIps.push(ip);
77
+ }
78
+ if (reachableIps.length === 5)
79
+ break;
80
+ }
81
+ if (reachableIps.length > 0) {
82
+ return reachableIps;
83
+ }
84
+ }
85
+ }
86
+ catch (error) {
87
+ console.error(`Failed to resolve IPs from ${DNS_HOST}: ${error.message}`);
88
+ }
89
+ }
90
+ throw new Error("No reachable IPs found in any DNS records.");
91
+ }
92
+ static async getPeerIPs() {
93
+ const ips = await FullNodePeer.memoizedFetchNewPeerIPs();
94
+ const reachableIps = await Promise.all(ips.map(async (ip) => {
95
+ if (ip && (await FullNodePeer.isPortReachable(ip, FULLNODE_PORT))) {
96
+ return ip;
97
+ }
98
+ return null;
99
+ })).then((results) => results.filter((ip) => ip !== null));
100
+ if (reachableIps.length > 0) {
101
+ return reachableIps;
102
+ }
103
+ // @ts-ignore
104
+ if (FullNodePeer.memoizedFetchNewPeerIPs?.cache?.clear) {
105
+ // @ts-ignore
106
+ FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
107
+ }
108
+ return FullNodePeer.memoizedFetchNewPeerIPs();
109
+ }
110
+ static createPeerProxy(peer, certFile, keyFile) {
111
+ return new Proxy(peer, {
112
+ get: (target, prop) => {
113
+ const originalMethod = target[prop];
114
+ if (typeof originalMethod === "function") {
115
+ return async (...args) => {
116
+ try {
117
+ return await originalMethod.apply(target, args);
118
+ }
119
+ catch (error) {
120
+ if (error.message.includes("WebSocket")) {
121
+ FullNodePeer.cachedPeer = null;
122
+ const newPeer = await FullNodePeer.getBestPeer();
123
+ return newPeer[prop](...args);
124
+ }
125
+ throw error;
126
+ }
127
+ };
128
+ }
129
+ return originalMethod;
130
+ },
131
+ });
132
+ }
133
+ static async getBestPeer() {
134
+ const now = Date.now();
135
+ if (FullNodePeer.cachedPeer &&
136
+ now - FullNodePeer.cachedPeer.timestamp < CACHE_DURATION) {
137
+ return FullNodePeer.cachedPeer.peer;
138
+ }
139
+ const sslFolder = path_1.default.resolve(os_1.default.homedir(), ".dig", "ssl");
140
+ const certFile = path_1.default.join(sslFolder, "public_dig.crt");
141
+ const keyFile = path_1.default.join(sslFolder, "public_dig.key");
142
+ if (!fs_1.default.existsSync(sslFolder)) {
143
+ fs_1.default.mkdirSync(sslFolder, { recursive: true });
144
+ }
145
+ new chia_server_coin_1.Tls(certFile, keyFile);
146
+ const peerIPs = await FullNodePeer.getPeerIPs();
147
+ const trustedNodeIp = process.env.TRUSTED_FULLNODE || null;
148
+ const peers = await Promise.all(peerIPs.map(async (ip) => {
149
+ if (ip) {
150
+ try {
151
+ const peer = await datalayer_driver_1.Peer.new(`${ip}:${FULLNODE_PORT}`, false, certFile, keyFile);
152
+ return FullNodePeer.createPeerProxy(peer, certFile, keyFile);
153
+ }
154
+ catch (error) {
155
+ console.error(`Failed to create peer for IP ${ip}: ${error.message}`);
156
+ return null;
157
+ }
158
+ }
159
+ return null;
160
+ })).then((results) => results.filter((peer) => peer !== null));
161
+ if (peers.length === 0) {
162
+ throw new Error("No peers available, please try again.");
163
+ }
164
+ await new Promise((resolve) => setTimeout(resolve, 1000));
165
+ const peakHeights = await Promise.all(peers.map((peer) => peer
166
+ .getPeak()
167
+ .then((height) => height)
168
+ .catch((error) => {
169
+ console.error(`Failed to get peak for peer: ${error.message}`);
170
+ return null;
171
+ })));
172
+ const validHeights = peakHeights.filter((height) => height !== null);
173
+ if (validHeights.length === 0) {
174
+ throw new Error("No valid peak heights obtained from any peer.");
175
+ }
176
+ const highestPeak = Math.max(...validHeights);
177
+ // Prioritize LOCALHOST and TRUSTED_NODE_IP if they have the highest peak height
178
+ let bestPeerIndex = validHeights.findIndex((height, index) => height === highestPeak &&
179
+ (peerIPs[index] === LOCALHOST || peerIPs[index] === trustedNodeIp));
180
+ // If LOCALHOST or TRUSTED_NODE_IP don't have the highest peak, select any peer with the highest peak
181
+ if (bestPeerIndex === -1) {
182
+ bestPeerIndex = validHeights.findIndex((height) => height === highestPeak);
183
+ }
184
+ const bestPeer = peers[bestPeerIndex];
185
+ FullNodePeer.cachedPeer = { peer: bestPeer, timestamp: now };
186
+ console.log(`Using Peer: ${peerIPs[bestPeerIndex]}`);
187
+ return bestPeer;
188
+ }
189
+ getPeer() {
190
+ return this.peer;
191
+ }
192
+ static async waitForConfirmation(parentCoinInfo) {
193
+ const spinner = (0, nanospinner_1.createSpinner)("Waiting for confirmation...").start();
194
+ const peer = await FullNodePeer.connect();
195
+ try {
196
+ while (true) {
197
+ const confirmed = await peer.isCoinSpent(parentCoinInfo, config_1.MIN_HEIGHT, Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex"));
198
+ if (confirmed) {
199
+ spinner.success({ text: "Coin confirmed!" });
200
+ return true;
201
+ }
202
+ await new Promise((resolve) => setTimeout(resolve, 5000));
203
+ }
204
+ }
205
+ catch (error) {
206
+ spinner.error({ text: "Error while waiting for confirmation." });
207
+ throw error;
208
+ }
209
+ }
210
+ }
211
+ exports.FullNodePeer = FullNodePeer;
212
+ FullNodePeer.cachedPeer = null;
213
+ (() => {
214
+ FullNodePeer.memoizedFetchNewPeerIPs = (0, lodash_1.memoize)(FullNodePeer.fetchNewPeerIPs);
215
+ })();
@@ -0,0 +1,21 @@
1
+ import { ServerCoin as ServerCoinDriver } from "datalayer-driver";
2
+ import { NconfManager } from "../utils/NconfManager";
3
+ export declare class ServerCoin {
4
+ private storeId;
5
+ static readonly serverCoinManager: NconfManager;
6
+ constructor(storeId: string);
7
+ createForEpoch(peerIp: string): Promise<ServerCoinDriver>;
8
+ saveServerCoinData(serverCoin: ServerCoinDriver, epoch: number, peerIp: string): Promise<void>;
9
+ melt(epoch: number, peerIp: string): Promise<void>;
10
+ private removeServerCoinData;
11
+ sampleCurrentEpoch(sampleSize?: number, blacklist?: string[]): Promise<string[]>;
12
+ sampleServerCoinsByEpoch(epoch: number, sampleSize?: number, blacklist?: string[]): Promise<string[]>;
13
+ static getCurrentEpoch(): number;
14
+ ensureServerCoinExists(peerIp: string): Promise<void>;
15
+ meltOutdatedEpochs(peerIp: string): Promise<void>;
16
+ static meltUntrackedStoreCoins(): Promise<void>;
17
+ private getServerCoinsForStore;
18
+ hasEpochCoinBeenCreated(currentEpoch: number, peerIp: string): Promise<boolean>;
19
+ static calculateEpoch(currentTimestampUTC: Date): number;
20
+ }
21
+ //# sourceMappingURL=ServerCoin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServerCoin.d.ts","sourceRoot":"","sources":["../../src/blockchain/ServerCoin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,UAAU,IAAI,gBAAgB,EAE/B,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAMrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAAwC;gBAGpE,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2DzD,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAqBH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoDjD,oBAAoB;IAgBrB,kBAAkB,CAC7B,UAAU,GAAE,MAAU,EACtB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAMP,wBAAwB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAU,EACtB,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;WAsCN,eAAe,IAAI,MAAM;IAK1B,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDrD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WA+B1C,uBAAuB;YA4D7B,sBAAsB;IAWvB,uBAAuB,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;WAuBL,cAAc,CAAC,mBAAmB,EAAE,IAAI,GAAG,MAAM;CAiBhE"}
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ServerCoin = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const datalayer_driver_1 = require("datalayer-driver");
9
+ const FullNodePeer_1 = require("./FullNodePeer");
10
+ const coins_1 = require("./coins");
11
+ const Wallet_1 = require("./Wallet");
12
+ const NconfManager_1 = require("../utils/NconfManager");
13
+ const DataStore_1 = require("./DataStore");
14
+ const serverCoinCollateral = 300000000;
15
+ class ServerCoin {
16
+ constructor(storeId) {
17
+ this.storeId = storeId;
18
+ }
19
+ // Create a new server coin for the current epoch
20
+ async createForEpoch(peerIp) {
21
+ try {
22
+ const peer = await FullNodePeer_1.FullNodePeer.connect();
23
+ const wallet = await Wallet_1.Wallet.load("default");
24
+ 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 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));
29
+ const combinedCoinSpends = [...newServerCoin.coinSpends];
30
+ const sig = (0, datalayer_driver_1.signCoinSpends)(combinedCoinSpends, [await wallet.getPrivateSyntheticKey()], false);
31
+ const err = await peer.broadcastSpend(combinedCoinSpends, [sig]);
32
+ if (err) {
33
+ if (err.includes("no spendable coins")) {
34
+ console.log("No coins available. Will try again in 5 seconds...");
35
+ await new Promise((resolve) => setTimeout(resolve, 5000));
36
+ return this.createForEpoch(peerIp);
37
+ }
38
+ throw new Error(err);
39
+ }
40
+ // Cache the new server coin in the NconfManager
41
+ await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp);
42
+ return newServerCoin.serverCoin;
43
+ }
44
+ catch (error) {
45
+ throw new Error("Failed to create server coin: " + error.message);
46
+ }
47
+ }
48
+ // Save the server coin data to NconfManager
49
+ async saveServerCoinData(serverCoin, epoch, peerIp) {
50
+ const newServerCoinData = {
51
+ coin: {
52
+ amount: serverCoin.coin.amount.toString(),
53
+ puzzleHash: serverCoin.coin.puzzleHash.toString("hex"),
54
+ parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"),
55
+ },
56
+ createdAt: new Date().toISOString(),
57
+ epoch: epoch,
58
+ };
59
+ const serverCoins = await this.getServerCoinsForStore(peerIp);
60
+ serverCoins.push(newServerCoinData);
61
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
62
+ }
63
+ // Melt server coin by epoch
64
+ async melt(epoch, peerIp) {
65
+ const peer = await FullNodePeer_1.FullNodePeer.connect();
66
+ const wallet = await Wallet_1.Wallet.load("default");
67
+ const publicSyntheticKey = await wallet.getPublicSyntheticKey();
68
+ const serverCoins = await this.getServerCoinsForStore(peerIp);
69
+ const serverCoin = serverCoins.find((coin) => coin.epoch === epoch);
70
+ if (!serverCoin) {
71
+ throw new Error(`No server coin found for epoch ${epoch} in store ${this.storeId}.`);
72
+ }
73
+ const feeCoins = await (0, coins_1.selectUnspentCoins)(peer, BigInt(0), BigInt(1000000));
74
+ const coin = {
75
+ amount: BigInt(serverCoin.coin.amount),
76
+ puzzleHash: Buffer.from(serverCoin.coin.puzzleHash, "hex"),
77
+ parentCoinInfo: Buffer.from(serverCoin.coin.parentCoinInfo, "hex"),
78
+ };
79
+ const serverCoinId = (0, datalayer_driver_1.getCoinId)(coin);
80
+ console.log("Melt Coin ID: ", serverCoinId.toString("hex"));
81
+ const spendBundle = await peer.lookupAndSpendServerCoins(publicSyntheticKey, [coin, ...feeCoins], BigInt(1000000), false);
82
+ const sig = (0, datalayer_driver_1.signCoinSpends)(spendBundle, [await wallet.getPrivateSyntheticKey()], false);
83
+ const err = await peer.broadcastSpend(spendBundle, [sig]);
84
+ if (err) {
85
+ throw new Error(err);
86
+ }
87
+ await FullNodePeer_1.FullNodePeer.waitForConfirmation(serverCoinId);
88
+ // Remove the melted coin from the NconfManager
89
+ await this.removeServerCoinData(serverCoin.coin, peerIp);
90
+ }
91
+ // Remove the melted server coin from NconfManager
92
+ async removeServerCoinData(serverCoin, peerIp) {
93
+ let serverCoins = await this.getServerCoinsForStore(peerIp);
94
+ serverCoins = serverCoins.filter((coin) => coin.coin.parentCoinInfo !== serverCoin.parentCoinInfo);
95
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
96
+ }
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 = []) {
104
+ const epochBasedHint = (0, datalayer_driver_1.morphLauncherId)(Buffer.from(this.storeId, "hex"), BigInt(epoch));
105
+ const peer = await FullNodePeer_1.FullNodePeer.connect();
106
+ const maxClvmCost = BigInt(11000000000);
107
+ const hintedCoinStates = await peer.getHintedCoinStates(epochBasedHint, false);
108
+ const filteredCoinStates = hintedCoinStates.filter((coinState) => coinState.coin.amount >= serverCoinCollateral);
109
+ // Use a Set to ensure uniqueness
110
+ const serverCoinPeers = new Set();
111
+ for (const coinState of filteredCoinStates) {
112
+ const serverCoin = await peer.fetchServerCoin(coinState, maxClvmCost);
113
+ const peerUrl = serverCoin.memoUrls[0];
114
+ if (!blacklist.includes(peerUrl)) {
115
+ serverCoinPeers.add(peerUrl);
116
+ }
117
+ }
118
+ if (process.env.DIG_DEBUG === "1") {
119
+ console.log("Server Coin Peers: ", serverCoinPeers);
120
+ }
121
+ // Convert the Set back to an array if needed
122
+ return lodash_1.default.sampleSize(Array.from(serverCoinPeers), sampleSize);
123
+ }
124
+ // Get the current epoch based on the current timestamp
125
+ static getCurrentEpoch() {
126
+ return ServerCoin.calculateEpoch(new Date());
127
+ }
128
+ // Ensure server coin exists for the current epoch
129
+ async ensureServerCoinExists(peerIp) {
130
+ try {
131
+ console.log(`Ensuring server coin exists for store ${this.storeId}...`);
132
+ const currentEpoch = ServerCoin.getCurrentEpoch();
133
+ const serverCoins = await this.getServerCoinsForStore(peerIp);
134
+ // Check if a server coin already exists for the current epoch
135
+ const existingCoin = serverCoins.find((coin) => coin.epoch === currentEpoch);
136
+ if (existingCoin) {
137
+ return;
138
+ }
139
+ console.log(`No server coin found for epoch ${currentEpoch}. Creating new server coin...`);
140
+ const serverCoin = await this.createForEpoch(peerIp);
141
+ const newServerCoinData = {
142
+ coin: {
143
+ amount: serverCoin.coin.amount.toString(),
144
+ puzzleHash: serverCoin.coin.puzzleHash.toString("hex"),
145
+ parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"),
146
+ },
147
+ createdAt: new Date().toISOString(),
148
+ epoch: currentEpoch,
149
+ };
150
+ await FullNodePeer_1.FullNodePeer.waitForConfirmation(serverCoin.coin.parentCoinInfo);
151
+ serverCoins.push(newServerCoinData);
152
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
153
+ console.log(`Server coin created and saved for store ${this.storeId}. Epoch: ${currentEpoch}`);
154
+ }
155
+ catch (error) {
156
+ console.error(`Error ensuring server coin: ${error.message}`);
157
+ throw error;
158
+ }
159
+ }
160
+ // Melt outdated server coins
161
+ async meltOutdatedEpochs(peerIp) {
162
+ try {
163
+ const currentEpoch = ServerCoin.getCurrentEpoch();
164
+ let serverCoins = await this.getServerCoinsForStore(peerIp);
165
+ // Filter out coins that are not in the current epoch
166
+ const outdatedCoins = serverCoins.filter((coin) => coin.epoch < currentEpoch);
167
+ for (const serverCoin of outdatedCoins) {
168
+ await this.melt(serverCoin.epoch, peerIp);
169
+ // Remove the processed coin from the serverCoins array
170
+ serverCoins = serverCoins.filter((coin) => coin.epoch !== serverCoin.epoch);
171
+ // Update the nconf file with the updated array
172
+ await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
173
+ }
174
+ }
175
+ catch (error) {
176
+ console.error(`Error processing outdated epochs: ${error.message}`);
177
+ throw error;
178
+ }
179
+ }
180
+ // If a store is unsubscribed this will melt all of its untracked coins and recover the locked amount
181
+ static async meltUntrackedStoreCoins() {
182
+ // Get all the store coins from the server config
183
+ const allServerCoins = await ServerCoin.serverCoinManager.getFullConfig();
184
+ // Get all the store ids from the server config
185
+ const storeIdsWithCoins = Object.keys(allServerCoins);
186
+ // Get all the subscribed stores and map them to their StoreId
187
+ const allSubscribedStores = DataStore_1.DataStore.getAllStores();
188
+ const allSubscribedStoreIds = allSubscribedStores.map((store) => store.StoreId);
189
+ // Iterate over each store in the server coins
190
+ for (const storeCoin of storeIdsWithCoins) {
191
+ const serverCoin = new ServerCoin(storeCoin);
192
+ // If the store is no longer subscribed
193
+ if (!allSubscribedStoreIds.includes(storeCoin)) {
194
+ // Get all the IPs that have coins for this untracked store
195
+ const ips = Object.keys(allServerCoins[storeCoin]);
196
+ // Iterate over each IP (e.g., "71.121.246.129")
197
+ for (const ip of ips) {
198
+ // Get the array of coins for that IP
199
+ let coins = allServerCoins[storeCoin][ip];
200
+ // Iterate over each coin and melt it
201
+ for (const coinInfo of coins) {
202
+ const { epoch, coin } = coinInfo;
203
+ // Melt the coin
204
+ await serverCoin.melt(epoch, ip);
205
+ // Remove the melted coin from the array
206
+ coins = coins.filter((c) => c !== coinInfo);
207
+ // Update the config to reflect the remaining coins for this IP
208
+ await ServerCoin.serverCoinManager.setConfigValue(`${storeCoin}:${ip}`, coins);
209
+ }
210
+ // If no coins are left for this IP, optionally remove the entire IP entry
211
+ if (coins.length === 0) {
212
+ delete allServerCoins[storeCoin][ip];
213
+ await ServerCoin.serverCoinManager.setConfigValue(`${storeCoin}:${ip}`, undefined);
214
+ }
215
+ }
216
+ // If no IPs are left for this store, optionally remove the store entry
217
+ if (Object.keys(allServerCoins[storeCoin]).length === 0) {
218
+ delete allServerCoins[storeCoin];
219
+ await ServerCoin.serverCoinManager.setConfigValue(storeCoin, undefined);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ // Retrieve server coins from the store
225
+ async getServerCoinsForStore(peerIp) {
226
+ const serverCoins = (await ServerCoin.serverCoinManager.getConfigValue(`${this.storeId}:${peerIp}`)) || [];
227
+ return serverCoins;
228
+ }
229
+ // Check if the server coin has been created for the current epoch
230
+ async hasEpochCoinBeenCreated(currentEpoch, peerIp) {
231
+ try {
232
+ const serverCoins = await this.getServerCoinsForStore(peerIp);
233
+ const existingCoin = serverCoins.find((coin) => coin.epoch === currentEpoch);
234
+ if (!existingCoin) {
235
+ console.log(`No server coin found for epoch ${currentEpoch}.`);
236
+ return false;
237
+ }
238
+ console.log(`Server coin found for epoch ${currentEpoch}.`);
239
+ return true;
240
+ }
241
+ catch (error) {
242
+ console.error(`Error checking for existing server coin: ${error.message}`);
243
+ return false;
244
+ }
245
+ }
246
+ // Static method to calculate the current epoch
247
+ static calculateEpoch(currentTimestampUTC) {
248
+ const firstEpochStart = new Date(Date.UTC(2024, 8, 3, 0, 0)); // Sept 3, 2024, 00:00 UTC
249
+ // Convert the current timestamp to milliseconds
250
+ const currentTimestampMillis = currentTimestampUTC.getTime();
251
+ // Calculate the number of milliseconds in one epoch (7 days)
252
+ const millisecondsInEpoch = 7 * 24 * 60 * 60 * 1000;
253
+ // Calculate the difference in milliseconds between the current timestamp and the first epoch start
254
+ const differenceMillis = currentTimestampMillis - firstEpochStart.getTime();
255
+ // Calculate the current epoch number
256
+ const epochNumber = Math.floor(differenceMillis / millisecondsInEpoch) + 1;
257
+ return epochNumber;
258
+ }
259
+ }
260
+ exports.ServerCoin = ServerCoin;
261
+ ServerCoin.serverCoinManager = new NconfManager_1.NconfManager("server_coin.json");
@@ -0,0 +1,28 @@
1
+ import { Buffer } from "buffer";
2
+ import { Peer, Coin, CoinSpend } from "datalayer-driver";
3
+ export declare const DEFAULT_FEE_COIN_COST = 64000000;
4
+ export declare class Wallet {
5
+ private mnemonic;
6
+ private walletName;
7
+ private constructor();
8
+ static load(walletName?: string, createOnUndefined?: boolean): Promise<Wallet>;
9
+ getMnemonic(): string;
10
+ static createNewWallet(walletName: string): Promise<string>;
11
+ static importWallet(walletName: string, seed?: string): Promise<string>;
12
+ static importWalletFromChia(walletName: string): Promise<string>;
13
+ getMasterSecretKey(): Promise<Buffer>;
14
+ getPublicSyntheticKey(): Promise<Buffer>;
15
+ getPrivateSyntheticKey(): Promise<Buffer>;
16
+ getOwnerPuzzleHash(): Promise<Buffer>;
17
+ getOwnerPublicKey(): Promise<string>;
18
+ static deleteWallet(walletName: string): Promise<boolean>;
19
+ static listWallets(): Promise<string[]>;
20
+ private static getWalletFromKeyring;
21
+ private static saveWalletToKeyring;
22
+ createKeyOwnershipSignature(nonce: string): Promise<string>;
23
+ verifyKeyOwnershipSignature(nonce: string, signature: string, publicKey: string): Promise<boolean>;
24
+ selectUnspentCoins(peer: Peer, coinAmount: bigint, feeBigInt: bigint, omitCoins?: Coin[]): Promise<Coin[]>;
25
+ static calculateFeeForCoinSpends(peer: Peer, coinSpends: CoinSpend[] | null): Promise<bigint>;
26
+ static isCoinSpendable(peer: Peer, coinId: Buffer): Promise<boolean>;
27
+ }
28
+ //# sourceMappingURL=Wallet.d.ts.map
@@ -0,0 +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"}