@dignetwork/dig-sdk 0.0.1-alpha.18 → 0.0.1-alpha.181
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +10 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +77 -10
- package/dist/DigNetwork/ContentServer.d.ts +22 -5
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +226 -28
- package/dist/DigNetwork/DigNetwork.d.ts +15 -9
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +129 -189
- package/dist/DigNetwork/DigPeer.d.ts +8 -3
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +72 -115
- package/dist/DigNetwork/IncentiveServer.d.ts.map +1 -1
- package/dist/DigNetwork/IncentiveServer.js +6 -5
- package/dist/DigNetwork/PropagationServer.d.ts +89 -33
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +625 -358
- package/dist/blockchain/DataStore.d.ts +25 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +117 -159
- package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
- package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.d.ts +95 -10
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +325 -115
- package/dist/blockchain/ServerCoin.d.ts +13 -5
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +89 -28
- package/dist/blockchain/StoreMonitorRegistry.d.ts +85 -0
- package/dist/blockchain/StoreMonitorRegistry.d.ts.map +1 -0
- package/dist/blockchain/StoreMonitorRegistry.js +238 -0
- package/dist/blockchain/Wallet.d.ts +2 -2
- package/dist/blockchain/Wallet.d.ts.map +1 -1
- package/dist/blockchain/Wallet.js +50 -18
- package/dist/blockchain/coins.d.ts +1 -2
- package/dist/blockchain/coins.d.ts.map +1 -1
- package/dist/blockchain/coins.js +1 -55
- package/dist/blockchain/index.d.ts +1 -0
- package/dist/blockchain/index.d.ts.map +1 -1
- package/dist/blockchain/index.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/ContentScanner.d.ts +63 -0
- package/dist/utils/ContentScanner.d.ts.map +1 -0
- package/dist/utils/ContentScanner.js +175 -0
- package/dist/utils/DigCache.d.ts +38 -0
- package/dist/utils/DigCache.d.ts.map +1 -0
- package/dist/utils/DigCache.js +236 -0
- package/dist/utils/Environment.d.ts +21 -0
- package/dist/utils/Environment.d.ts.map +1 -0
- package/dist/utils/Environment.js +93 -0
- package/dist/utils/FileCache.d.ts +1 -1
- package/dist/utils/FileCache.d.ts.map +1 -1
- package/dist/utils/FileCache.js +2 -2
- package/dist/utils/FileTransfer.d.ts +47 -0
- package/dist/utils/FileTransfer.d.ts.map +1 -0
- package/dist/utils/FileTransfer.js +209 -0
- package/dist/utils/NconfManager.d.ts.map +1 -1
- package/dist/utils/NconfManager.js +16 -7
- package/dist/utils/PeerRanker.d.ts +55 -0
- package/dist/utils/PeerRanker.d.ts.map +1 -0
- package/dist/utils/PeerRanker.js +186 -0
- package/dist/utils/StoreArchiveManager.d.ts +45 -0
- package/dist/utils/StoreArchiveManager.d.ts.map +1 -0
- package/dist/utils/StoreArchiveManager.js +153 -0
- package/dist/utils/Udi.d.ts +28 -0
- package/dist/utils/Udi.d.ts.map +1 -0
- package/dist/utils/Udi.js +145 -0
- package/dist/utils/config.d.ts +4 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +13 -8
- package/dist/utils/directoryUtils.d.ts +16 -0
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/dist/utils/directoryUtils.js +66 -0
- package/dist/utils/index.d.ts +5 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/merkle.d.ts +2 -0
- package/dist/utils/merkle.d.ts.map +1 -0
- package/dist/utils/merkle.js +28 -0
- package/dist/utils/network.d.ts +2 -1
- package/dist/utils/network.d.ts.map +1 -1
- package/dist/utils/network.js +40 -15
- package/dist/utils/promiseUtils.d.ts +27 -0
- package/dist/utils/promiseUtils.d.ts.map +1 -0
- package/dist/utils/promiseUtils.js +69 -0
- package/dist/utils/ssl.js +2 -2
- package/package.json +31 -13
- package/dist/utils/deltaUtils.d.ts +0 -2
- package/dist/utils/deltaUtils.d.ts.map +0 -1
- package/dist/utils/deltaUtils.js +0 -83
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { DataStore as DataStoreDriver, DataStoreMetadata } from "datalayer-driver";
|
|
1
|
+
import { DataStore as DataStoreDriver, DataStoreMetadata } from "@dignetwork/datalayer-driver";
|
|
2
2
|
import { RootHistoryItem } from "../types";
|
|
3
3
|
import { DataIntegrityTree, DataIntegrityTreeOptions } from "../DataIntegrityTree";
|
|
4
4
|
import { CreateStoreUserInputs } from "../types";
|
|
5
5
|
export declare class DataStore {
|
|
6
6
|
private storeId;
|
|
7
7
|
private tree;
|
|
8
|
+
private static activeMonitors;
|
|
8
9
|
constructor(storeId: string, options?: DataIntegrityTreeOptions);
|
|
9
10
|
get StoreId(): string;
|
|
10
11
|
get Tree(): DataIntegrityTree;
|
|
@@ -13,7 +14,7 @@ export declare class DataStore {
|
|
|
13
14
|
serialize(): string;
|
|
14
15
|
static getActiveStore(): Promise<DataStore | undefined>;
|
|
15
16
|
static deserialize(serialized: string): DataStore;
|
|
16
|
-
static from(storeId: string | Buffer): DataStore;
|
|
17
|
+
static from(storeId: string | Buffer, rootHash?: string): DataStore;
|
|
17
18
|
static create(inputs?: CreateStoreUserInputs): Promise<DataStore>;
|
|
18
19
|
private static mint;
|
|
19
20
|
/**
|
|
@@ -33,24 +34,41 @@ export declare class DataStore {
|
|
|
33
34
|
*/
|
|
34
35
|
private static calculateFolderSize;
|
|
35
36
|
static getAllStores(): DataStore[];
|
|
37
|
+
/**
|
|
38
|
+
* Fetches the latest coin information using the StoreMonitorRegistry.
|
|
39
|
+
* Registers the storeId if it's not already registered, retrieves the cached value, and returns it.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<{ latestStore: DataStoreDriver; latestHeight: number; latestHash: Buffer }>}
|
|
42
|
+
*/
|
|
36
43
|
fetchCoinInfo(): Promise<{
|
|
37
44
|
latestStore: DataStoreDriver;
|
|
38
45
|
latestHeight: number;
|
|
39
46
|
latestHash: Buffer;
|
|
40
47
|
}>;
|
|
48
|
+
cacheStoreCreationHeight(): Promise<{
|
|
49
|
+
createdAtHeight: number;
|
|
50
|
+
createdAtHash: Buffer;
|
|
51
|
+
}>;
|
|
41
52
|
getCreationHeight(): Promise<{
|
|
42
53
|
createdAtHeight: number;
|
|
43
54
|
createdAtHash: Buffer;
|
|
44
55
|
}>;
|
|
45
56
|
private setCreationHeight;
|
|
46
|
-
getRootHistory(): Promise<RootHistoryItem[]>;
|
|
47
|
-
|
|
48
|
-
validate(): Promise<boolean>;
|
|
57
|
+
getRootHistory(bustCache?: boolean): Promise<RootHistoryItem[]>;
|
|
58
|
+
generateManifestFile(folderPath?: string): Promise<void>;
|
|
49
59
|
getMetaData(): Promise<DataStoreMetadata>;
|
|
50
60
|
isSynced(): Promise<boolean>;
|
|
51
61
|
hasMetaWritePermissions(publicSyntheticKey?: Buffer): Promise<boolean>;
|
|
52
62
|
updateMetadata(metadata: DataStoreMetadata): Promise<DataStoreDriver>;
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Retrieve the file set for a given root hash and validate file integrity.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} rootHash - The root hash to fetch the file set.
|
|
67
|
+
* @returns {Promise<{ fileName: string, file: string }[]>} - An array of unique file objects.
|
|
68
|
+
*/
|
|
69
|
+
getFileSetForRootHash(rootHash: string, skipIntegirtyCheck?: boolean): Promise<{
|
|
70
|
+
name: string;
|
|
71
|
+
path: string;
|
|
72
|
+
}[]>;
|
|
55
73
|
}
|
|
56
74
|
//# sourceMappingURL=DataStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataStore.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,IAAI,eAAe,EAE5B,iBAAiB,EAIlB,MAAM,
|
|
1
|
+
{"version":3,"file":"DataStore.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,SAAS,IAAI,eAAe,EAE5B,iBAAiB,EAIlB,MAAM,8BAA8B,CAAC;AAWtC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAcjD,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAmC;gBAEpD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,wBAAwB;IAiB/D,IAAW,OAAO,IAAI,MAAM,CAE3B;IAED,IAAW,IAAI,IAAI,iBAAiB,CAEnC;IAEM,QAAQ,IAAI,MAAM;IAIlB,QAAQ,IAAI,MAAM;IAIlB,SAAS,IAAI,MAAM;WAMN,cAAc,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;WAOtD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS;WAK1C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS;WAqBtD,MAAM,CACxB,MAAM,GAAE,qBAA0B,GACjC,OAAO,CAAC,SAAS,CAAC;mBAyCA,IAAI;IA4FzB;;;OAGG;IACU,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5C;;;OAGG;WACiB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxD;;;;OAIG;mBACkB,mBAAmB;WAqB1B,YAAY,IAAI,SAAS,EAAE;IAWzC;;;;;OAKG;IACU,aAAa,IAAI,OAAO,CAAC;QACpC,WAAW,EAAE,eAAe,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAkCW,wBAAwB,IAAI,OAAO,CAAC;QAC/C,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IAiBW,iBAAiB,IAAI,OAAO,CAAC;QACxC,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;YA0BY,iBAAiB;IAYlB,cAAc,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA8C/D,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAexD,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAKzC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAM5B,uBAAuB,CAClC,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkBN,cAAc,CACzB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,eAAe,CAAC;IAgD3B;;;;;OAKG;IACU,qBAAqB,CAChC,QAAQ,EAAE,MAAM,EAChB,kBAAkB,GAAE,OAAe,GAClC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CA6C7C"}
|
|
@@ -6,18 +6,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.DataStore = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const datalayer_driver_1 = require("datalayer-driver");
|
|
9
|
+
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
10
10
|
const util_1 = require("util");
|
|
11
11
|
const FullNodePeer_1 = require("./FullNodePeer");
|
|
12
12
|
const Wallet_1 = require("./Wallet");
|
|
13
13
|
const config_1 = require("../utils/config");
|
|
14
14
|
const coins_1 = require("./coins");
|
|
15
|
-
const
|
|
15
|
+
const colorette_1 = require("colorette");
|
|
16
16
|
const hashUtils_1 = require("../utils/hashUtils");
|
|
17
17
|
const DataIntegrityTree_1 = require("../DataIntegrityTree");
|
|
18
18
|
const prompts_1 = require("../prompts");
|
|
19
19
|
const FileCache_1 = require("../utils/FileCache");
|
|
20
20
|
const DataStoreSerializer_1 = require("./DataStoreSerializer");
|
|
21
|
+
const config_2 = require("../utils/config");
|
|
22
|
+
const StoreMonitorRegistry_1 = require("./StoreMonitorRegistry");
|
|
23
|
+
const node_cache_1 = __importDefault(require("node-cache"));
|
|
24
|
+
// Initialize the cache with a TTL of 180 seconds (3 minutes)
|
|
25
|
+
const rootHistoryCache = new node_cache_1.default({ stdTTL: 180 });
|
|
21
26
|
const stat = (0, util_1.promisify)(fs_1.default.stat);
|
|
22
27
|
const readdir = (0, util_1.promisify)(fs_1.default.readdir);
|
|
23
28
|
class DataStore {
|
|
@@ -62,12 +67,18 @@ class DataStore {
|
|
|
62
67
|
const parsed = JSON.parse(serialized);
|
|
63
68
|
return new DataStore(parsed.storeId);
|
|
64
69
|
}
|
|
65
|
-
static from(storeId) {
|
|
70
|
+
static from(storeId, rootHash) {
|
|
71
|
+
if (!fs_1.default.existsSync(path_1.default.join(config_1.STORE_PATH, storeId.toString("hex")))) {
|
|
72
|
+
throw new Error(`Store with ID ${storeId.toString("hex")} does not exist.`);
|
|
73
|
+
}
|
|
66
74
|
const existingTreeOptions = {
|
|
67
75
|
storageMode: "local",
|
|
68
76
|
storeDir: config_1.STORE_PATH,
|
|
69
77
|
disableInitialize: true,
|
|
70
78
|
};
|
|
79
|
+
if (rootHash) {
|
|
80
|
+
existingTreeOptions.rootHash = rootHash;
|
|
81
|
+
}
|
|
71
82
|
if (storeId instanceof Buffer) {
|
|
72
83
|
return new DataStore(storeId.toString("hex"), existingTreeOptions);
|
|
73
84
|
}
|
|
@@ -107,7 +118,7 @@ class DataStore {
|
|
|
107
118
|
const wallet = await Wallet_1.Wallet.load("default");
|
|
108
119
|
const publicSyntheticKey = await wallet.getPublicSyntheticKey();
|
|
109
120
|
const ownerSyntheicPuzzleHash = (0, datalayer_driver_1.syntheticKeyToPuzzleHash)(publicSyntheticKey);
|
|
110
|
-
const storeCreationCoins = await
|
|
121
|
+
const storeCreationCoins = await wallet.selectUnspentCoins(peer, BigInt(1), BigInt(0));
|
|
111
122
|
const delegationLayers = [];
|
|
112
123
|
if (adminPublicSyntheticKey) {
|
|
113
124
|
delegationLayers.push((0, datalayer_driver_1.adminDelegatedPuzzleFromKey)(Buffer.from(adminPublicSyntheticKey, "hex")));
|
|
@@ -189,90 +200,68 @@ class DataStore {
|
|
|
189
200
|
}
|
|
190
201
|
static getAllStores() {
|
|
191
202
|
const storeFolders = fs_1.default.readdirSync(config_1.STORE_PATH);
|
|
192
|
-
const
|
|
203
|
+
const storeIds = storeFolders.filter((folder) => /^[a-f0-9]{64}$/.test(folder) &&
|
|
193
204
|
fs_1.default.lstatSync(path_1.default.join(config_1.STORE_PATH, folder)).isDirectory());
|
|
194
|
-
return
|
|
205
|
+
return storeIds.map((storeId) => DataStore.from(storeId));
|
|
195
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Fetches the latest coin information using the StoreMonitorRegistry.
|
|
209
|
+
* Registers the storeId if it's not already registered, retrieves the cached value, and returns it.
|
|
210
|
+
*
|
|
211
|
+
* @returns {Promise<{ latestStore: DataStoreDriver; latestHeight: number; latestHash: Buffer }>}
|
|
212
|
+
*/
|
|
196
213
|
async fetchCoinInfo() {
|
|
214
|
+
const storeMonitor = StoreMonitorRegistry_1.StoreMonitorRegistry.getInstance();
|
|
197
215
|
try {
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const { latestStore: previousInfo } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
207
|
-
latestStore: serializedStore,
|
|
208
|
-
latestHeight: previousHeight.toString(),
|
|
209
|
-
latestHash: previousHash,
|
|
210
|
-
});
|
|
211
|
-
// Sync with peer if necessary
|
|
212
|
-
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
213
|
-
const { latestStore, latestHeight } = await peer.syncStore(previousInfo, previousHeight, Buffer.from(previousHash, "hex"), false);
|
|
214
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
215
|
-
// Serialize the store data for caching
|
|
216
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
217
|
-
// Cache updated store info
|
|
218
|
-
storeCoinCache.set(this.storeId, {
|
|
219
|
-
latestStore: serializedLatestStore,
|
|
220
|
-
latestHeight,
|
|
221
|
-
latestHash: latestHash.toString("hex"),
|
|
222
|
-
});
|
|
223
|
-
return { latestStore, latestHeight, latestHash };
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
// Return cached info if sync fails
|
|
227
|
-
const { latestStore, latestHeight, latestHash } = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
228
|
-
latestStore: cachedInfo.latestStore,
|
|
229
|
-
latestHeight: cachedInfo.latestHeight.toString(),
|
|
230
|
-
latestHash: cachedInfo.latestHash,
|
|
231
|
-
});
|
|
232
|
-
return {
|
|
233
|
-
latestStore,
|
|
234
|
-
latestHeight,
|
|
235
|
-
latestHash: latestHash,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
216
|
+
// Register the storeId with a no-op callback. If already registered, the registry will handle it.
|
|
217
|
+
await storeMonitor.registerStore(this.storeId, () => {
|
|
218
|
+
// No operation callback since we're fetching the cache directly
|
|
219
|
+
});
|
|
220
|
+
// Retrieve the latest cached store information
|
|
221
|
+
const cachedInfo = await storeMonitor.getLatestCache(this.storeId);
|
|
222
|
+
if (!cachedInfo) {
|
|
223
|
+
throw new Error(`No cached info found for storeId: ${this.storeId}`);
|
|
238
224
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const { latestStore, latestHeight } = await peer.syncStoreFromLauncherId(Buffer.from(this.storeId, "hex"), createdAtHeight, createdAtHash, false);
|
|
244
|
-
const latestHash = await peer.getHeaderHash(latestHeight);
|
|
245
|
-
// Serialize the latest store info for caching
|
|
246
|
-
const serializedLatestStore = new DataStoreSerializer_1.DataStoreSerializer(latestStore, latestHeight, latestHash).serialize();
|
|
247
|
-
// Cache the latest store info
|
|
248
|
-
storeCoinCache.set(this.storeId, {
|
|
249
|
-
latestStore: serializedLatestStore,
|
|
250
|
-
latestHeight,
|
|
251
|
-
latestHash: latestHash.toString("hex"),
|
|
225
|
+
const deserializedStore = DataStoreSerializer_1.DataStoreSerializer.deserialize({
|
|
226
|
+
latestStore: cachedInfo.latestStore,
|
|
227
|
+
latestHeight: cachedInfo.latestHeight.toString(),
|
|
228
|
+
latestHash: cachedInfo.latestHash,
|
|
252
229
|
});
|
|
253
|
-
return {
|
|
230
|
+
return {
|
|
231
|
+
latestStore: deserializedStore.latestStore,
|
|
232
|
+
latestHeight: deserializedStore.latestHeight,
|
|
233
|
+
latestHash: deserializedStore.latestHash,
|
|
234
|
+
};
|
|
254
235
|
}
|
|
255
236
|
catch (error) {
|
|
256
|
-
|
|
257
|
-
throw error;
|
|
237
|
+
throw new Error(`Failed to fetch coin info for storeId ${this.storeId}: ${error.message}`);
|
|
258
238
|
}
|
|
259
239
|
}
|
|
240
|
+
async cacheStoreCreationHeight() {
|
|
241
|
+
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
242
|
+
const createdAtHeight = await peer.getStoreCreationHeight(Buffer.from(this.storeId, "hex"), null, Buffer.from(config_2.MAIN_NET_GENISES_CHALLENGE, "hex"));
|
|
243
|
+
// Get just before created at height so we can find the coin
|
|
244
|
+
const justBeforeCreatedAtHeight = Number(createdAtHeight) - 1;
|
|
245
|
+
const createdAtHash = await peer.getHeaderHash(justBeforeCreatedAtHeight);
|
|
246
|
+
await this.setCreationHeight(justBeforeCreatedAtHeight, createdAtHash);
|
|
247
|
+
return { createdAtHeight: justBeforeCreatedAtHeight, createdAtHash };
|
|
248
|
+
}
|
|
260
249
|
async getCreationHeight() {
|
|
261
|
-
const defaultHeight = config_1.MIN_HEIGHT;
|
|
262
|
-
const defaultHash = Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex");
|
|
263
250
|
// Initialize the FileCache for the height file
|
|
264
251
|
const fileCache = new FileCache_1.FileCache(`stores/${this.storeId}`);
|
|
265
252
|
// Try to retrieve the cached height information
|
|
266
253
|
const cachedHeightInfo = fileCache.get("height");
|
|
267
254
|
if (!cachedHeightInfo) {
|
|
268
|
-
// If no cache,
|
|
269
|
-
return
|
|
255
|
+
// If no cache, regenerate the cache
|
|
256
|
+
return this.cacheStoreCreationHeight();
|
|
270
257
|
}
|
|
271
258
|
// Parse the cached height and hash values
|
|
272
259
|
const { height, hash } = cachedHeightInfo;
|
|
260
|
+
const defaultHeight = config_1.MIN_HEIGHT;
|
|
261
|
+
const defaultHash = Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex");
|
|
273
262
|
return {
|
|
274
263
|
createdAtHeight: height || defaultHeight,
|
|
275
|
-
createdAtHash: Buffer.from(hash
|
|
264
|
+
createdAtHash: hash ? Buffer.from(hash, "hex") : defaultHash,
|
|
276
265
|
};
|
|
277
266
|
}
|
|
278
267
|
async setCreationHeight(height, hash) {
|
|
@@ -283,98 +272,53 @@ class DataStore {
|
|
|
283
272
|
hash: hash.toString("hex"),
|
|
284
273
|
});
|
|
285
274
|
}
|
|
286
|
-
async getRootHistory() {
|
|
275
|
+
async getRootHistory(bustCache) {
|
|
276
|
+
if (bustCache) {
|
|
277
|
+
rootHistoryCache.del(this.storeId);
|
|
278
|
+
}
|
|
279
|
+
// Check if the root history is cached for this storeId
|
|
280
|
+
const cachedHistory = await rootHistoryCache.get(this.storeId);
|
|
281
|
+
if (cachedHistory) {
|
|
282
|
+
return cachedHistory;
|
|
283
|
+
}
|
|
284
|
+
// Fetch root history from peer if not cached
|
|
287
285
|
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
288
286
|
const { createdAtHeight, createdAtHash } = await this.getCreationHeight();
|
|
289
287
|
const { rootHashes, rootHashesTimestamps } = await peer.syncStoreFromLauncherId(Buffer.from(this.storeId, "hex"), createdAtHeight, createdAtHash, true);
|
|
290
288
|
if (!rootHashes) {
|
|
291
289
|
return [];
|
|
292
290
|
}
|
|
293
|
-
|
|
291
|
+
// Build the root history list
|
|
292
|
+
const rootHistory = rootHashes.map((rootHash, index) => ({
|
|
294
293
|
root_hash: rootHash.toString("hex"),
|
|
295
294
|
timestamp: Number(rootHashesTimestamps?.[index].toString()),
|
|
295
|
+
synced: fs_1.default.existsSync(path_1.default.join(config_1.STORE_PATH, this.storeId, `${rootHash.toString("hex")}.dat`)),
|
|
296
296
|
}));
|
|
297
|
+
// Store the root history in the cache
|
|
298
|
+
rootHistoryCache.set(this.storeId, rootHistory);
|
|
299
|
+
return rootHistory;
|
|
297
300
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
// Generates a fresh manifest file based on the current root history
|
|
302
|
+
// and what is currently on disk
|
|
303
|
+
async generateManifestFile(folderPath) {
|
|
304
|
+
if (!folderPath) {
|
|
305
|
+
folderPath = path_1.default.join(config_1.STORE_PATH, this.storeId, "data");
|
|
303
306
|
}
|
|
304
|
-
const manifestHashes = fs_1.default
|
|
305
|
-
.readFileSync(manifestFilePath, "utf-8")
|
|
306
|
-
.split("\n")
|
|
307
|
-
.filter(Boolean);
|
|
308
|
-
return manifestHashes.map((rootHash) => ({
|
|
309
|
-
root_hash: rootHash,
|
|
310
|
-
timestamp: 0, // Timestamps are not yet included in the manifest
|
|
311
|
-
}));
|
|
312
|
-
}
|
|
313
|
-
async validate() {
|
|
314
307
|
const rootHistory = await this.getRootHistory();
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const manifestHashes = fs_1.default
|
|
321
|
-
.readFileSync(manifestFilePath, "utf-8")
|
|
322
|
-
.split("\n")
|
|
323
|
-
.filter(Boolean);
|
|
324
|
-
if (manifestHashes.length > rootHistory.length) {
|
|
325
|
-
console.error("The store is corrupted: Manifest file has more hashes than the root history.");
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
if (rootHistory.length > manifestHashes.length) {
|
|
329
|
-
console.error("The store is not synced: Root history has more hashes than the manifest file.");
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
for (let i = 0; i < manifestHashes.length; i++) {
|
|
333
|
-
if (manifestHashes[i] !== rootHistory[i]?.root_hash) {
|
|
334
|
-
console.error(`Root hash mismatch at position ${i}: expected ${manifestHashes[i]} but found ${rootHistory[i]?.root_hash}`);
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
let filesIntegrityIntact = true;
|
|
339
|
-
for (const rootHash of manifestHashes) {
|
|
340
|
-
const datFilePath = path_1.default.join(config_1.STORE_PATH, this.storeId, `${rootHash}.dat`);
|
|
341
|
-
if (!fs_1.default.existsSync(datFilePath)) {
|
|
342
|
-
console.error(`Data file for root hash ${rootHash} not found`);
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
const datFileContent = JSON.parse(fs_1.default.readFileSync(datFilePath, "utf-8"));
|
|
346
|
-
if (datFileContent.root !== rootHash) {
|
|
347
|
-
console.error(`Root hash in data file does not match: ${datFileContent.root} !== ${rootHash}`);
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
for (const [fileKey, fileData] of Object.entries(datFileContent.files)) {
|
|
351
|
-
const integrityCheck = (0, utils_1.validateFileSha256)(fileData.sha256, path_1.default.join(config_1.STORE_PATH, this.storeId, "data"));
|
|
352
|
-
if (!integrityCheck) {
|
|
353
|
-
filesIntegrityIntact = false;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (!filesIntegrityIntact) {
|
|
358
|
-
console.error("Store Corrupted: Data failed SHA256 validation.");
|
|
359
|
-
return false;
|
|
360
|
-
}
|
|
361
|
-
return true;
|
|
308
|
+
// Need this for the dataintegrity tree to work properly
|
|
309
|
+
fs_1.default.writeFileSync(path_1.default.join(folderPath, "manifest.dat"), rootHistory
|
|
310
|
+
.filter((root) => root.synced)
|
|
311
|
+
.map((root) => root.root_hash)
|
|
312
|
+
.join("\n"));
|
|
362
313
|
}
|
|
363
314
|
async getMetaData() {
|
|
364
315
|
const { latestStore } = await this.fetchCoinInfo();
|
|
365
316
|
return latestStore.metadata;
|
|
366
317
|
}
|
|
367
318
|
async isSynced() {
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
const manifestHashes = fs_1.default
|
|
374
|
-
.readFileSync(manifestFilePath, "utf-8")
|
|
375
|
-
.split("\n")
|
|
376
|
-
.filter(Boolean);
|
|
377
|
-
return rootHistory.length === manifestHashes.length;
|
|
319
|
+
const dataStore = await DataStore.from(this.storeId);
|
|
320
|
+
const rootHistory = await dataStore.getRootHistory();
|
|
321
|
+
return !rootHistory.some((root) => !root.synced);
|
|
378
322
|
}
|
|
379
323
|
async hasMetaWritePermissions(publicSyntheticKey) {
|
|
380
324
|
const wallet = await Wallet_1.Wallet.load("default");
|
|
@@ -394,7 +338,7 @@ class DataStore {
|
|
|
394
338
|
const { latestStore } = await this.fetchCoinInfo();
|
|
395
339
|
const updateStoreResponse = (0, datalayer_driver_1.updateStoreMetadata)(latestStore, metadata.rootHash, metadata.label, metadata.description, metadata.bytes, publicSyntheticKey, null, null);
|
|
396
340
|
const fee = await (0, coins_1.calculateFeeForCoinSpends)(peer, null);
|
|
397
|
-
const unspentCoins = await
|
|
341
|
+
const unspentCoins = await wallet.selectUnspentCoins(peer, BigInt(0), fee);
|
|
398
342
|
const feeCoinSpends = await (0, datalayer_driver_1.addFee)(publicSyntheticKey, unspentCoins, updateStoreResponse.coinSpends.map((coinSpend) => (0, datalayer_driver_1.getCoinId)(coinSpend.coin)), fee);
|
|
399
343
|
const combinedCoinSpends = [
|
|
400
344
|
...updateStoreResponse.coinSpends,
|
|
@@ -407,27 +351,41 @@ class DataStore {
|
|
|
407
351
|
}
|
|
408
352
|
return updateStoreResponse.newStore;
|
|
409
353
|
}
|
|
410
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Retrieve the file set for a given root hash and validate file integrity.
|
|
356
|
+
*
|
|
357
|
+
* @param {string} rootHash - The root hash to fetch the file set.
|
|
358
|
+
* @returns {Promise<{ fileName: string, file: string }[]>} - An array of unique file objects.
|
|
359
|
+
*/
|
|
360
|
+
async getFileSetForRootHash(rootHash, skipIntegirtyCheck = false) {
|
|
411
361
|
const datFilePath = path_1.default.join(config_1.STORE_PATH, this.storeId, `${rootHash}.dat`);
|
|
412
362
|
const datFileContent = JSON.parse(fs_1.default.readFileSync(datFilePath, "utf-8"));
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
filesInvolved.push(manifestFilePath);
|
|
417
|
-
filesInvolved.push(datFilePath);
|
|
418
|
-
filesInvolved.push(heightDatFilePath);
|
|
363
|
+
// Use a Set to ensure uniqueness
|
|
364
|
+
const filesInvolved = new Set();
|
|
365
|
+
// Iterate over each file and perform an integrity check
|
|
419
366
|
for (const [fileKey, fileData] of Object.entries(datFileContent.files)) {
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
367
|
+
const filePath = (0, hashUtils_1.getFilePathFromSha256)(datFileContent.files[fileKey].sha256, "data");
|
|
368
|
+
// Perform the integrity check
|
|
369
|
+
let integrityCheck;
|
|
370
|
+
if (!skipIntegirtyCheck) {
|
|
371
|
+
integrityCheck =
|
|
372
|
+
await DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(fileKey, datFileContent.files[fileKey].sha256, datFileContent, rootHash, path_1.default.join(config_1.STORE_PATH, this.storeId, "data"));
|
|
373
|
+
}
|
|
374
|
+
if (integrityCheck || skipIntegirtyCheck) {
|
|
375
|
+
// Add the file to the Set
|
|
376
|
+
filesInvolved.add({
|
|
377
|
+
name: Buffer.from(fileKey, "hex").toString("utf-8"),
|
|
378
|
+
path: filePath,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
console.error((0, colorette_1.red)(`File ${fileKey} failed the integrity check.`));
|
|
383
|
+
throw new Error(`Integrity check failed for file: ${fileKey}. Aborting.`);
|
|
384
|
+
}
|
|
423
385
|
}
|
|
424
|
-
return
|
|
425
|
-
|
|
426
|
-
getManifestHashes() {
|
|
427
|
-
const manifestFilePath = path_1.default.join(config_1.STORE_PATH, this.storeId, "manifest.dat");
|
|
428
|
-
return fs_1.default.existsSync(manifestFilePath)
|
|
429
|
-
? fs_1.default.readFileSync(manifestFilePath, "utf-8").split("\n").filter(Boolean)
|
|
430
|
-
: [];
|
|
386
|
+
// Convert Set to Array and return
|
|
387
|
+
return Array.from(filesInvolved);
|
|
431
388
|
}
|
|
432
389
|
}
|
|
433
390
|
exports.DataStore = DataStore;
|
|
391
|
+
DataStore.activeMonitors = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataStoreSerializer.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStoreSerializer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAKV,MAAM,
|
|
1
|
+
{"version":3,"file":"DataStoreSerializer.d.ts","sourceRoot":"","sources":["../../src/blockchain/DataStoreSerializer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAKV,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAS;gBAEf,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAOnE,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgDF,WAAW,CACvB,IAAI,EAAE;QACJ,WAAW,EAAE,GAAG,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,GACA;QACD,WAAW,EAAE,SAAS,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB;CAuEF"}
|
|
@@ -1,24 +1,109 @@
|
|
|
1
|
-
import { Peer } from "datalayer-driver";
|
|
1
|
+
import { Peer } from "@dignetwork/datalayer-driver";
|
|
2
|
+
/**
|
|
3
|
+
* FullNodePeer manages connections to full nodes, prioritizing certain peers and handling reliability.
|
|
4
|
+
*/
|
|
2
5
|
export declare class FullNodePeer {
|
|
3
|
-
private static cachedPeer;
|
|
4
|
-
private static memoizedFetchNewPeerIPs;
|
|
5
6
|
private peer;
|
|
7
|
+
private static instance;
|
|
8
|
+
private static cooldownCache;
|
|
9
|
+
private static failedDNSCache;
|
|
10
|
+
private static peerWeights;
|
|
11
|
+
private static prioritizedPeers;
|
|
12
|
+
private static peerInfos;
|
|
13
|
+
private static peerIPCache;
|
|
14
|
+
private static dnsCache;
|
|
15
|
+
private static peerLimiters;
|
|
16
|
+
private static roundRobinIndex;
|
|
6
17
|
private constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Retrieves the singleton instance of FullNodePeer.
|
|
20
|
+
* @returns {FullNodePeer} The singleton instance.
|
|
21
|
+
*/
|
|
22
|
+
static getInstance(): FullNodePeer;
|
|
23
|
+
/**
|
|
24
|
+
* Initializes the singleton instance by connecting to the best peer.
|
|
25
|
+
*/
|
|
26
|
+
initialize(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Connects and returns the best available peer.
|
|
29
|
+
* Implements singleton behavior.
|
|
30
|
+
* @returns {Promise<Peer>} The connected Peer instance.
|
|
31
|
+
*/
|
|
7
32
|
static connect(): Promise<Peer>;
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a given port on a host is reachable.
|
|
35
|
+
* @param {string} host - The host IP address.
|
|
36
|
+
* @param {number} port - The port number.
|
|
37
|
+
* @param {number} timeout - Connection timeout in milliseconds.
|
|
38
|
+
* @returns {Promise<boolean>} Whether the port is reachable.
|
|
39
|
+
*/
|
|
8
40
|
private static isPortReachable;
|
|
41
|
+
/**
|
|
42
|
+
* Validates an IPv4 address.
|
|
43
|
+
* @param {string} ip - The IP address to validate.
|
|
44
|
+
* @returns {boolean} Whether the IP address is valid.
|
|
45
|
+
*/
|
|
9
46
|
private static isValidIpAddress;
|
|
10
47
|
/**
|
|
11
|
-
* Retrieves the TRUSTED_FULLNODE IP from the environment
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @returns {string | null} The valid IP address or null if invalid
|
|
48
|
+
* Retrieves the TRUSTED_FULLNODE IP from the environment and verifies its validity.
|
|
49
|
+
* @returns {string | null} The trusted full node IP or null if invalid.
|
|
15
50
|
*/
|
|
16
51
|
private static getTrustedFullNode;
|
|
52
|
+
/**
|
|
53
|
+
* Fetches new peer IPs from DNS introducers and prioritized hosts.
|
|
54
|
+
* Utilizes caching to avoid redundant DNS resolutions.
|
|
55
|
+
* @returns {Promise<string[]>} An array of reachable peer IPs.
|
|
56
|
+
*/
|
|
17
57
|
private static fetchNewPeerIPs;
|
|
58
|
+
/**
|
|
59
|
+
* Shuffles an array using the Fisher-Yates algorithm.
|
|
60
|
+
* @param {T[]} array - The array to shuffle.
|
|
61
|
+
* @returns {T[]} The shuffled array.
|
|
62
|
+
*/
|
|
63
|
+
private static shuffleArray;
|
|
64
|
+
/**
|
|
65
|
+
* Initializes the peer weights map.
|
|
66
|
+
* Assigns higher initial weights to prioritized peers.
|
|
67
|
+
* @param {string[]} peerIPs - An array of peer IPs.
|
|
68
|
+
*/
|
|
69
|
+
private static initializePeerWeights;
|
|
70
|
+
/**
|
|
71
|
+
* Selects the next peer based on round-robin selection.
|
|
72
|
+
* @returns {string} The selected peer IP.
|
|
73
|
+
*/
|
|
74
|
+
private static selectPeerRoundRobin;
|
|
75
|
+
/**
|
|
76
|
+
* Retrieves all reachable peer IPs, excluding those in cooldown.
|
|
77
|
+
* @returns {Promise<string[]>} An array of reachable peer IPs.
|
|
78
|
+
*/
|
|
18
79
|
private static getPeerIPs;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Initializes the peer weights based on prioritization and reliability.
|
|
82
|
+
* @param {string[]} peerIPs - An array of peer IPs.
|
|
83
|
+
*/
|
|
84
|
+
private static setupPeers;
|
|
85
|
+
/**
|
|
86
|
+
* Connects to the best available peer based on round-robin selection and reliability.
|
|
87
|
+
* @returns {Promise<Peer>} The connected Peer instance.
|
|
88
|
+
*/
|
|
89
|
+
private getBestPeer;
|
|
90
|
+
private createPeerProxy;
|
|
91
|
+
/**
|
|
92
|
+
* Handles peer disconnection by marking it in cooldown and updating internal states.
|
|
93
|
+
* @param {string} peerIP - The IP address of the disconnected peer.
|
|
94
|
+
*/
|
|
95
|
+
static handlePeerDisconnection(peerIP: string): void;
|
|
96
|
+
/**
|
|
97
|
+
* Extracts the IP address from a Peer instance.
|
|
98
|
+
* @param {Peer} peer - The Peer instance.
|
|
99
|
+
* @returns {string | null} The extracted IP address or null if not found.
|
|
100
|
+
*/
|
|
101
|
+
private static extractPeerIP;
|
|
102
|
+
/**
|
|
103
|
+
* Waits for a coin to be confirmed (spent) on the blockchain.
|
|
104
|
+
* @param {Buffer} parentCoinInfo - The parent coin information.
|
|
105
|
+
* @returns {Promise<boolean>} Whether the coin was confirmed.
|
|
106
|
+
*/
|
|
22
107
|
static waitForConfirmation(parentCoinInfo: Buffer): Promise<boolean>;
|
|
23
108
|
}
|
|
24
109
|
//# sourceMappingURL=FullNodePeer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAO,MAAM,8BAA8B,CAAC;AAgCzD;;GAEG;AACH,qBAAa,YAAY;IAqCH,OAAO,CAAC,IAAI;IAnChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IAGpD,OAAO,CAAC,MAAM,CAAC,aAAa,CAEzB;IAGH,OAAO,CAAC,MAAM,CAAC,cAAc,CAAoC;IAGjE,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkC;IAG5D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAgB;IAG/C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAoC;IAG5D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAoD;IAG9E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAGpB;IAGH,OAAO,CAAC,MAAM,CAAC,YAAY,CAAsC;IAGjE,OAAO,CAAC,MAAM,CAAC,eAAe,CAAa;IAG3C,OAAO;IAEP;;;OAGG;WACW,WAAW,IAAI,YAAY;IAOzC;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxC;;;;OAIG;WACiB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5C;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAuB9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IASjC;;;;OAIG;mBACkB,eAAe;IAoGpC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAS3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAYpC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IA4BnC;;;OAGG;mBACkB,UAAU;IAK/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB;;;OAGG;YACW,WAAW;IAuEzB,OAAO,CAAC,eAAe;IAqDvB;;;OAGG;WACW,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAmB3D;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAS5B;;;;OAIG;WACiB,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
|