@dignetwork/dig-sdk 0.0.1-alpha.4 → 0.0.1-alpha.42

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 (47) hide show
  1. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +160 -0
  2. package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -0
  3. package/dist/DataIntegrityTree/DataIntegrityTree.js +672 -0
  4. package/dist/DataIntegrityTree/DataLayerError.d.ts +6 -0
  5. package/dist/DataIntegrityTree/DataLayerError.d.ts.map +1 -0
  6. package/dist/DataIntegrityTree/DataLayerError.js +17 -0
  7. package/dist/DataIntegrityTree/index.d.ts +2 -0
  8. package/dist/DataIntegrityTree/index.d.ts.map +1 -0
  9. package/dist/DataIntegrityTree/index.js +17 -0
  10. package/dist/DigNetwork/ContentServer.d.ts +5 -1
  11. package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
  12. package/dist/DigNetwork/ContentServer.js +28 -13
  13. package/dist/DigNetwork/DigNetwork.d.ts +1 -0
  14. package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
  15. package/dist/DigNetwork/DigNetwork.js +79 -50
  16. package/dist/DigNetwork/DigPeer.d.ts +5 -1
  17. package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
  18. package/dist/DigNetwork/DigPeer.js +51 -16
  19. package/dist/DigNetwork/PropagationServer.d.ts +7 -1
  20. package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
  21. package/dist/DigNetwork/PropagationServer.js +100 -68
  22. package/dist/blockchain/DataStore.d.ts +2 -2
  23. package/dist/blockchain/DataStore.d.ts.map +1 -1
  24. package/dist/blockchain/DataStore.js +9 -5
  25. package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
  26. package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
  27. package/dist/blockchain/FullNodePeer.d.ts +8 -1
  28. package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
  29. package/dist/blockchain/FullNodePeer.js +39 -7
  30. package/dist/blockchain/ServerCoin.d.ts +11 -3
  31. package/dist/blockchain/ServerCoin.d.ts.map +1 -1
  32. package/dist/blockchain/ServerCoin.js +30 -17
  33. package/dist/blockchain/Wallet.d.ts +1 -1
  34. package/dist/blockchain/Wallet.d.ts.map +1 -1
  35. package/dist/blockchain/Wallet.js +1 -1
  36. package/dist/blockchain/coins.d.ts +1 -1
  37. package/dist/blockchain/coins.d.ts.map +1 -1
  38. package/dist/blockchain/coins.js +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/utils/config.d.ts.map +1 -1
  43. package/dist/utils/config.js +4 -9
  44. package/dist/utils/directoryUtils.d.ts +1 -7
  45. package/dist/utils/directoryUtils.d.ts.map +1 -1
  46. package/dist/utils/directoryUtils.js +30 -11
  47. package/package.json +3 -3
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class DataLayerError extends Error {
4
+ constructor(code, message) {
5
+ // Call the parent constructor with the message
6
+ super(message);
7
+ // Set the name of the error to the class name
8
+ this.name = this.constructor.name;
9
+ // Assign the custom code
10
+ this.code = code;
11
+ // Capture the stack trace (if available in the environment)
12
+ if (Error.captureStackTrace) {
13
+ Error.captureStackTrace(this, this.constructor);
14
+ }
15
+ }
16
+ }
17
+ exports.default = DataLayerError;
@@ -0,0 +1,2 @@
1
+ export * from './DataIntegrityTree';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,17 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./DataIntegrityTree"), exports);
@@ -3,6 +3,8 @@ import { Readable } from "stream";
3
3
  export declare class ContentServer {
4
4
  private ipAddress;
5
5
  private storeId;
6
+ private static certPath;
7
+ private static keyPath;
6
8
  private static readonly port;
7
9
  constructor(ipAddress: string, storeId: string);
8
10
  getKey(key: string, rootHash: string, challengeHex?: string): Promise<string>;
@@ -15,7 +17,9 @@ export declare class ContentServer {
15
17
  success: boolean;
16
18
  headers?: http.IncomingHttpHeaders;
17
19
  }>;
18
- headStore(): Promise<{
20
+ headStore(options?: {
21
+ hasRootHash: string;
22
+ }): Promise<{
19
23
  success: boolean;
20
24
  headers?: http.IncomingHttpHeaders;
21
25
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGlC,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAM;gBAEtB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAMjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa7E,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAepC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,OAAO,CAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;KAAE,CAAC;IAMvD,SAAS,IAAI,OAAO,CAAC;QAChC,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;KACpC,CAAC;IAMK,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YA0ClC,IAAI;YAuEJ,SAAS;YAMT,gBAAgB;YAiChB,KAAK;CAuDpB"}
1
+ {"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGlC,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;gBAExB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa7E,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAepC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,OAAO,CAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;KAAE,CAAC;IAMvD,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC;QAChE,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;KACpC,CAAC;IAUK,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YA0ClC,IAAI;YA0EJ,SAAS;YAMT,gBAAgB;YAiChB,KAAK;CA0DpB"}
@@ -4,17 +4,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ContentServer = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
7
8
  const http_1 = __importDefault(require("http"));
8
9
  const url_1 = require("url");
10
+ const ssl_1 = require("../utils/ssl");
9
11
  class ContentServer {
10
12
  constructor(ipAddress, storeId) {
11
13
  this.ipAddress = ipAddress;
12
14
  this.storeId = storeId;
15
+ if (!ContentServer.certPath || !ContentServer.keyPath) {
16
+ const { certPath, keyPath } = (0, ssl_1.getOrCreateSSLCerts)();
17
+ ContentServer.certPath = certPath;
18
+ ContentServer.keyPath = keyPath;
19
+ }
13
20
  }
14
21
  // Method to get the content of a specified key from the peer, with optional challenge query
15
22
  async getKey(key, rootHash, challengeHex) {
16
23
  // Construct the base URL
17
- let url = `http://${this.ipAddress}/chia.${this.storeId}.${rootHash}/${key}`;
24
+ let url = `https://${this.ipAddress}/chia.${this.storeId}.${rootHash}/${key}`;
18
25
  // If a challenge is provided, append it as a query parameter
19
26
  if (challengeHex) {
20
27
  url += `?challenge=${challengeHex}`;
@@ -35,38 +42,40 @@ class ContentServer {
35
42
  }
36
43
  // Method to get the .well-known information
37
44
  async getWellKnown() {
38
- const url = `http://${this.ipAddress}/.well-known`;
45
+ const url = `https://${this.ipAddress}/.well-known`;
39
46
  return this.fetchJson(url);
40
47
  }
41
48
  // Method to get the list of known stores
42
49
  async getKnownStores() {
43
- const url = `http://${this.ipAddress}/.well-known/stores`;
50
+ const url = `https://${this.ipAddress}/.well-known/stores`;
44
51
  return this.fetchJson(url);
45
52
  }
46
53
  // Method to get the index of all stores
47
54
  async getStoresIndex() {
48
- const url = `http://${this.ipAddress}/`;
55
+ const url = `https://${this.ipAddress}/`;
49
56
  return this.fetchJson(url);
50
57
  }
51
58
  // Method to get the index of keys in a store
52
59
  async getKeysIndex() {
53
- const url = `http://${this.ipAddress}/${this.storeId}`;
60
+ const url = `https://${this.ipAddress}/${this.storeId}`;
54
61
  return this.fetchJson(url);
55
62
  }
56
63
  // Method to check if a specific key exists (HEAD request)
57
64
  async headKey(key) {
58
- const url = `http://${this.ipAddress}/${this.storeId}/${key}`;
59
- return this.head(url); // Return the object from head method
65
+ const url = `https://${this.ipAddress}/${this.storeId}/${key}`;
66
+ return this.head(url);
60
67
  }
61
68
  // Method to check if a specific store exists (HEAD request)
62
- async headStore() {
63
- const url = `http://${this.ipAddress}/${this.storeId}`;
64
- console.log({ url });
65
- return this.head(url); // Return the object from head method
69
+ async headStore(options) {
70
+ let url = `https://${this.ipAddress}/${this.storeId}`;
71
+ if (options?.hasRootHash) {
72
+ url += `?hasRootHash=${options.hasRootHash}`;
73
+ }
74
+ return this.head(url);
66
75
  }
67
76
  streamKey(key) {
68
77
  return new Promise((resolve, reject) => {
69
- const url = `http://${this.ipAddress}/${this.storeId}/${key}`;
78
+ const url = `https://${this.ipAddress}/${this.storeId}/${key}`;
70
79
  const urlObj = new url_1.URL(url);
71
80
  const requestOptions = {
72
81
  hostname: urlObj.hostname,
@@ -111,6 +120,9 @@ class ContentServer {
111
120
  (urlObj.protocol === "http:" ? 80 : ContentServer.port),
112
121
  path: urlObj.pathname + urlObj.search,
113
122
  method: "HEAD",
123
+ key: fs_1.default.readFileSync(ContentServer.keyPath),
124
+ cert: fs_1.default.readFileSync(ContentServer.certPath),
125
+ rejectUnauthorized: false,
114
126
  };
115
127
  const request = http_1.default.request(requestOptions, (response) => {
116
128
  const { statusCode, headers } = response;
@@ -201,6 +213,9 @@ class ContentServer {
201
213
  port: urlObj.port || ContentServer.port,
202
214
  path: urlObj.pathname + urlObj.search, // Include query params
203
215
  method: "GET",
216
+ key: fs_1.default.readFileSync(ContentServer.keyPath),
217
+ cert: fs_1.default.readFileSync(ContentServer.certPath),
218
+ rejectUnauthorized: false,
204
219
  };
205
220
  const request = http_1.default.request(requestOptions, (response) => {
206
221
  let data = "";
@@ -240,4 +255,4 @@ class ContentServer {
240
255
  }
241
256
  }
242
257
  exports.ContentServer = ContentServer;
243
- ContentServer.port = 80;
258
+ ContentServer.port = 4161;
@@ -9,6 +9,7 @@ export declare class DigNetwork {
9
9
  uploadStoreHead(digPeer: DigPeer): Promise<void>;
10
10
  uploadStore(digPeer: DigPeer): Promise<void>;
11
11
  static subscribeToStore(storeId: string): Promise<void>;
12
+ static findPeerWithStoreKey(storeId: string, rootHash: string, key?: string, intialBlackList?: string[]): Promise<string | null>;
12
13
  static unsubscribeFromStore(storeId: string): void;
13
14
  downloadFiles(forceDownload?: boolean, renderProgressBar?: boolean, skipData?: boolean): Promise<void>;
14
15
  private fetchAvailablePeers;
@@ -1 +1 @@
1
- {"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;WA+BrC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQtD,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ5C,aAAa,CACxB,aAAa,GAAE,OAAe,EAC9B,iBAAiB,GAAE,OAAc,EACjC,QAAQ,GAAE,OAAe,GACxB,OAAO,CAAC,IAAI,CAAC;YAqFF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YAuErB,cAAc;CAuB7B"}
1
+ {"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;WA6BrC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQhD,oBAAoB,CACtC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,EACZ,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;WAuCX,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ5C,aAAa,CACxB,aAAa,GAAE,OAAe,EAC9B,iBAAiB,GAAE,OAAc,EACjC,QAAQ,GAAE,OAAe,GACxB,OAAO,CAAC,IAAI,CAAC;YAiFF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YA2ErB,cAAc;CAoB7B"}
@@ -32,6 +32,9 @@ const deltaUtils_1 = require("../utils/deltaUtils");
32
32
  const hashUtils_1 = require("../utils/hashUtils");
33
33
  const blockchain_1 = require("../blockchain");
34
34
  const config_1 = require("../utils/config");
35
+ const util_1 = require("util");
36
+ const rename = (0, util_1.promisify)(fs.rename);
37
+ const unlink = (0, util_1.promisify)(fs.unlink);
35
38
  class DigNetwork {
36
39
  constructor(storeId) {
37
40
  this.dataStore = blockchain_1.DataStore.from(storeId);
@@ -101,7 +104,6 @@ class DigNetwork {
101
104
  async uploadStore(digPeer) {
102
105
  const { generationIndex } = await this.uploadPreflight(digPeer);
103
106
  const filesToUpload = await (0, deltaUtils_1.getDeltaFiles)(this.dataStore.StoreId, generationIndex, path.resolve(config_1.DIG_FOLDER_PATH, "stores"));
104
- console.log(filesToUpload);
105
107
  if (!filesToUpload.length) {
106
108
  console.log("No files to upload.");
107
109
  return;
@@ -123,6 +125,36 @@ class DigNetwork {
123
125
  const digNetwork = new DigNetwork(storeId);
124
126
  await digNetwork.downloadFiles(true);
125
127
  }
128
+ static async findPeerWithStoreKey(storeId, rootHash, key, intialBlackList = []) {
129
+ const peerBlackList = intialBlackList;
130
+ const serverCoin = new blockchain_1.ServerCoin(storeId);
131
+ while (true) {
132
+ try {
133
+ const digPeers = await serverCoin.sampleCurrentEpoch(1, peerBlackList);
134
+ if (digPeers.length === 0)
135
+ break;
136
+ const peerIp = digPeers[0];
137
+ const digPeer = new DigPeer_1.DigPeer(peerIp, storeId);
138
+ const storeResponse = await digPeer.propagationServer.headStore({
139
+ hasRootHash: rootHash,
140
+ });
141
+ console.log(peerIp, storeResponse.headers);
142
+ if (storeResponse.headers?.["x-has-roothash"] === "true") {
143
+ console.log(`Found Peer at ${peerIp} for storeId: ${storeId}, root hash ${rootHash}`);
144
+ if (!key) {
145
+ return peerIp;
146
+ }
147
+ const keyResponse = await digPeer.contentServer.headKey(key);
148
+ if (keyResponse.headers?.["x-key-exists"] === "true") {
149
+ return peerIp;
150
+ }
151
+ }
152
+ peerBlackList.push(peerIp);
153
+ }
154
+ catch { }
155
+ }
156
+ return null;
157
+ }
126
158
  static unsubscribeFromStore(storeId) {
127
159
  fs.rmdirSync(path.join(config_1.DIG_FOLDER_PATH, "stores", storeId), {
128
160
  recursive: true,
@@ -133,47 +165,40 @@ class DigNetwork {
133
165
  async downloadFiles(forceDownload = false, renderProgressBar = true, skipData = false) {
134
166
  try {
135
167
  const rootHistory = await this.dataStore.getRootHistory();
136
- if (!rootHistory.length)
168
+ if (!rootHistory.length) {
137
169
  throw new Error("No roots found in rootHistory. Cannot proceed with file download.");
138
- await this.downloadHeightFile(forceDownload);
139
- const localManifestPath = path.join(this.storeDir, "manifest.dat");
140
- const localManifestHashes = fs.existsSync(localManifestPath)
141
- ? fs.readFileSync(localManifestPath, "utf-8").trim().split("\n")
142
- : [];
143
- const progressBar = renderProgressBar
144
- ? new cli_progress_1.MultiBar({
145
- clearOnComplete: false,
146
- hideCursor: true,
147
- format: "Syncing Store | {bar} | {percentage}%",
148
- noTTYOutput: true,
149
- }, cli_progress_1.Presets.shades_classic)
150
- : null;
151
- const progress = progressBar
152
- ? progressBar.create(rootHistory.length, 0)
153
- : null;
154
- const newRootHashes = [];
155
- for (let i = 0; i < rootHistory.length; i++) {
156
- const { root_hash: rootHash } = rootHistory[i];
157
- const datFilePath = path.join(this.storeDir, `${rootHash}.dat`);
158
- await this.downloadFileFromPeers(`${rootHash}.dat`, datFilePath, forceDownload);
159
- const datFileContent = JSON.parse(fs.readFileSync(datFilePath, "utf-8"));
160
- if (datFileContent.root !== rootHash)
161
- throw new Error("Root hash mismatch");
170
+ }
171
+ await this.downloadHeightFile(true);
172
+ const rootHistorySorted = rootHistory
173
+ .filter((item) => item.timestamp !== undefined)
174
+ .sort((a, b) => b.timestamp - a.timestamp);
175
+ // Process rootHistory sequentially
176
+ for (const rootInfo of rootHistorySorted) {
177
+ const peerIp = await DigNetwork.findPeerWithStoreKey(this.dataStore.StoreId, rootInfo.root_hash);
178
+ if (fs.existsSync(`${this.storeDir}/${rootInfo.root_hash}.dat`)) {
179
+ console.log(`Root hash ${rootInfo.root_hash} already exists locally. Skipping download.`);
180
+ continue; // Skip to the next rootInfo
181
+ }
182
+ if (!peerIp) {
183
+ console.error(`No peer found with root hash ${rootInfo.root_hash}. Skipping download.`);
184
+ continue; // Skip to the next rootInfo
185
+ }
186
+ const digPeer = new DigPeer_1.DigPeer(peerIp, this.dataStore.StoreId);
187
+ const rootResponse = await digPeer.propagationServer.getStoreData(`${rootInfo.root_hash}.dat`);
188
+ fs.writeFileSync(`${this.storeDir}/${rootInfo.root_hash}.dat`, rootResponse);
189
+ const root = JSON.parse(rootResponse);
162
190
  if (!skipData) {
163
- for (const file of Object.keys(datFileContent.files)) {
164
- const filePath = (0, hashUtils_1.getFilePathFromSha256)(datFileContent.files[file].sha256, path.join(this.storeDir, "data"));
165
- const isInDataDir = filePath.startsWith(path.join(this.storeDir, "data"));
166
- await this.downloadFileFromPeers((0, hashUtils_1.getFilePathFromSha256)(datFileContent.files[file].sha256, "data"), filePath, forceDownload || !isInDataDir);
167
- }
191
+ // Use Object.entries() to iterate over the map (root.files)
192
+ await Promise.all(Object.entries(root.files).map(async ([storeKey, file]) => {
193
+ const filePath = (0, hashUtils_1.getFilePathFromSha256)(file.sha256, `${this.storeDir}/data`);
194
+ if (!fs.existsSync(filePath) || forceDownload) {
195
+ console.log(`Downloading file with sha256: ${file.sha256}...`);
196
+ await this.downloadFileFromPeers(`data/${file.sha256.match(/.{1,2}/g).join("/")}`, filePath, forceDownload);
197
+ }
198
+ }));
168
199
  }
169
- if (localManifestHashes[i] !== rootHash)
170
- newRootHashes.push(rootHash);
171
- progress?.increment();
172
200
  }
173
- if (newRootHashes.length)
174
- fs.appendFileSync(localManifestPath, newRootHashes.join("\n") + "\n");
175
- await this.downloadManifestFile(forceDownload);
176
- progressBar?.stop();
201
+ await this.downloadManifestFile(true);
177
202
  console.log("Syncing store complete.");
178
203
  }
179
204
  catch (error) {
@@ -198,6 +223,7 @@ class DigNetwork {
198
223
  }
199
224
  async downloadFileFromPeers(dataPath, filePath, overwrite) {
200
225
  let digPeers = await this.fetchAvailablePeers();
226
+ const tempFilePath = `${filePath}.tmp`;
201
227
  while (true) {
202
228
  if (!overwrite && fs.existsSync(filePath))
203
229
  return;
@@ -207,22 +233,24 @@ class DigNetwork {
207
233
  continue;
208
234
  try {
209
235
  // Create directory if it doesn't exist
210
- const directory = path.dirname(filePath);
236
+ const directory = path.dirname(tempFilePath);
211
237
  if (!fs.existsSync(directory)) {
212
238
  fs.mkdirSync(directory, { recursive: true });
213
239
  }
214
- // Stream the file data directly to the file system
215
- const fileStream = fs.createWriteStream(filePath);
240
+ // Stream the file data to a temporary file
241
+ const fileStream = fs.createWriteStream(tempFilePath);
216
242
  // Start streaming the data from the peer
217
243
  const peerStream = await digPeer.propagationServer.streamStoreData(dataPath);
218
- // Pipe the peer stream directly to the file system
244
+ // Pipe the peer stream to the temp file
219
245
  await new Promise((resolve, reject) => {
220
246
  peerStream.pipe(fileStream);
221
- peerStream.on("end", resolve);
222
- peerStream.on("error", reject);
223
- fileStream.on("error", reject);
247
+ peerStream.on('end', resolve);
248
+ peerStream.on('error', reject);
249
+ fileStream.on('error', reject);
224
250
  });
225
- if (process.env.DIG_DEBUG === "1") {
251
+ // Rename the temp file to the final file path after successful download
252
+ await rename(tempFilePath, filePath);
253
+ if (process.env.DIG_DEBUG === '1') {
226
254
  console.log(`Downloaded ${dataPath} from ${digPeer.IpAddress}`);
227
255
  }
228
256
  return; // Exit the method if download succeeds
@@ -230,11 +258,15 @@ class DigNetwork {
230
258
  catch (error) {
231
259
  console.warn(`Failed to download ${dataPath} from ${digPeer.IpAddress}, blacklisting peer and trying next...`);
232
260
  blacklist.add(digPeer.IpAddress);
261
+ // Clean up the temp file in case of failure
262
+ if (fs.existsSync(tempFilePath)) {
263
+ await unlink(tempFilePath);
264
+ }
233
265
  }
234
266
  }
235
267
  this.peerBlacklist.set(dataPath, blacklist);
236
268
  if (blacklist.size >= digPeers.length) {
237
- if (process.env.DIG_DEBUG === "1") {
269
+ if (process.env.DIG_DEBUG === '1') {
238
270
  console.warn(`All peers blacklisted for ${dataPath}. Refreshing peers...`);
239
271
  }
240
272
  digPeers = await this.fetchAvailablePeers();
@@ -246,8 +278,6 @@ class DigNetwork {
246
278
  }
247
279
  async runProgressBar(total, name, task) {
248
280
  // Using 'any' to work around TypeScript issues
249
- const oldConsoleLog = console.log;
250
- console.log = () => { }; // Suppress console.log output
251
281
  const multiBar = new cli_progress_1.MultiBar({
252
282
  clearOnComplete: false,
253
283
  hideCursor: true,
@@ -257,7 +287,6 @@ class DigNetwork {
257
287
  const progress = multiBar.create(total, 0, { name });
258
288
  await task(progress).finally(() => {
259
289
  multiBar.stop();
260
- console.log = oldConsoleLog; // Restore console.log
261
290
  });
262
291
  }
263
292
  }
@@ -1,6 +1,7 @@
1
1
  import { ContentServer } from "./ContentServer";
2
2
  import { PropagationServer } from "./PropagationServer";
3
3
  import { IncentiveServer } from "./IncentiveServer";
4
+ import { Output } from "@dignetwork/datalayer-driver";
4
5
  export declare class DigPeer {
5
6
  private ipAddress;
6
7
  private storeId;
@@ -14,6 +15,9 @@ export declare class DigPeer {
14
15
  get IpAddress(): string;
15
16
  validateStore(rootHash: string, keys: string[]): Promise<boolean>;
16
17
  isSynced(): Promise<boolean>;
17
- sendPayment(walletName: string, amount: bigint): Promise<void>;
18
+ static sendEqualBulkPayments(walletName: string, addresses: string[], totalAmount: bigint, memos: Buffer[]): Promise<void>;
19
+ static sendBulkPayments(walletName: string, outputs: Output[]): Promise<void>;
20
+ sendPayment(walletName: string, amount: bigint, memos?: Buffer[]): Promise<void>;
21
+ static createPaymentHint(storeId: Buffer): Buffer;
18
22
  }
19
23
  //# sourceMappingURL=DigPeer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DigPeer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigPeer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAcpD,qBAAa,OAAO;IAClB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,kBAAkB,CAAoB;IAC9C,OAAO,CAAC,gBAAgB,CAAkB;gBAE9B,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAS9C,IAAW,aAAa,IAAI,aAAa,CAExC;IAGD,IAAW,iBAAiB,IAAI,iBAAiB,CAEhD;IAGD,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,MAAM,CAE7B;IAEY,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,OAAO,CAAC;IA8GN,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAyB5B,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAmC5E"}
1
+ {"version":3,"file":"DigPeer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigPeer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAKL,MAAM,EACP,MAAM,8BAA8B,CAAC;AAKtC,qBAAa,OAAO;IAClB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,kBAAkB,CAAoB;IAC9C,OAAO,CAAC,gBAAgB,CAAkB;gBAE9B,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAS9C,IAAW,aAAa,IAAI,aAAa,CAExC;IAGD,IAAW,iBAAiB,IAAI,iBAAiB,CAEhD;IAGD,IAAW,eAAe,IAAI,eAAe,CAE5C;IAED,IAAW,SAAS,IAAI,MAAM,CAE7B;IAEY,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,OAAO,CAAC;IAgHN,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;WA2B3B,qBAAqB,CACjC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC;WAuBI,gBAAgB,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC;IAwCH,WAAW,CACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAM,EAAO,GACnB,OAAO,CAAC,IAAI,CAAC;WAYF,iBAAiB,CAAC,OAAO,EAAE,MAAM;CAoBhD"}
@@ -9,8 +9,8 @@ const ContentServer_1 = require("./ContentServer");
9
9
  const PropagationServer_1 = require("./PropagationServer");
10
10
  const IncentiveServer_1 = require("./IncentiveServer");
11
11
  const blockchain_1 = require("../blockchain");
12
- const data_integrity_tree_1 = require("@dignetwork/data-integrity-tree");
13
- const datalayer_driver_1 = require("datalayer-driver");
12
+ const DataIntegrityTree_1 = require("../DataIntegrityTree");
13
+ const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
14
14
  const blockchain_2 = require("../blockchain");
15
15
  const blockchain_3 = require("../blockchain");
16
16
  const coins_1 = require("../blockchain/coins");
@@ -97,7 +97,7 @@ class DigPeer {
97
97
  });
98
98
  });
99
99
  // Perform tree integrity validation using the datFileContent and the root hash
100
- const treeCheck = data_integrity_tree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(key, fileData.sha256, datFileContent, rootHash);
100
+ const treeCheck = DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(key, fileData.sha256, datFileContent, rootHash);
101
101
  if (!treeCheck) {
102
102
  console.error(`Tree validation failed for file ${key}.`);
103
103
  filesIntegrityIntact = false;
@@ -137,22 +137,31 @@ class DigPeer {
137
137
  return false;
138
138
  }
139
139
  }
140
- async sendPayment(walletName, amount) {
141
- const paymentAddress = await this.contentServer.getPaymentAddress();
142
- console.log(`Sending ${amount} Mojos to ${paymentAddress}...`);
143
- const fee = BigInt(1000);
140
+ static sendEqualBulkPayments(walletName, addresses, totalAmount, memos) {
141
+ // Use a Set to ensure unique addresses
142
+ const uniqueAddresses = Array.from(new Set(addresses));
143
+ // Convert unique addresses to puzzle hashes
144
+ const puzzleHashes = uniqueAddresses.map((address) => (0, datalayer_driver_1.addressToPuzzleHash)(address));
145
+ // Calculate amount per puzzle hash
146
+ const amountPerPuzzleHash = totalAmount / BigInt(puzzleHashes.length);
147
+ // Create outputs array
148
+ const outputs = puzzleHashes.map((puzzleHash) => ({
149
+ puzzleHash,
150
+ amount: amountPerPuzzleHash,
151
+ memos,
152
+ }));
153
+ // Call the sendBulkPayments function with the generated outputs
154
+ return DigPeer.sendBulkPayments(walletName, outputs);
155
+ }
156
+ static async sendBulkPayments(walletName, outputs) {
157
+ const feePerCondition = BigInt(1000);
158
+ const totalFee = feePerCondition * BigInt(outputs.length);
144
159
  const wallet = await blockchain_3.Wallet.load(walletName);
145
160
  const publicSyntheticKey = await wallet.getPublicSyntheticKey();
146
161
  const peer = await blockchain_2.FullNodePeer.connect();
147
- const coins = await (0, coins_1.selectUnspentCoins)(peer, amount, fee, [], walletName);
148
- const paymentAddressPuzzleHash = (0, datalayer_driver_1.addressToPuzzleHash)(paymentAddress);
149
- const outputs = [
150
- {
151
- puzzleHash: paymentAddressPuzzleHash,
152
- amount: amount,
153
- },
154
- ];
155
- const coinSpends = await (0, datalayer_driver_1.sendXch)(publicSyntheticKey, coins, outputs, fee);
162
+ const totalAmount = outputs.reduce((acc, output) => acc + output.amount, BigInt(0));
163
+ const coins = await (0, coins_1.selectUnspentCoins)(peer, totalAmount, totalFee, [], walletName);
164
+ const coinSpends = await (0, datalayer_driver_1.sendXch)(publicSyntheticKey, coins, outputs, totalFee);
156
165
  const sig = (0, datalayer_driver_1.signCoinSpends)(coinSpends, [await wallet.getPrivateSyntheticKey()], false);
157
166
  const err = await peer.broadcastSpend(coinSpends, [sig]);
158
167
  if (err) {
@@ -160,5 +169,31 @@ class DigPeer {
160
169
  }
161
170
  await blockchain_2.FullNodePeer.waitForConfirmation((0, datalayer_driver_1.getCoinId)(coins[0]));
162
171
  }
172
+ async sendPayment(walletName, amount, memos = []) {
173
+ const paymentAddress = await this.contentServer.getPaymentAddress();
174
+ const paymentAddressPuzzleHash = (0, datalayer_driver_1.addressToPuzzleHash)(paymentAddress);
175
+ const output = {
176
+ puzzleHash: paymentAddressPuzzleHash,
177
+ amount,
178
+ memos,
179
+ };
180
+ return DigPeer.sendBulkPayments(walletName, [output]);
181
+ }
182
+ static createPaymentHint(storeId) {
183
+ // Ensure the input is a 32-byte buffer
184
+ if (!Buffer.isBuffer(storeId) || storeId.length !== 32) {
185
+ throw new Error("Invalid input. Must be a 32-byte buffer.");
186
+ }
187
+ // Define the seed
188
+ const seed = "digpayment";
189
+ // Combine the seed and the original buffer
190
+ const combinedBuffer = Buffer.concat([Buffer.from(seed), storeId]);
191
+ // Apply SHA-256 hash to the combined buffer
192
+ const hash = crypto_1.default.createHash("sha256");
193
+ hash.update(combinedBuffer);
194
+ const transformedBuffer = hash.digest();
195
+ // Return the 32-byte hash as a hex string
196
+ return transformedBuffer;
197
+ }
163
198
  }
164
199
  exports.DigPeer = DigPeer;
@@ -1,3 +1,4 @@
1
+ import http from 'http';
1
2
  import { Readable } from "stream";
2
3
  export declare class PropagationServer {
3
4
  private ipAddress;
@@ -19,7 +20,12 @@ export declare class PropagationServer {
19
20
  getStatus(): Promise<{
20
21
  synced: boolean;
21
22
  }>;
22
- headStore(): Promise<boolean>;
23
+ headStore(options?: {
24
+ hasRootHash: string;
25
+ }): Promise<{
26
+ success: boolean;
27
+ headers?: http.IncomingHttpHeaders;
28
+ }>;
23
29
  isStoreSynced(): Promise<boolean>;
24
30
  getUploadDetails(): Promise<{
25
31
  nonce: string;
@@ -1 +1 @@
1
- {"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGlC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAK;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAQ;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAO;gBAElC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB/D,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAK9B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAMpD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAOzC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAM7B,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAMjC,gBAAgB,IAAI,OAAO,CAAC;QACvC,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;YAmBY,WAAW;YAyCX,kBAAkB;YA0ElB,gBAAgB;YAgEhB,cAAc;YAiCd,KAAK;YAmDL,IAAI;YAgCJ,SAAS;YAuCT,2BAA2B;YAiC3B,gBAAgB;CAyC/B"}
1
+ {"version":3,"file":"PropagationServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/PropagationServer.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAK;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAQ;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAO;gBAElC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB/D,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAK9B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAMpD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAOzC,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;KAAE,CAAC;IAW/G,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAMjC,gBAAgB,IAAI,OAAO,CAAC;QACvC,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;YAmBY,WAAW;YAyCX,kBAAkB;YAoElB,gBAAgB;YAgEhB,cAAc;YAiCd,KAAK;YAmDL,IAAI;YAmEJ,SAAS;YAuCT,2BAA2B;YAiC3B,gBAAgB;CAyC/B"}