@dignetwork/dig-sdk 0.0.1-alpha.95 → 0.0.1-alpha.98
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.
- package/dist/blockchain/FullNodePeer.d.ts +7 -1
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +35 -10
- package/dist/blockchain/ServerCoin.d.ts +2 -2
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +31 -6
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,13 @@ export declare class FullNodePeer {
|
|
|
5
5
|
private peer;
|
|
6
6
|
private static deprioritizedIps;
|
|
7
7
|
private constructor();
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Connect to the best peer, optionally using custom certificate and key file paths.
|
|
10
|
+
* @param {string} [certFilePath] - Optional custom certificate file path.
|
|
11
|
+
* @param {string} [keyFilePath] - Optional custom key file path.
|
|
12
|
+
* @returns {Promise<Peer>} The connected peer.
|
|
13
|
+
*/
|
|
14
|
+
static connect(certFilePath?: string, keyFilePath?: string): Promise<Peer>;
|
|
9
15
|
private static isPortReachable;
|
|
10
16
|
private static isValidIpAddress;
|
|
11
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAqBpD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAA0B;
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAqBpD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAA0B;IAQzD,OAAO;IAIP;;;;;OAKG;WACiB,OAAO,CACzB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC;IAKhB,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBASZ,eAAe;mBA6Df,UAAU;IA2B/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBAyDT,WAAW;IA+HzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
|
|
@@ -30,8 +30,14 @@ class FullNodePeer {
|
|
|
30
30
|
constructor(peer) {
|
|
31
31
|
this.peer = peer;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Connect to the best peer, optionally using custom certificate and key file paths.
|
|
35
|
+
* @param {string} [certFilePath] - Optional custom certificate file path.
|
|
36
|
+
* @param {string} [keyFilePath] - Optional custom key file path.
|
|
37
|
+
* @returns {Promise<Peer>} The connected peer.
|
|
38
|
+
*/
|
|
39
|
+
static async connect(certFilePath, keyFilePath) {
|
|
40
|
+
const peer = await FullNodePeer.getBestPeer(certFilePath, keyFilePath);
|
|
35
41
|
return new FullNodePeer(peer).peer;
|
|
36
42
|
}
|
|
37
43
|
static isPortReachable(host, port, timeout = CONNECTION_TIMEOUT) {
|
|
@@ -158,7 +164,8 @@ class FullNodePeer {
|
|
|
158
164
|
}
|
|
159
165
|
catch (error) {
|
|
160
166
|
// If the error is WebSocket-related or timeout, reset the peer
|
|
161
|
-
if (error.message.includes("WebSocket") ||
|
|
167
|
+
if (error.message.includes("WebSocket") ||
|
|
168
|
+
error.message.includes("Operation timed out")) {
|
|
162
169
|
FullNodePeer.cachedPeer = null;
|
|
163
170
|
// @ts-ignore
|
|
164
171
|
FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
|
|
@@ -175,17 +182,33 @@ class FullNodePeer {
|
|
|
175
182
|
},
|
|
176
183
|
});
|
|
177
184
|
}
|
|
178
|
-
static async getBestPeer() {
|
|
185
|
+
static async getBestPeer(certFilePath, keyFilePath) {
|
|
179
186
|
const now = Date.now();
|
|
180
187
|
if (FullNodePeer.cachedPeer &&
|
|
181
188
|
now - FullNodePeer.cachedPeer.timestamp < CACHE_DURATION) {
|
|
182
189
|
return FullNodePeer.cachedPeer.peer;
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
191
|
+
let certFile;
|
|
192
|
+
let keyFile;
|
|
193
|
+
// If certFilePath or keyFilePath is provided, ensure both are provided
|
|
194
|
+
if (certFilePath && keyFilePath) {
|
|
195
|
+
certFile = certFilePath;
|
|
196
|
+
keyFile = keyFilePath;
|
|
197
|
+
if (!fs_1.default.existsSync(certFile)) {
|
|
198
|
+
throw new Error(`Certificate file not found: ${certFile}`);
|
|
199
|
+
}
|
|
200
|
+
if (!fs_1.default.existsSync(keyFile)) {
|
|
201
|
+
throw new Error(`Key file not found: ${keyFile}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Use default paths if no custom paths are provided
|
|
206
|
+
const sslFolder = path_1.default.resolve(os_1.default.homedir(), ".dig", "ssl");
|
|
207
|
+
certFile = path_1.default.join(sslFolder, "public_dig.crt");
|
|
208
|
+
keyFile = path_1.default.join(sslFolder, "public_dig.key");
|
|
209
|
+
if (!fs_1.default.existsSync(sslFolder)) {
|
|
210
|
+
throw new Error(`SSL folder not found: ${sslFolder}`);
|
|
211
|
+
}
|
|
189
212
|
}
|
|
190
213
|
new chia_server_coin_1.Tls(certFile, keyFile);
|
|
191
214
|
const peerIPs = await FullNodePeer.getPeerIPs();
|
|
@@ -230,7 +253,9 @@ class FullNodePeer {
|
|
|
230
253
|
// Prioritize LOCALHOST, TRUSTED_NODE_IP, and CHIA_NODES_HOST if they have the highest peak height
|
|
231
254
|
let bestPeerIndex = validHeights.findIndex((height, index) => height === highestPeak &&
|
|
232
255
|
!FullNodePeer.deprioritizedIps.has(peerIPs[index]) && // Exclude deprioritized IPs
|
|
233
|
-
(peerIPs[index] === LOCALHOST ||
|
|
256
|
+
(peerIPs[index] === LOCALHOST ||
|
|
257
|
+
peerIPs[index] === trustedNodeIp ||
|
|
258
|
+
peerIPs[index] === CHIA_NODES_HOST));
|
|
234
259
|
// If LOCALHOST, TRUSTED_NODE_IP, or CHIA_NODES_HOST don't have the highest peak, select any peer with the highest peak
|
|
235
260
|
if (bestPeerIndex === -1) {
|
|
236
261
|
bestPeerIndex = validHeights.findIndex((height) => height === highestPeak);
|
|
@@ -4,8 +4,8 @@ export declare class ServerCoin {
|
|
|
4
4
|
private storeId;
|
|
5
5
|
static readonly serverCoinManager: NconfManager;
|
|
6
6
|
constructor(storeId: string);
|
|
7
|
-
createForEpoch(peerIp: string): Promise<ServerCoinDriver>;
|
|
8
|
-
saveServerCoinData(serverCoin: ServerCoinDriver, epoch: number, peerIp: string): Promise<void>;
|
|
7
|
+
createForEpoch(peerIp: string, rootHash: string): Promise<ServerCoinDriver>;
|
|
8
|
+
saveServerCoinData(serverCoin: ServerCoinDriver, epoch: number, peerIp: string, rootHash: string): Promise<void>;
|
|
9
9
|
melt(epoch: number, peerIp: string): Promise<void>;
|
|
10
10
|
private removeServerCoinData;
|
|
11
11
|
getAllEpochPeers(epoch: number, blacklist?: string[]): Promise<string[]>;
|
|
@@ -1 +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,8BAA8B,CAAC;AAItC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ServerCoin.d.ts","sourceRoot":"","sources":["../../src/blockchain/ServerCoin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,UAAU,IAAI,gBAAgB,EAE/B,MAAM,8BAA8B,CAAC;AAItC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAYrD,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,gBAAuB,iBAAiB,eAEtC;gBAEU,OAAO,EAAE,MAAM;IAKd,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4D3E,kBAAkB,CAC7B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsBH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAoDjD,oBAAoB;IAerB,gBAAgB,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAgDP,mBAAmB,CAC9B,SAAS,GAAE,MAAM,EAAO,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC;IAMP,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;WAeN,eAAe,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAKpD,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiFrD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WA+B1C,uBAAuB;YAkE7B,sBAAsB;IAWvB,uBAAuB,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;WAuBL,sBAAsB,CAAC,mBAAmB,EAAE,IAAI,GAAG;QAC/D,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf;CA2BF"}
|
|
@@ -22,7 +22,7 @@ class ServerCoin {
|
|
|
22
22
|
this.storeId = storeId;
|
|
23
23
|
}
|
|
24
24
|
// Create a new server coin for the current epoch
|
|
25
|
-
async createForEpoch(peerIp) {
|
|
25
|
+
async createForEpoch(peerIp, rootHash) {
|
|
26
26
|
try {
|
|
27
27
|
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
28
28
|
const wallet = await Wallet_1.Wallet.load("default");
|
|
@@ -38,12 +38,12 @@ class ServerCoin {
|
|
|
38
38
|
if (err.includes("no spendable coins")) {
|
|
39
39
|
console.log("No coins available. Will try again in 5 seconds...");
|
|
40
40
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
41
|
-
return this.createForEpoch(peerIp);
|
|
41
|
+
return this.createForEpoch(peerIp, rootHash);
|
|
42
42
|
}
|
|
43
43
|
throw new Error(err);
|
|
44
44
|
}
|
|
45
45
|
// Cache the new server coin in the NconfManager
|
|
46
|
-
await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp);
|
|
46
|
+
await this.saveServerCoinData(newServerCoin.serverCoin, currentEpoch, peerIp, rootHash);
|
|
47
47
|
return newServerCoin.serverCoin;
|
|
48
48
|
}
|
|
49
49
|
catch (error) {
|
|
@@ -51,7 +51,7 @@ class ServerCoin {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
// Save the server coin data to NconfManager
|
|
54
|
-
async saveServerCoinData(serverCoin, epoch, peerIp) {
|
|
54
|
+
async saveServerCoinData(serverCoin, epoch, peerIp, rootHash) {
|
|
55
55
|
const newServerCoinData = {
|
|
56
56
|
coin: {
|
|
57
57
|
amount: serverCoin.coin.amount.toString(),
|
|
@@ -59,7 +59,8 @@ class ServerCoin {
|
|
|
59
59
|
parentCoinInfo: serverCoin.coin.parentCoinInfo.toString("hex"),
|
|
60
60
|
},
|
|
61
61
|
createdAt: new Date().toISOString(),
|
|
62
|
-
epoch
|
|
62
|
+
epoch,
|
|
63
|
+
rootHash,
|
|
63
64
|
};
|
|
64
65
|
const serverCoins = await this.getServerCoinsForStore(peerIp);
|
|
65
66
|
serverCoins.push(newServerCoinData);
|
|
@@ -162,11 +163,34 @@ class ServerCoin {
|
|
|
162
163
|
const serverCoins = await this.getServerCoinsForStore(peerIp);
|
|
163
164
|
// Check if a server coin already exists for the current epoch
|
|
164
165
|
const existingCoin = serverCoins.find((coin) => coin.epoch === currentEpoch);
|
|
166
|
+
const dataStore = DataStore_1.DataStore.from(this.storeId);
|
|
167
|
+
const rootHistory = await dataStore.getRootHistory(true);
|
|
168
|
+
const lastRoot = lodash_1.default.last(rootHistory);
|
|
169
|
+
if (!lastRoot) {
|
|
170
|
+
throw new Error('Cant get the last root on chain, something is wrong.');
|
|
171
|
+
}
|
|
165
172
|
if (existingCoin) {
|
|
173
|
+
// nothing to do
|
|
174
|
+
if (lastRoot?.synced && lastRoot?.root_hash === existingCoin.rootHash) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// everything is fine, lets just update the roothash for tracking
|
|
178
|
+
if (lastRoot?.synced && lastRoot?.root_hash !== existingCoin.rootHash) {
|
|
179
|
+
existingCoin.rootHash = lastRoot.root_hash;
|
|
180
|
+
// Update the conf with the modified server coin
|
|
181
|
+
await ServerCoin.serverCoinManager.setConfigValue(`${this.storeId}:${peerIp}`, serverCoins);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// If not synced, melt the coin, a new one will be created when synced up
|
|
185
|
+
// this helps prevent penalties for not having a valid peer registered
|
|
186
|
+
await this.melt(currentEpoch, peerIp);
|
|
187
|
+
}
|
|
188
|
+
// Don't create a server coin until you're synced up
|
|
189
|
+
if (!lodash_1.default.last(rootHistory)?.synced) {
|
|
166
190
|
return;
|
|
167
191
|
}
|
|
168
192
|
console.log(`No server coin found for epoch ${currentEpoch}. Creating new server coin...`);
|
|
169
|
-
const serverCoin = await this.createForEpoch(peerIp);
|
|
193
|
+
const serverCoin = await this.createForEpoch(peerIp, lastRoot?.root_hash);
|
|
170
194
|
const newServerCoinData = {
|
|
171
195
|
coin: {
|
|
172
196
|
amount: serverCoin.coin.amount.toString(),
|
|
@@ -175,6 +199,7 @@ class ServerCoin {
|
|
|
175
199
|
},
|
|
176
200
|
createdAt: new Date().toISOString(),
|
|
177
201
|
epoch: currentEpoch,
|
|
202
|
+
rootHash: lodash_1.default.last(rootHistory)?.root_hash || "",
|
|
178
203
|
};
|
|
179
204
|
await FullNodePeer_1.FullNodePeer.waitForConfirmation(serverCoin.coin.parentCoinInfo);
|
|
180
205
|
serverCoins.push(newServerCoinData);
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE;QACL,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE;QACL,CAAC,GAAG,EAAE,MAAM,GAAG;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B"}
|