@dignetwork/dig-sdk 0.0.1-alpha.9 → 0.0.1-alpha.92
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 +5 -2
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +93 -34
- package/dist/DigNetwork/ContentServer.d.ts +10 -5
- package/dist/DigNetwork/ContentServer.d.ts.map +1 -1
- package/dist/DigNetwork/ContentServer.js +89 -24
- package/dist/DigNetwork/DigNetwork.d.ts +3 -9
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +98 -189
- package/dist/DigNetwork/DigPeer.d.ts +8 -3
- package/dist/DigNetwork/DigPeer.d.ts.map +1 -1
- package/dist/DigNetwork/DigPeer.js +68 -114
- package/dist/DigNetwork/PropagationServer.d.ts +67 -33
- package/dist/DigNetwork/PropagationServer.d.ts.map +1 -1
- package/dist/DigNetwork/PropagationServer.js +484 -363
- package/dist/blockchain/DataStore.d.ts +18 -7
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +78 -98
- package/dist/blockchain/DataStoreSerializer.d.ts +1 -1
- package/dist/blockchain/DataStoreSerializer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.d.ts +9 -1
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +45 -11
- package/dist/blockchain/ServerCoin.d.ts +11 -3
- package/dist/blockchain/ServerCoin.d.ts.map +1 -1
- package/dist/blockchain/ServerCoin.js +51 -17
- package/dist/blockchain/Wallet.d.ts +2 -2
- package/dist/blockchain/Wallet.d.ts.map +1 -1
- package/dist/blockchain/Wallet.js +51 -18
- package/dist/blockchain/coins.d.ts +1 -1
- package/dist/blockchain/coins.d.ts.map +1 -1
- package/dist/blockchain/coins.js +1 -1
- package/dist/types.d.ts +1 -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/FileTransfer.d.ts +47 -0
- package/dist/utils/FileTransfer.d.ts.map +1 -0
- package/dist/utils/FileTransfer.js +209 -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/asyncPool.d.ts +9 -0
- package/dist/utils/asyncPool.d.ts.map +1 -0
- package/dist/utils/asyncPool.js +23 -0
- package/dist/utils/config.d.ts +4 -3
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +9 -5
- package/dist/utils/directoryUtils.d.ts +0 -6
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/dist/utils/directoryUtils.js +30 -11
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +0 -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.map +1 -1
- package/dist/utils/network.js +24 -9
- package/package.json +13 -3
- 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,4 +1,4 @@
|
|
|
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";
|
|
@@ -13,7 +13,7 @@ export declare class DataStore {
|
|
|
13
13
|
serialize(): string;
|
|
14
14
|
static getActiveStore(): Promise<DataStore | undefined>;
|
|
15
15
|
static deserialize(serialized: string): DataStore;
|
|
16
|
-
static from(storeId: string | Buffer): DataStore;
|
|
16
|
+
static from(storeId: string | Buffer, rootHash?: string): DataStore;
|
|
17
17
|
static create(inputs?: CreateStoreUserInputs): Promise<DataStore>;
|
|
18
18
|
private static mint;
|
|
19
19
|
/**
|
|
@@ -38,19 +38,30 @@ export declare class DataStore {
|
|
|
38
38
|
latestHeight: number;
|
|
39
39
|
latestHash: Buffer;
|
|
40
40
|
}>;
|
|
41
|
+
cacheStoreCreationHeight(): Promise<{
|
|
42
|
+
createdAtHeight: number;
|
|
43
|
+
createdAtHash: Buffer;
|
|
44
|
+
}>;
|
|
41
45
|
getCreationHeight(): Promise<{
|
|
42
46
|
createdAtHeight: number;
|
|
43
47
|
createdAtHash: Buffer;
|
|
44
48
|
}>;
|
|
45
49
|
private setCreationHeight;
|
|
46
|
-
getRootHistory(): Promise<RootHistoryItem[]>;
|
|
47
|
-
|
|
48
|
-
validate(): Promise<boolean>;
|
|
50
|
+
getRootHistory(bustCache?: boolean): Promise<RootHistoryItem[]>;
|
|
51
|
+
generateManifestFile(folderPath?: string): Promise<void>;
|
|
49
52
|
getMetaData(): Promise<DataStoreMetadata>;
|
|
50
53
|
isSynced(): Promise<boolean>;
|
|
51
54
|
hasMetaWritePermissions(publicSyntheticKey?: Buffer): Promise<boolean>;
|
|
52
55
|
updateMetadata(metadata: DataStoreMetadata): Promise<DataStoreDriver>;
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Retrieve the file set for a given root hash and validate file integrity.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} rootHash - The root hash to fetch the file set.
|
|
60
|
+
* @returns {Promise<{ fileName: string, file: string }[]>} - An array of unique file objects.
|
|
61
|
+
*/
|
|
62
|
+
getFileSetForRootHash(rootHash: string): Promise<{
|
|
63
|
+
name: string;
|
|
64
|
+
path: string;
|
|
65
|
+
}[]>;
|
|
55
66
|
}
|
|
56
67
|
//# 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,EAAW,MAAM,UAAU,CAAC;AAGpD,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAajD,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAoB;gBAEpB,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;WAiBtD,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;IAW5B,aAAa,IAAI,OAAO,CAAC;QACpC,WAAW,EAAE,eAAe,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAyGW,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;IA4C/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;IA+C3B;;;;;OAKG;IACU,qBAAqB,CAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAyC7C"}
|
|
@@ -6,18 +6,22 @@ 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 node_cache_1 = __importDefault(require("node-cache"));
|
|
22
|
+
const config_2 = require("../utils/config");
|
|
23
|
+
// Initialize the cache with a TTL of 180 seconds (3 minutes)
|
|
24
|
+
const rootHistoryCache = new node_cache_1.default({ stdTTL: 180 });
|
|
21
25
|
const stat = (0, util_1.promisify)(fs_1.default.stat);
|
|
22
26
|
const readdir = (0, util_1.promisify)(fs_1.default.readdir);
|
|
23
27
|
class DataStore {
|
|
@@ -62,12 +66,15 @@ class DataStore {
|
|
|
62
66
|
const parsed = JSON.parse(serialized);
|
|
63
67
|
return new DataStore(parsed.storeId);
|
|
64
68
|
}
|
|
65
|
-
static from(storeId) {
|
|
69
|
+
static from(storeId, rootHash) {
|
|
66
70
|
const existingTreeOptions = {
|
|
67
71
|
storageMode: "local",
|
|
68
72
|
storeDir: config_1.STORE_PATH,
|
|
69
73
|
disableInitialize: true,
|
|
70
74
|
};
|
|
75
|
+
if (rootHash) {
|
|
76
|
+
existingTreeOptions.rootHash = rootHash;
|
|
77
|
+
}
|
|
71
78
|
if (storeId instanceof Buffer) {
|
|
72
79
|
return new DataStore(storeId.toString("hex"), existingTreeOptions);
|
|
73
80
|
}
|
|
@@ -257,22 +264,31 @@ class DataStore {
|
|
|
257
264
|
throw error;
|
|
258
265
|
}
|
|
259
266
|
}
|
|
267
|
+
async cacheStoreCreationHeight() {
|
|
268
|
+
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
269
|
+
const createdAtHeight = await peer.getStoreCreationHeight(Buffer.from(this.storeId, "hex"), null, Buffer.from(config_2.MAIN_NET_GENISES_CHALLENGE, "hex"));
|
|
270
|
+
// Get just before created at height so we can find the coin
|
|
271
|
+
const justBeforeCreatedAtHeight = Number(createdAtHeight) - 1;
|
|
272
|
+
const createdAtHash = await peer.getHeaderHash(justBeforeCreatedAtHeight);
|
|
273
|
+
await this.setCreationHeight(justBeforeCreatedAtHeight, createdAtHash);
|
|
274
|
+
return { createdAtHeight: justBeforeCreatedAtHeight, createdAtHash };
|
|
275
|
+
}
|
|
260
276
|
async getCreationHeight() {
|
|
261
|
-
const defaultHeight = config_1.MIN_HEIGHT;
|
|
262
|
-
const defaultHash = Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex");
|
|
263
277
|
// Initialize the FileCache for the height file
|
|
264
278
|
const fileCache = new FileCache_1.FileCache(`stores/${this.storeId}`);
|
|
265
279
|
// Try to retrieve the cached height information
|
|
266
280
|
const cachedHeightInfo = fileCache.get("height");
|
|
267
281
|
if (!cachedHeightInfo) {
|
|
268
|
-
// If no cache,
|
|
269
|
-
return
|
|
282
|
+
// If no cache, regenerate the cache
|
|
283
|
+
return this.cacheStoreCreationHeight();
|
|
270
284
|
}
|
|
271
285
|
// Parse the cached height and hash values
|
|
272
286
|
const { height, hash } = cachedHeightInfo;
|
|
287
|
+
const defaultHeight = config_1.MIN_HEIGHT;
|
|
288
|
+
const defaultHash = Buffer.from(config_1.MIN_HEIGHT_HEADER_HASH, "hex");
|
|
273
289
|
return {
|
|
274
290
|
createdAtHeight: height || defaultHeight,
|
|
275
|
-
createdAtHash: Buffer.from(hash
|
|
291
|
+
createdAtHash: hash ? Buffer.from(hash, "hex") : defaultHash,
|
|
276
292
|
};
|
|
277
293
|
}
|
|
278
294
|
async setCreationHeight(height, hash) {
|
|
@@ -283,98 +299,53 @@ class DataStore {
|
|
|
283
299
|
hash: hash.toString("hex"),
|
|
284
300
|
});
|
|
285
301
|
}
|
|
286
|
-
async getRootHistory() {
|
|
302
|
+
async getRootHistory(bustCache) {
|
|
303
|
+
if (bustCache) {
|
|
304
|
+
rootHistoryCache.del(this.storeId);
|
|
305
|
+
}
|
|
306
|
+
// Check if the root history is cached for this storeId
|
|
307
|
+
const cachedHistory = rootHistoryCache.get(this.storeId);
|
|
308
|
+
if (cachedHistory) {
|
|
309
|
+
return cachedHistory;
|
|
310
|
+
}
|
|
311
|
+
// Fetch root history from peer if not cached
|
|
287
312
|
const peer = await FullNodePeer_1.FullNodePeer.connect();
|
|
288
313
|
const { createdAtHeight, createdAtHash } = await this.getCreationHeight();
|
|
289
314
|
const { rootHashes, rootHashesTimestamps } = await peer.syncStoreFromLauncherId(Buffer.from(this.storeId, "hex"), createdAtHeight, createdAtHash, true);
|
|
290
315
|
if (!rootHashes) {
|
|
291
316
|
return [];
|
|
292
317
|
}
|
|
293
|
-
|
|
318
|
+
// Build the root history list
|
|
319
|
+
const rootHistory = rootHashes.map((rootHash, index) => ({
|
|
294
320
|
root_hash: rootHash.toString("hex"),
|
|
295
321
|
timestamp: Number(rootHashesTimestamps?.[index].toString()),
|
|
322
|
+
synced: fs_1.default.existsSync(path_1.default.join(config_1.STORE_PATH, this.storeId, `${rootHash.toString("hex")}.dat`)),
|
|
296
323
|
}));
|
|
324
|
+
// Store the root history in the cache
|
|
325
|
+
rootHistoryCache.set(this.storeId, rootHistory);
|
|
326
|
+
return rootHistory;
|
|
297
327
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
328
|
+
// Generates a fresh manifest file based on the current root history
|
|
329
|
+
// and what is currently on disk
|
|
330
|
+
async generateManifestFile(folderPath) {
|
|
331
|
+
if (!folderPath) {
|
|
332
|
+
folderPath = path_1.default.join(config_1.STORE_PATH, this.storeId, "data");
|
|
303
333
|
}
|
|
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
334
|
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;
|
|
335
|
+
// Need this for the dataintegrity tree to work properly
|
|
336
|
+
fs_1.default.writeFileSync(path_1.default.join(folderPath, "manifest.dat"), rootHistory
|
|
337
|
+
.filter((root) => root.synced)
|
|
338
|
+
.map((root) => root.root_hash)
|
|
339
|
+
.join("\n"));
|
|
362
340
|
}
|
|
363
341
|
async getMetaData() {
|
|
364
342
|
const { latestStore } = await this.fetchCoinInfo();
|
|
365
343
|
return latestStore.metadata;
|
|
366
344
|
}
|
|
367
345
|
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;
|
|
346
|
+
const dataStore = await DataStore.from(this.storeId);
|
|
347
|
+
const rootHistory = await dataStore.getRootHistory();
|
|
348
|
+
return !rootHistory.some((root) => !root.synced);
|
|
378
349
|
}
|
|
379
350
|
async hasMetaWritePermissions(publicSyntheticKey) {
|
|
380
351
|
const wallet = await Wallet_1.Wallet.load("default");
|
|
@@ -407,27 +378,36 @@ class DataStore {
|
|
|
407
378
|
}
|
|
408
379
|
return updateStoreResponse.newStore;
|
|
409
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Retrieve the file set for a given root hash and validate file integrity.
|
|
383
|
+
*
|
|
384
|
+
* @param {string} rootHash - The root hash to fetch the file set.
|
|
385
|
+
* @returns {Promise<{ fileName: string, file: string }[]>} - An array of unique file objects.
|
|
386
|
+
*/
|
|
410
387
|
async getFileSetForRootHash(rootHash) {
|
|
411
388
|
const datFilePath = path_1.default.join(config_1.STORE_PATH, this.storeId, `${rootHash}.dat`);
|
|
412
389
|
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);
|
|
390
|
+
// Use a Set to ensure uniqueness
|
|
391
|
+
const filesInvolved = new Set();
|
|
392
|
+
// Iterate over each file and perform an integrity check
|
|
419
393
|
for (const [fileKey, fileData] of Object.entries(datFileContent.files)) {
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
394
|
+
const filePath = (0, hashUtils_1.getFilePathFromSha256)(datFileContent.files[fileKey].sha256, "data");
|
|
395
|
+
// Perform the integrity check
|
|
396
|
+
const integrityCheck = await DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(fileKey, datFileContent.files[fileKey].sha256, datFileContent, rootHash, path_1.default.join(config_1.STORE_PATH, this.storeId, "data"));
|
|
397
|
+
if (integrityCheck) {
|
|
398
|
+
// Add the file to the Set
|
|
399
|
+
filesInvolved.add({
|
|
400
|
+
name: Buffer.from(fileKey, "hex").toString("utf-8"),
|
|
401
|
+
path: filePath,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
console.error((0, colorette_1.red)(`File ${fileKey} failed the integrity check.`));
|
|
406
|
+
throw new Error(`Integrity check failed for file: ${fileKey}. Aborting.`);
|
|
407
|
+
}
|
|
423
408
|
}
|
|
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
|
-
: [];
|
|
409
|
+
// Convert Set to Array and return
|
|
410
|
+
return Array.from(filesInvolved);
|
|
431
411
|
}
|
|
432
412
|
}
|
|
433
413
|
exports.DataStore = DataStore;
|
|
@@ -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,12 +1,20 @@
|
|
|
1
|
-
import { Peer } from "datalayer-driver";
|
|
1
|
+
import { Peer } from "@dignetwork/datalayer-driver";
|
|
2
2
|
export declare class FullNodePeer {
|
|
3
3
|
private static cachedPeer;
|
|
4
4
|
private static memoizedFetchNewPeerIPs;
|
|
5
5
|
private peer;
|
|
6
|
+
private static deprioritizedIps;
|
|
6
7
|
private constructor();
|
|
7
8
|
static connect(): Promise<Peer>;
|
|
8
9
|
private static isPortReachable;
|
|
9
10
|
private static isValidIpAddress;
|
|
11
|
+
/**
|
|
12
|
+
* Retrieves the TRUSTED_FULLNODE IP from the environment
|
|
13
|
+
* and verifies if it is a valid IP address.
|
|
14
|
+
*
|
|
15
|
+
* @returns {string | null} The valid IP address or null if invalid
|
|
16
|
+
*/
|
|
17
|
+
private static getTrustedFullNode;
|
|
10
18
|
private static fetchNewPeerIPs;
|
|
11
19
|
private static getPeerIPs;
|
|
12
20
|
private static createPeerProxy;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAoBpD,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;IAMzD,OAAO;WAIa,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;mBASZ,eAAe;mBA2Df,UAAU;IA2B/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBAkDT,WAAW;IAuGzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
|
|
@@ -7,7 +7,7 @@ exports.FullNodePeer = void 0;
|
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const datalayer_driver_1 = require("datalayer-driver");
|
|
10
|
+
const datalayer_driver_1 = require("@dignetwork/datalayer-driver");
|
|
11
11
|
const chia_server_coin_1 = require("chia-server-coin");
|
|
12
12
|
const promises_1 = require("dns/promises");
|
|
13
13
|
const net_1 = __importDefault(require("net"));
|
|
@@ -16,6 +16,7 @@ const nanospinner_1 = require("nanospinner");
|
|
|
16
16
|
const config_1 = require("../utils/config");
|
|
17
17
|
const FULLNODE_PORT = 8444;
|
|
18
18
|
const LOCALHOST = "127.0.0.1";
|
|
19
|
+
const CHIA_NODES_HOST = "chia-nodes";
|
|
19
20
|
const DNS_HOSTS = [
|
|
20
21
|
"dns-introducer.chia.net",
|
|
21
22
|
"chia.ctrlaltdel.ch",
|
|
@@ -48,19 +49,38 @@ class FullNodePeer {
|
|
|
48
49
|
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
50
|
return ipv4Regex.test(ip);
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves the TRUSTED_FULLNODE IP from the environment
|
|
54
|
+
* and verifies if it is a valid IP address.
|
|
55
|
+
*
|
|
56
|
+
* @returns {string | null} The valid IP address or null if invalid
|
|
57
|
+
*/
|
|
58
|
+
static getTrustedFullNode() {
|
|
52
59
|
const trustedNodeIp = process.env.TRUSTED_FULLNODE || null;
|
|
60
|
+
if (trustedNodeIp && FullNodePeer.isValidIpAddress(trustedNodeIp)) {
|
|
61
|
+
return trustedNodeIp;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
static async fetchNewPeerIPs() {
|
|
66
|
+
const trustedNodeIp = FullNodePeer.getTrustedFullNode();
|
|
53
67
|
const priorityIps = [];
|
|
54
|
-
// Prioritize trustedNodeIp
|
|
68
|
+
// Prioritize trustedNodeIp unless it's deprioritized
|
|
55
69
|
if (trustedNodeIp &&
|
|
56
|
-
FullNodePeer.
|
|
70
|
+
!FullNodePeer.deprioritizedIps.has(trustedNodeIp) &&
|
|
57
71
|
(await FullNodePeer.isPortReachable(trustedNodeIp, FULLNODE_PORT))) {
|
|
58
72
|
priorityIps.push(trustedNodeIp);
|
|
59
73
|
}
|
|
60
|
-
// Prioritize LOCALHOST
|
|
61
|
-
if (
|
|
74
|
+
// Prioritize LOCALHOST unless it's deprioritized
|
|
75
|
+
if (!FullNodePeer.deprioritizedIps.has(LOCALHOST) &&
|
|
76
|
+
(await FullNodePeer.isPortReachable(LOCALHOST, FULLNODE_PORT))) {
|
|
62
77
|
priorityIps.push(LOCALHOST);
|
|
63
78
|
}
|
|
79
|
+
// Prioritize CHIA_NODES_HOST unless it's deprioritized
|
|
80
|
+
if (!FullNodePeer.deprioritizedIps.has(CHIA_NODES_HOST) &&
|
|
81
|
+
(await FullNodePeer.isPortReachable(CHIA_NODES_HOST, FULLNODE_PORT))) {
|
|
82
|
+
priorityIps.push(CHIA_NODES_HOST);
|
|
83
|
+
}
|
|
64
84
|
if (priorityIps.length > 0) {
|
|
65
85
|
return priorityIps;
|
|
66
86
|
}
|
|
@@ -102,6 +122,8 @@ class FullNodePeer {
|
|
|
102
122
|
}
|
|
103
123
|
// @ts-ignore
|
|
104
124
|
if (FullNodePeer.memoizedFetchNewPeerIPs?.cache?.clear) {
|
|
125
|
+
// Clear cache and reset deprioritized IPs when cache is cleared
|
|
126
|
+
FullNodePeer.deprioritizedIps.clear();
|
|
105
127
|
// @ts-ignore
|
|
106
128
|
FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
|
|
107
129
|
}
|
|
@@ -139,6 +161,8 @@ class FullNodePeer {
|
|
|
139
161
|
FullNodePeer.cachedPeer = null;
|
|
140
162
|
// @ts-ignore
|
|
141
163
|
FullNodePeer.memoizedFetchNewPeerIPs.cache.clear();
|
|
164
|
+
FullNodePeer.deprioritizedIps.clear();
|
|
165
|
+
console.info(`Fullnode Peer error, reconnecting to a new peer...`);
|
|
142
166
|
const newPeer = await FullNodePeer.getBestPeer();
|
|
143
167
|
return newPeer[prop](...args);
|
|
144
168
|
}
|
|
@@ -167,8 +191,16 @@ class FullNodePeer {
|
|
|
167
191
|
const trustedNodeIp = process.env.TRUSTED_FULLNODE || null;
|
|
168
192
|
const peers = await Promise.all(peerIPs.map(async (ip) => {
|
|
169
193
|
if (ip) {
|
|
194
|
+
// Allow override of the trusted fullnode port if the override exists
|
|
195
|
+
let port = FULLNODE_PORT;
|
|
196
|
+
if (trustedNodeIp && ip === trustedNodeIp) {
|
|
197
|
+
const trustedFullNodePort = process.env.TRUSTED_FULLNODE_PORT;
|
|
198
|
+
if (trustedFullNodePort) {
|
|
199
|
+
port = parseInt(trustedFullNodePort);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
170
202
|
try {
|
|
171
|
-
const peer = await datalayer_driver_1.Peer.new(`${ip}:${
|
|
203
|
+
const peer = await datalayer_driver_1.Peer.new(`${ip}:${port}`, false, certFile, keyFile);
|
|
172
204
|
return FullNodePeer.createPeerProxy(peer);
|
|
173
205
|
}
|
|
174
206
|
catch (error) {
|
|
@@ -194,16 +226,17 @@ class FullNodePeer {
|
|
|
194
226
|
throw new Error("No valid peak heights obtained from any peer.");
|
|
195
227
|
}
|
|
196
228
|
const highestPeak = Math.max(...validHeights);
|
|
197
|
-
// Prioritize LOCALHOST and
|
|
229
|
+
// Prioritize LOCALHOST, TRUSTED_NODE_IP, and CHIA_NODES_HOST if they have the highest peak height
|
|
198
230
|
let bestPeerIndex = validHeights.findIndex((height, index) => height === highestPeak &&
|
|
199
|
-
(peerIPs[index]
|
|
200
|
-
|
|
231
|
+
!FullNodePeer.deprioritizedIps.has(peerIPs[index]) && // Exclude deprioritized IPs
|
|
232
|
+
(peerIPs[index] === LOCALHOST || peerIPs[index] === trustedNodeIp || peerIPs[index] === CHIA_NODES_HOST));
|
|
233
|
+
// If LOCALHOST, TRUSTED_NODE_IP, or CHIA_NODES_HOST don't have the highest peak, select any peer with the highest peak
|
|
201
234
|
if (bestPeerIndex === -1) {
|
|
202
235
|
bestPeerIndex = validHeights.findIndex((height) => height === highestPeak);
|
|
203
236
|
}
|
|
204
237
|
const bestPeer = peers[bestPeerIndex];
|
|
205
238
|
FullNodePeer.cachedPeer = { peer: bestPeer, timestamp: now };
|
|
206
|
-
console.log(`Using Peer: ${peerIPs[bestPeerIndex]}`);
|
|
239
|
+
console.log(`Using Fullnode Peer: ${peerIPs[bestPeerIndex]}`);
|
|
207
240
|
return bestPeer;
|
|
208
241
|
}
|
|
209
242
|
getPeer() {
|
|
@@ -230,6 +263,7 @@ class FullNodePeer {
|
|
|
230
263
|
}
|
|
231
264
|
exports.FullNodePeer = FullNodePeer;
|
|
232
265
|
FullNodePeer.cachedPeer = null;
|
|
266
|
+
FullNodePeer.deprioritizedIps = new Set(); // New set for deprioritized IPs
|
|
233
267
|
(() => {
|
|
234
268
|
FullNodePeer.memoizedFetchNewPeerIPs = (0, lodash_1.memoize)(FullNodePeer.fetchNewPeerIPs);
|
|
235
269
|
})();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ServerCoin as ServerCoinDriver } from "datalayer-driver";
|
|
1
|
+
import { ServerCoin as ServerCoinDriver } from "@dignetwork/datalayer-driver";
|
|
2
2
|
import { NconfManager } from "../utils/NconfManager";
|
|
3
3
|
export declare class ServerCoin {
|
|
4
4
|
private storeId;
|
|
@@ -8,14 +8,22 @@ export declare class ServerCoin {
|
|
|
8
8
|
saveServerCoinData(serverCoin: ServerCoinDriver, epoch: number, peerIp: string): Promise<void>;
|
|
9
9
|
melt(epoch: number, peerIp: string): Promise<void>;
|
|
10
10
|
private removeServerCoinData;
|
|
11
|
+
getAllEpochPeers(epoch: number, blacklist?: string[]): Promise<string[]>;
|
|
12
|
+
getActiveEpochPeers(blacklist?: string[]): Promise<string[]>;
|
|
11
13
|
sampleCurrentEpoch(sampleSize?: number, blacklist?: string[]): Promise<string[]>;
|
|
12
14
|
sampleServerCoinsByEpoch(epoch: number, sampleSize?: number, blacklist?: string[]): Promise<string[]>;
|
|
13
|
-
static getCurrentEpoch():
|
|
15
|
+
static getCurrentEpoch(): {
|
|
16
|
+
epoch: number;
|
|
17
|
+
round: number;
|
|
18
|
+
};
|
|
14
19
|
ensureServerCoinExists(peerIp: string): Promise<void>;
|
|
15
20
|
meltOutdatedEpochs(peerIp: string): Promise<void>;
|
|
16
21
|
static meltUntrackedStoreCoins(): Promise<void>;
|
|
17
22
|
private getServerCoinsForStore;
|
|
18
23
|
hasEpochCoinBeenCreated(currentEpoch: number, peerIp: string): Promise<boolean>;
|
|
19
|
-
static
|
|
24
|
+
static calculateEpochAndRound(currentTimestampUTC: Date): {
|
|
25
|
+
epoch: number;
|
|
26
|
+
round: number;
|
|
27
|
+
};
|
|
20
28
|
}
|
|
21
29
|
//# sourceMappingURL=ServerCoin.d.ts.map
|
|
@@ -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,
|
|
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;AAWrD,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;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;IAerB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0C5E,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;IAiDrD,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"}
|