@dignetwork/dig-sdk 0.0.1-alpha.99 → 0.2.0
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/LICENSE +21 -7
- package/README.md +412 -0
- package/dist/adapters.cjs +356 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +20 -0
- package/dist/adapters.d.ts +20 -0
- package/dist/adapters.js +347 -0
- package/dist/adapters.js.map +1 -0
- package/dist/dev-shim-DfKRA1ok.d.cts +128 -0
- package/dist/dev-shim-DfKRA1ok.d.ts +128 -0
- package/dist/dig-client-entry-ChYxUvBn.d.cts +372 -0
- package/dist/dig-client-entry-ChYxUvBn.d.ts +372 -0
- package/dist/dig-client.cjs +443 -0
- package/dist/dig-client.cjs.map +1 -0
- package/dist/dig-client.d.cts +1 -0
- package/dist/dig-client.d.ts +1 -0
- package/dist/dig-client.js +432 -0
- package/dist/dig-client.js.map +1 -0
- package/dist/index.cjs +1186 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +502 -0
- package/dist/index.d.ts +502 -5
- package/dist/index.js +1156 -19
- package/dist/index.js.map +1 -0
- package/dist/next.cjs +369 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +23 -0
- package/dist/next.d.ts +23 -0
- package/dist/next.js +362 -0
- package/dist/next.js.map +1 -0
- package/dist/spend.cjs +14 -0
- package/dist/spend.cjs.map +1 -0
- package/dist/spend.d.cts +1 -0
- package/dist/spend.d.ts +1 -0
- package/dist/spend.js +3 -0
- package/dist/spend.js.map +1 -0
- package/dist/vite.cjs +381 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +35 -0
- package/dist/vite.d.ts +35 -0
- package/dist/vite.js +375 -0
- package/dist/vite.js.map +1 -0
- package/package.json +87 -59
- package/vendor/PROVENANCE.md +102 -0
- package/vendor/dig_client.d.ts +150 -0
- package/vendor/dig_client.mjs +805 -0
- package/vendor/dig_client_bg.wasm +0 -0
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts +0 -163
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +0 -1
- package/dist/DataIntegrityTree/DataIntegrityTree.js +0 -707
- package/dist/DataIntegrityTree/DataLayerError.d.ts +0 -6
- package/dist/DataIntegrityTree/DataLayerError.d.ts.map +0 -1
- package/dist/DataIntegrityTree/DataLayerError.js +0 -17
- package/dist/DataIntegrityTree/index.d.ts +0 -2
- package/dist/DataIntegrityTree/index.d.ts.map +0 -1
- package/dist/DataIntegrityTree/index.js +0 -17
- package/dist/DigNetwork/ContentServer.d.ts +0 -31
- package/dist/DigNetwork/ContentServer.d.ts.map +0 -1
- package/dist/DigNetwork/ContentServer.js +0 -176
- package/dist/DigNetwork/DigChallenge.d.ts +0 -82
- package/dist/DigNetwork/DigChallenge.d.ts.map +0 -1
- package/dist/DigNetwork/DigChallenge.js +0 -144
- package/dist/DigNetwork/DigNetwork.d.ts +0 -14
- package/dist/DigNetwork/DigNetwork.d.ts.map +0 -1
- package/dist/DigNetwork/DigNetwork.js +0 -169
- package/dist/DigNetwork/DigPeer.d.ts +0 -24
- package/dist/DigNetwork/DigPeer.d.ts.map +0 -1
- package/dist/DigNetwork/DigPeer.js +0 -118
- package/dist/DigNetwork/IncentiveServer.d.ts +0 -13
- package/dist/DigNetwork/IncentiveServer.d.ts.map +0 -1
- package/dist/DigNetwork/IncentiveServer.js +0 -76
- package/dist/DigNetwork/PropagationServer.d.ts +0 -81
- package/dist/DigNetwork/PropagationServer.d.ts.map +0 -1
- package/dist/DigNetwork/PropagationServer.js +0 -563
- package/dist/DigNetwork/index.d.ts +0 -4
- package/dist/DigNetwork/index.d.ts.map +0 -1
- package/dist/DigNetwork/index.js +0 -19
- package/dist/blockchain/DataStore.d.ts +0 -67
- package/dist/blockchain/DataStore.d.ts.map +0 -1
- package/dist/blockchain/DataStore.js +0 -413
- package/dist/blockchain/DataStoreSerializer.d.ts +0 -52
- package/dist/blockchain/DataStoreSerializer.d.ts.map +0 -1
- package/dist/blockchain/DataStoreSerializer.js +0 -109
- package/dist/blockchain/FullNodePeer.d.ts +0 -31
- package/dist/blockchain/FullNodePeer.d.ts.map +0 -1
- package/dist/blockchain/FullNodePeer.js +0 -295
- package/dist/blockchain/ServerCoin.d.ts +0 -29
- package/dist/blockchain/ServerCoin.d.ts.map +0 -1
- package/dist/blockchain/ServerCoin.js +0 -324
- package/dist/blockchain/Wallet.d.ts +0 -28
- package/dist/blockchain/Wallet.d.ts.map +0 -1
- package/dist/blockchain/Wallet.js +0 -253
- package/dist/blockchain/coins.d.ts +0 -6
- package/dist/blockchain/coins.d.ts.map +0 -1
- package/dist/blockchain/coins.js +0 -102
- package/dist/blockchain/index.d.ts +0 -8
- package/dist/blockchain/index.d.ts.map +0 -1
- package/dist/blockchain/index.js +0 -36
- package/dist/index.d.ts.map +0 -1
- package/dist/prompts.d.ts +0 -21
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -149
- package/dist/types.d.ts +0 -64
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/utils/ContentScanner.d.ts +0 -63
- package/dist/utils/ContentScanner.d.ts.map +0 -1
- package/dist/utils/ContentScanner.js +0 -175
- package/dist/utils/Environment.d.ts +0 -18
- package/dist/utils/Environment.d.ts.map +0 -1
- package/dist/utils/Environment.js +0 -85
- package/dist/utils/FileCache.d.ts +0 -11
- package/dist/utils/FileCache.d.ts.map +0 -1
- package/dist/utils/FileCache.js +0 -58
- package/dist/utils/FileTransfer.d.ts +0 -47
- package/dist/utils/FileTransfer.d.ts.map +0 -1
- package/dist/utils/FileTransfer.js +0 -209
- package/dist/utils/NconfManager.d.ts +0 -11
- package/dist/utils/NconfManager.d.ts.map +0 -1
- package/dist/utils/NconfManager.js +0 -56
- package/dist/utils/StoreArchiveManager.d.ts +0 -45
- package/dist/utils/StoreArchiveManager.d.ts.map +0 -1
- package/dist/utils/StoreArchiveManager.js +0 -153
- package/dist/utils/asyncPool.d.ts +0 -9
- package/dist/utils/asyncPool.d.ts.map +0 -1
- package/dist/utils/asyncPool.js +0 -23
- package/dist/utils/config.d.ts +0 -68
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js +0 -223
- package/dist/utils/connectionUtils.d.ts +0 -10
- package/dist/utils/connectionUtils.d.ts.map +0 -1
- package/dist/utils/connectionUtils.js +0 -18
- package/dist/utils/credentialsUtils.d.ts +0 -8
- package/dist/utils/credentialsUtils.d.ts.map +0 -1
- package/dist/utils/credentialsUtils.js +0 -122
- package/dist/utils/directoryUtils.d.ts +0 -9
- package/dist/utils/directoryUtils.d.ts.map +0 -1
- package/dist/utils/directoryUtils.js +0 -110
- package/dist/utils/encryption.d.ts +0 -8
- package/dist/utils/encryption.d.ts.map +0 -1
- package/dist/utils/encryption.js +0 -55
- package/dist/utils/hashUtils.d.ts +0 -8
- package/dist/utils/hashUtils.d.ts.map +0 -1
- package/dist/utils/hashUtils.js +0 -44
- package/dist/utils/index.d.ts +0 -14
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -29
- package/dist/utils/merkle.d.ts +0 -2
- package/dist/utils/merkle.d.ts.map +0 -1
- package/dist/utils/merkle.js +0 -28
- package/dist/utils/network.d.ts +0 -2
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -54
- package/dist/utils/spinnerUtils.d.ts +0 -2
- package/dist/utils/spinnerUtils.d.ts.map +0 -1
- package/dist/utils/spinnerUtils.js +0 -47
- package/dist/utils/ssl.d.ts +0 -5
- package/dist/utils/ssl.d.ts.map +0 -1
- package/dist/utils/ssl.js +0 -50
- package/dist/utils/validationUtils.d.ts +0 -9
- package/dist/utils/validationUtils.d.ts.map +0 -1
- package/dist/utils/validationUtils.js +0 -55
|
@@ -1,707 +0,0 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.DataIntegrityTree = void 0;
|
|
30
|
-
const fs = __importStar(require("fs"));
|
|
31
|
-
const path = __importStar(require("path"));
|
|
32
|
-
const crypto = __importStar(require("crypto"));
|
|
33
|
-
const zlib = __importStar(require("zlib"));
|
|
34
|
-
const crypto_js_1 = require("crypto-js");
|
|
35
|
-
const merkletreejs_1 = require("merkletreejs");
|
|
36
|
-
const util_1 = require("util");
|
|
37
|
-
const DataLayerError_1 = __importDefault(require("./DataLayerError"));
|
|
38
|
-
const unlink = (0, util_1.promisify)(fs.unlink);
|
|
39
|
-
/**
|
|
40
|
-
* Convert a string to hexadecimal representation.
|
|
41
|
-
* @param str - The input string.
|
|
42
|
-
* @returns The hexadecimal representation of the input string.
|
|
43
|
-
*/
|
|
44
|
-
const toHex = (str) => {
|
|
45
|
-
return Buffer.from(str).toString("hex");
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* Remove empty directories recursively.
|
|
49
|
-
* @param dir - The directory path.
|
|
50
|
-
*/
|
|
51
|
-
const removeEmptyDirectories = (dir) => {
|
|
52
|
-
if (fs.existsSync(dir)) {
|
|
53
|
-
const files = fs.readdirSync(dir);
|
|
54
|
-
if (files.length === 0) {
|
|
55
|
-
fs.rmdirSync(dir);
|
|
56
|
-
const parentDir = path.dirname(dir);
|
|
57
|
-
removeEmptyDirectories(parentDir);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Check if a string is a valid hexadecimal string.
|
|
63
|
-
* @param str - The string to validate.
|
|
64
|
-
* @returns True if the string is a valid hex string, false otherwise.
|
|
65
|
-
*/
|
|
66
|
-
const isHexString = (str) => {
|
|
67
|
-
return /^[0-9a-fA-F]+$/.test(str) && str.length % 2 === 0;
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* DataStoreManager class to manage Merkle tree operations.
|
|
71
|
-
*/
|
|
72
|
-
class DataIntegrityTree {
|
|
73
|
-
constructor(storeId, options = {}) {
|
|
74
|
-
if (!isHexString(storeId) || storeId.length !== 64) {
|
|
75
|
-
throw new Error("storeId must be a 64 char hex string");
|
|
76
|
-
}
|
|
77
|
-
this.storeId = storeId;
|
|
78
|
-
this.storeBaseDir = options.storeDir || "./";
|
|
79
|
-
if (!fs.existsSync(this.storeBaseDir)) {
|
|
80
|
-
fs.mkdirSync(this.storeBaseDir, { recursive: true });
|
|
81
|
-
}
|
|
82
|
-
if (options.storageMode === "unified") {
|
|
83
|
-
this.dataDir = path.join(this.storeBaseDir, "data");
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
this.dataDir = path.join(this.storeBaseDir, this.storeId, "data");
|
|
87
|
-
}
|
|
88
|
-
this.storeDir = path.join(this.storeBaseDir, this.storeId);
|
|
89
|
-
if (!fs.existsSync(this.storeDir)) {
|
|
90
|
-
fs.mkdirSync(this.storeDir, { recursive: true });
|
|
91
|
-
}
|
|
92
|
-
if (!fs.existsSync(this.dataDir)) {
|
|
93
|
-
fs.mkdirSync(this.dataDir, { recursive: true });
|
|
94
|
-
}
|
|
95
|
-
this.files = new Map();
|
|
96
|
-
if (options.rootHash) {
|
|
97
|
-
if (fs.existsSync(path.join(this.storeDir, `${options.rootHash}.dat`))) {
|
|
98
|
-
this.tree = this.deserializeTree(options.rootHash);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
throw new DataLayerError_1.default(404, `Root hash ${options.rootHash} not found`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
this.tree = this._loadLatestTree();
|
|
106
|
-
}
|
|
107
|
-
// Commit the empty Merkle tree immediately upon creation
|
|
108
|
-
if (!options.disableInitialize && this.tree.getLeafCount() === 0) {
|
|
109
|
-
this.commit();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
static from(storeId, options) {
|
|
113
|
-
return new DataIntegrityTree(storeId, {
|
|
114
|
-
...options,
|
|
115
|
-
disableInitialize: true,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Load the manifest file.
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
_loadManifest() {
|
|
123
|
-
const manifestPath = path.join(this.storeDir, "manifest.dat");
|
|
124
|
-
if (fs.existsSync(manifestPath)) {
|
|
125
|
-
return fs.readFileSync(manifestPath, "utf8").trim().split("\n");
|
|
126
|
-
}
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Load the latest tree from the manifest file.
|
|
131
|
-
* @private
|
|
132
|
-
*/
|
|
133
|
-
_loadLatestTree() {
|
|
134
|
-
const manifest = this._loadManifest();
|
|
135
|
-
if (manifest.length > 0) {
|
|
136
|
-
const latestRootHash = manifest[manifest.length - 1];
|
|
137
|
-
if (latestRootHash && isHexString(latestRootHash)) {
|
|
138
|
-
return this.deserializeTree(latestRootHash);
|
|
139
|
-
}
|
|
140
|
-
return new merkletreejs_1.MerkleTree([], crypto_js_1.SHA256, { sortPairs: true });
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
return new merkletreejs_1.MerkleTree([], crypto_js_1.SHA256, { sortPairs: true });
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Save a binary stream to the store's data directory.
|
|
148
|
-
* @param sha256 - The SHA-256 hash of the buffer.
|
|
149
|
-
* @returns The write stream for the file.
|
|
150
|
-
*/
|
|
151
|
-
_createWriteStream(sha256) {
|
|
152
|
-
const subDirs = sha256.match(/.{1,2}/g) || [];
|
|
153
|
-
const fileDir = path.join(this.dataDir, ...subDirs.slice(0, -1));
|
|
154
|
-
const fileName = subDirs[subDirs.length - 1];
|
|
155
|
-
const fileSavePath = path.join(fileDir, fileName);
|
|
156
|
-
if (!fs.existsSync(fileDir)) {
|
|
157
|
-
fs.mkdirSync(fileDir, { recursive: true });
|
|
158
|
-
}
|
|
159
|
-
return fs.createWriteStream(fileSavePath);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Stream file from one path to another.
|
|
163
|
-
* @param src - The source file path.
|
|
164
|
-
* @param dest - The destination file path.
|
|
165
|
-
*/
|
|
166
|
-
async _streamFile(src, dest) {
|
|
167
|
-
return new Promise((resolve, reject) => {
|
|
168
|
-
const readStream = fs.createReadStream(src);
|
|
169
|
-
const writeStream = fs.createWriteStream(dest);
|
|
170
|
-
readStream.pipe(writeStream);
|
|
171
|
-
writeStream.on("finish", resolve);
|
|
172
|
-
writeStream.on("error", reject);
|
|
173
|
-
readStream.on("error", reject);
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Upsert a key with a binary stream to the Merkle tree.
|
|
178
|
-
* Compresses the file, calculates the SHA-256 of the uncompressed file, and stores it.
|
|
179
|
-
* @param readStream - The binary data stream.
|
|
180
|
-
* @param key - The hexadecimal key for the binary data.
|
|
181
|
-
*/
|
|
182
|
-
async upsertKey(readStream, key) {
|
|
183
|
-
if (!isHexString(key)) {
|
|
184
|
-
throw new Error(`key must be a valid hex string: ${key}`);
|
|
185
|
-
}
|
|
186
|
-
const uncompressedHash = crypto.createHash("sha256");
|
|
187
|
-
const gzip = zlib.createGzip();
|
|
188
|
-
let sha256;
|
|
189
|
-
const tempDir = path.join(this.storeDir, "tmp");
|
|
190
|
-
if (!fs.existsSync(tempDir)) {
|
|
191
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
192
|
-
}
|
|
193
|
-
const tempFilePath = path.join(tempDir, `${crypto.randomUUID()}.gz`);
|
|
194
|
-
return new Promise((resolve, reject) => {
|
|
195
|
-
const tempWriteStream = fs.createWriteStream(tempFilePath);
|
|
196
|
-
readStream.on("data", (chunk) => {
|
|
197
|
-
uncompressedHash.update(chunk);
|
|
198
|
-
});
|
|
199
|
-
readStream.pipe(gzip).pipe(tempWriteStream);
|
|
200
|
-
tempWriteStream.on("finish", async () => {
|
|
201
|
-
sha256 = uncompressedHash.digest("hex");
|
|
202
|
-
const finalWriteStream = this._createWriteStream(sha256);
|
|
203
|
-
const finalPath = finalWriteStream.path;
|
|
204
|
-
// Ensure the directory exists before copying the file
|
|
205
|
-
const finalDir = path.dirname(finalPath);
|
|
206
|
-
if (!fs.existsSync(finalDir)) {
|
|
207
|
-
fs.mkdirSync(finalDir, { recursive: true });
|
|
208
|
-
}
|
|
209
|
-
try {
|
|
210
|
-
await this._streamFile(tempFilePath, finalPath);
|
|
211
|
-
await unlink(tempFilePath);
|
|
212
|
-
const combinedHash = crypto
|
|
213
|
-
.createHash("sha256")
|
|
214
|
-
.update(`${key}/${sha256}`)
|
|
215
|
-
.digest("hex");
|
|
216
|
-
if (Array.from(this.files.values()).some((file) => file.hash === combinedHash)) {
|
|
217
|
-
console.log(`No changes detected for key: ${key}`);
|
|
218
|
-
return resolve();
|
|
219
|
-
}
|
|
220
|
-
if (this.files.has(key)) {
|
|
221
|
-
this.deleteKey(key);
|
|
222
|
-
}
|
|
223
|
-
console.log(`Inserted key: ${key}`);
|
|
224
|
-
this.files.set(key, {
|
|
225
|
-
hash: combinedHash,
|
|
226
|
-
sha256: sha256,
|
|
227
|
-
});
|
|
228
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
229
|
-
this._rebuildTree();
|
|
230
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
231
|
-
resolve();
|
|
232
|
-
}
|
|
233
|
-
catch (err) {
|
|
234
|
-
reject(err);
|
|
235
|
-
}
|
|
236
|
-
tempWriteStream.end();
|
|
237
|
-
finalWriteStream.end();
|
|
238
|
-
});
|
|
239
|
-
tempWriteStream.on("error", (err) => {
|
|
240
|
-
tempWriteStream.end();
|
|
241
|
-
reject(err);
|
|
242
|
-
});
|
|
243
|
-
readStream.on("error", (err) => {
|
|
244
|
-
reject(err);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Delete a key from the Merkle tree.
|
|
250
|
-
* @param key - The hexadecimal key to delete.
|
|
251
|
-
*/
|
|
252
|
-
deleteKey(key) {
|
|
253
|
-
if (!isHexString(key)) {
|
|
254
|
-
throw new Error("key must be a valid hex string");
|
|
255
|
-
}
|
|
256
|
-
if (this.files.has(key)) {
|
|
257
|
-
this.files.delete(key);
|
|
258
|
-
this._rebuildTree();
|
|
259
|
-
console.log(`Deleted key: ${key}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* List all keys in the Merkle tree.
|
|
264
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
265
|
-
* @returns The list of keys.
|
|
266
|
-
*/
|
|
267
|
-
listKeys(rootHash = null) {
|
|
268
|
-
if (rootHash && !isHexString(rootHash)) {
|
|
269
|
-
throw new Error("rootHash must be a valid hex string");
|
|
270
|
-
}
|
|
271
|
-
if (rootHash) {
|
|
272
|
-
const tree = this.deserializeTree(rootHash);
|
|
273
|
-
// @ts-ignore
|
|
274
|
-
return Array.from(tree.files.keys());
|
|
275
|
-
}
|
|
276
|
-
return Array.from(this.files.keys());
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Rebuild the Merkle tree from the current files.
|
|
280
|
-
* @private
|
|
281
|
-
*/
|
|
282
|
-
_rebuildTree() {
|
|
283
|
-
const leaves = Array.from(this.files.values()).map(({ hash }) => Buffer.from(hash, "hex"));
|
|
284
|
-
this.tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Get the root of the Merkle tree.
|
|
288
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
289
|
-
* @returns The Merkle root.
|
|
290
|
-
*/
|
|
291
|
-
getRoot() {
|
|
292
|
-
return this.tree.getRoot().toString("hex");
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Serialize the Merkle tree to a JSON object.
|
|
296
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
297
|
-
* @returns The serialized Merkle tree.
|
|
298
|
-
*/
|
|
299
|
-
serialize(rootHash = null) {
|
|
300
|
-
if (rootHash && !isHexString(rootHash)) {
|
|
301
|
-
throw new Error("rootHash must be a valid hex string");
|
|
302
|
-
}
|
|
303
|
-
if (rootHash) {
|
|
304
|
-
const tree = this.deserializeTree(rootHash);
|
|
305
|
-
return {
|
|
306
|
-
root: tree.getRoot().toString("hex"),
|
|
307
|
-
leaves: tree.getLeaves().map((leaf) => leaf.toString("hex")),
|
|
308
|
-
// @ts-ignore
|
|
309
|
-
files: Object.fromEntries(tree.files),
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
return {
|
|
313
|
-
root: this.getRoot(),
|
|
314
|
-
leaves: this.tree.getLeaves().map((leaf) => leaf.toString("hex")),
|
|
315
|
-
files: Object.fromEntries(this.files),
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
static getRootOfForeignTree(treeData) {
|
|
319
|
-
const leaves = treeData.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
320
|
-
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
321
|
-
return tree.getRoot().toString("hex");
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Deserialize a JSON object to a Merkle tree.
|
|
325
|
-
* @param rootHash - The root hash of the tree.
|
|
326
|
-
* @returns The deserialized Merkle tree.
|
|
327
|
-
*/
|
|
328
|
-
deserializeTree(rootHash) {
|
|
329
|
-
if (!isHexString(rootHash)) {
|
|
330
|
-
throw new Error("rootHash must be a valid hex string");
|
|
331
|
-
}
|
|
332
|
-
const treeFilePath = path.join(this.storeDir, `${rootHash}.dat`);
|
|
333
|
-
if (!fs.existsSync(treeFilePath)) {
|
|
334
|
-
throw new Error(`Tree file ${treeFilePath} does not exist`);
|
|
335
|
-
}
|
|
336
|
-
const data = JSON.parse(fs.readFileSync(treeFilePath, "utf8"));
|
|
337
|
-
const leaves = data.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
338
|
-
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
339
|
-
// @ts-ignore
|
|
340
|
-
tree.files = new Map(Object.entries(data.files).map(([key, value]) => [
|
|
341
|
-
key,
|
|
342
|
-
{ hash: value.hash, sha256: value.sha256 },
|
|
343
|
-
]));
|
|
344
|
-
// @ts-ignore
|
|
345
|
-
this.files = tree.files;
|
|
346
|
-
return tree;
|
|
347
|
-
}
|
|
348
|
-
appendRootHashToManifest(rootHash) {
|
|
349
|
-
const manifestPath = path.join(this.storeDir, "manifest.dat");
|
|
350
|
-
// Read the current manifest file
|
|
351
|
-
const manifestContent = fs.existsSync(manifestPath)
|
|
352
|
-
? fs.readFileSync(manifestPath, "utf-8").trim().split("\n")
|
|
353
|
-
: [];
|
|
354
|
-
// Check if the last entry is the same as the rootHash to avoid duplicates
|
|
355
|
-
const latestRootHash = manifestContent.length > 0
|
|
356
|
-
? manifestContent[manifestContent.length - 1]
|
|
357
|
-
: null;
|
|
358
|
-
if (latestRootHash !== rootHash) {
|
|
359
|
-
// Append the new rootHash if it is not the same as the last one
|
|
360
|
-
fs.appendFileSync(manifestPath, `${rootHash}\n`);
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
console.log(`Root hash ${rootHash} is already at the end of the file. Skipping append.`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Commit the current state of the Merkle tree.
|
|
368
|
-
*/
|
|
369
|
-
commit() {
|
|
370
|
-
const emptyRootHash = "0000000000000000000000000000000000000000000000000000000000000000";
|
|
371
|
-
const rootHash = this.tree.getLeafCount() === 0 ? emptyRootHash : this.getRoot();
|
|
372
|
-
const manifest = this._loadManifest();
|
|
373
|
-
const latestRootHash = manifest.length > 0 ? manifest[manifest.length - 1] : null;
|
|
374
|
-
if (rootHash === latestRootHash && rootHash !== emptyRootHash) {
|
|
375
|
-
console.log("No changes to commit. Aborting commit.");
|
|
376
|
-
return undefined;
|
|
377
|
-
}
|
|
378
|
-
this.appendRootHashToManifest(rootHash);
|
|
379
|
-
const treeFilePath = path.join(this.storeDir, `${rootHash}.dat`);
|
|
380
|
-
if (!fs.existsSync(path.dirname(treeFilePath))) {
|
|
381
|
-
fs.mkdirSync(path.dirname(treeFilePath), { recursive: true });
|
|
382
|
-
}
|
|
383
|
-
const serializedTree = this.serialize();
|
|
384
|
-
if (rootHash === emptyRootHash) {
|
|
385
|
-
serializedTree.root = emptyRootHash;
|
|
386
|
-
}
|
|
387
|
-
fs.writeFileSync(treeFilePath, JSON.stringify(serializedTree));
|
|
388
|
-
console.log(`Committed new root`);
|
|
389
|
-
console.log(this.tree.toString());
|
|
390
|
-
return rootHash;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Clear pending changes and revert to the latest committed state.
|
|
394
|
-
*/
|
|
395
|
-
clearPendingRoot() {
|
|
396
|
-
this.tree = this._loadLatestTree();
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Check if a file exists for a given key.
|
|
400
|
-
* @param hexKey - The hexadecimal key of the file.
|
|
401
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
402
|
-
* @returns A boolean indicating if the file exists.
|
|
403
|
-
*/
|
|
404
|
-
hasKey(hexKey, rootHash = null) {
|
|
405
|
-
if (!isHexString(hexKey)) {
|
|
406
|
-
throw new Error("key must be a valid hex string");
|
|
407
|
-
}
|
|
408
|
-
if (rootHash && !isHexString(rootHash)) {
|
|
409
|
-
throw new Error("rootHash must be a valid hex string");
|
|
410
|
-
}
|
|
411
|
-
let sha256;
|
|
412
|
-
if (rootHash) {
|
|
413
|
-
const tree = this.deserializeTree(rootHash);
|
|
414
|
-
// @ts-ignore
|
|
415
|
-
sha256 = tree.files.get(hexKey)?.sha256;
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
sha256 = this.files.get(hexKey)?.sha256;
|
|
419
|
-
}
|
|
420
|
-
if (!sha256) {
|
|
421
|
-
return false;
|
|
422
|
-
}
|
|
423
|
-
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
424
|
-
// Check if the file exists at the calculated path
|
|
425
|
-
return fs.existsSync(filePath);
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Get a readable stream for a file based on its key, with decompression.
|
|
429
|
-
* @param hexKey - The hexadecimal key of the file.
|
|
430
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
431
|
-
* @returns The readable stream for the file.
|
|
432
|
-
*/
|
|
433
|
-
getValueStream(hexKey, rootHash = null, byteOffset = null, length = null) {
|
|
434
|
-
if (!isHexString(hexKey)) {
|
|
435
|
-
throw new Error("key must be a valid hex string");
|
|
436
|
-
}
|
|
437
|
-
if (rootHash && !isHexString(rootHash)) {
|
|
438
|
-
throw new Error("rootHash must be a valid hex string");
|
|
439
|
-
}
|
|
440
|
-
let sha256;
|
|
441
|
-
if (rootHash) {
|
|
442
|
-
const tree = this.deserializeTree(rootHash);
|
|
443
|
-
// @ts-ignore
|
|
444
|
-
sha256 = tree.files.get(hexKey)?.sha256;
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
sha256 = this.files.get(hexKey)?.sha256;
|
|
448
|
-
}
|
|
449
|
-
if (!sha256) {
|
|
450
|
-
throw new Error(`File with key ${hexKey} not found.`);
|
|
451
|
-
}
|
|
452
|
-
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
453
|
-
if (!fs.existsSync(filePath)) {
|
|
454
|
-
throw new Error(`File at path ${filePath} does not exist`);
|
|
455
|
-
}
|
|
456
|
-
const fileSize = fs.statSync(filePath).size;
|
|
457
|
-
// Validate offset and length
|
|
458
|
-
if (byteOffset !== null && length !== null) {
|
|
459
|
-
if (byteOffset + length > fileSize) {
|
|
460
|
-
throw new Error(`Offset (${byteOffset}) and length (${length}) exceed the file size (${fileSize}).`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
// Create the read stream with optional byte range
|
|
464
|
-
const options = {};
|
|
465
|
-
if (byteOffset !== null) {
|
|
466
|
-
options.start = byteOffset;
|
|
467
|
-
}
|
|
468
|
-
if (length !== null && byteOffset !== null) {
|
|
469
|
-
options.end = byteOffset + length - 1; // `end` is inclusive, hence `byteOffset + length - 1`
|
|
470
|
-
}
|
|
471
|
-
const readStream = fs.createReadStream(filePath, options);
|
|
472
|
-
const decompressStream = zlib.createGunzip();
|
|
473
|
-
// Return the combined stream as a generic Readable stream
|
|
474
|
-
return readStream.pipe(decompressStream);
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Delete all leaves from the Merkle tree.
|
|
478
|
-
*/
|
|
479
|
-
deleteAllLeaves() {
|
|
480
|
-
this.files.clear();
|
|
481
|
-
this._rebuildTree();
|
|
482
|
-
}
|
|
483
|
-
getSHA256(hexKey, rootHash) {
|
|
484
|
-
if (!rootHash) {
|
|
485
|
-
return this.files.get(hexKey)?.sha256;
|
|
486
|
-
}
|
|
487
|
-
const tree = this.deserializeTree(rootHash);
|
|
488
|
-
// @ts-ignore
|
|
489
|
-
return tree.files.get(hexKey)?.sha256;
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* Get a proof for a file based on its key and SHA-256 hash.
|
|
493
|
-
* @param hexKey - The hexadecimal key of the file.
|
|
494
|
-
* @param sha256 - The SHA-256 hash of the file.
|
|
495
|
-
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
496
|
-
* @returns The proof for the file as a hex string.
|
|
497
|
-
*/
|
|
498
|
-
getProof(hexKey, sha256, rootHash = null) {
|
|
499
|
-
if (!isHexString(hexKey)) {
|
|
500
|
-
throw new Error("key must be a valid hex string");
|
|
501
|
-
}
|
|
502
|
-
if (!isHexString(sha256)) {
|
|
503
|
-
throw new Error("sha256 must be a valid hex string");
|
|
504
|
-
}
|
|
505
|
-
if (rootHash && !isHexString(rootHash)) {
|
|
506
|
-
throw new Error("rootHash must be a valid hex string");
|
|
507
|
-
}
|
|
508
|
-
if (!rootHash) {
|
|
509
|
-
const manifest = this._loadManifest();
|
|
510
|
-
rootHash = manifest[manifest.length - 1];
|
|
511
|
-
}
|
|
512
|
-
const tree = this.deserializeTree(rootHash);
|
|
513
|
-
const combinedHash = (0, crypto_js_1.SHA256)(`${hexKey}/${sha256}`).toString();
|
|
514
|
-
const leaf = Buffer.from(combinedHash, "hex");
|
|
515
|
-
const proof = tree.getProof(leaf);
|
|
516
|
-
// Convert the proof to a single hex string
|
|
517
|
-
const proofHex = proof.map((p) => p.data.toString("hex")).join("");
|
|
518
|
-
// Create an object with the key, rootHash, and proofHex
|
|
519
|
-
const proofObject = {
|
|
520
|
-
key: hexKey,
|
|
521
|
-
rootHash: rootHash,
|
|
522
|
-
proof: proofHex,
|
|
523
|
-
};
|
|
524
|
-
// Convert the proofObject to JSON and then to a hex string
|
|
525
|
-
const proofObjectHex = Buffer.from(JSON.stringify(proofObject)).toString("hex");
|
|
526
|
-
return proofObjectHex;
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Verify a proof for a file against the Merkle tree.
|
|
530
|
-
* @param proofObjectHex - The proof object as a hex string.
|
|
531
|
-
* @param sha256 - The SHA-256 hash of the file.
|
|
532
|
-
* @returns True if the proof is valid, false otherwise.
|
|
533
|
-
*/
|
|
534
|
-
verifyProof(proofObjectHex, sha256) {
|
|
535
|
-
if (!isHexString(proofObjectHex)) {
|
|
536
|
-
throw new Error("proofObjectHex must be a valid hex string");
|
|
537
|
-
}
|
|
538
|
-
if (!isHexString(sha256)) {
|
|
539
|
-
throw new Error("sha256 must be a valid hex string");
|
|
540
|
-
}
|
|
541
|
-
// Convert the proofObjectHex back to a proof object
|
|
542
|
-
const proofObject = JSON.parse(Buffer.from(proofObjectHex, "hex").toString("utf8"));
|
|
543
|
-
const { key, rootHash, proof } = proofObject;
|
|
544
|
-
const tree = this.deserializeTree(rootHash);
|
|
545
|
-
const combinedHash = (0, crypto_js_1.SHA256)(`${key}/${sha256}`).toString();
|
|
546
|
-
const leaf = Buffer.from(combinedHash, "hex");
|
|
547
|
-
// Convert the proofHex string back to the proof array
|
|
548
|
-
const proofBufferArray = [];
|
|
549
|
-
for (let i = 0; i < proof.length; i += 64) {
|
|
550
|
-
proofBufferArray.push(Buffer.from(proof.slice(i, i + 64), "hex"));
|
|
551
|
-
}
|
|
552
|
-
const proofArray = proofBufferArray.map((data) => ({ data }));
|
|
553
|
-
return tree.verify(proofArray, leaf, Buffer.from(rootHash, "hex"));
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Get the difference between two Merkle tree roots.
|
|
557
|
-
* @param rootHash1 - The first root hash.
|
|
558
|
-
* @param rootHash2 - The second root hash.
|
|
559
|
-
* @returns An object containing the added and deleted keys and their SHA-256 hashes.
|
|
560
|
-
*/
|
|
561
|
-
getRootDiff(rootHash1, rootHash2) {
|
|
562
|
-
if (!isHexString(rootHash1) || !isHexString(rootHash2)) {
|
|
563
|
-
throw new Error("rootHash1 and rootHash2 must be valid hex strings");
|
|
564
|
-
}
|
|
565
|
-
const tree1 = this.deserializeTree(rootHash1);
|
|
566
|
-
const tree2 = this.deserializeTree(rootHash2);
|
|
567
|
-
// @ts-ignore
|
|
568
|
-
const files1 = tree1.files;
|
|
569
|
-
// @ts-ignore
|
|
570
|
-
const files2 = tree2.files;
|
|
571
|
-
const added = new Map();
|
|
572
|
-
const deleted = new Map();
|
|
573
|
-
files1.forEach((value, key) => {
|
|
574
|
-
if (!files2.has(key)) {
|
|
575
|
-
deleted.set(key, value.sha256);
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
files2.forEach((value, key) => {
|
|
579
|
-
if (!files1.has(key)) {
|
|
580
|
-
added.set(key, value.sha256);
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
return { added, deleted };
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Verify the integrity of a file based on its SHA-256 hash and check if it is in the specified Merkle root.
|
|
587
|
-
* @param sha256 - The SHA-256 hash of the file.
|
|
588
|
-
* @param root - The root hash to check against.
|
|
589
|
-
* @returns True if the file integrity is verified and it is in the Merkle root, false otherwise.
|
|
590
|
-
*/
|
|
591
|
-
async verifyKeyIntegrity(sha256, rootHash) {
|
|
592
|
-
if (!isHexString(sha256)) {
|
|
593
|
-
throw new Error("sha256 must be a valid hex string");
|
|
594
|
-
}
|
|
595
|
-
if (!isHexString(rootHash)) {
|
|
596
|
-
throw new Error("rootHash must be a valid hex string");
|
|
597
|
-
}
|
|
598
|
-
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
599
|
-
if (!fs.existsSync(filePath)) {
|
|
600
|
-
throw new Error(`File at path ${filePath} does not exist`);
|
|
601
|
-
}
|
|
602
|
-
const compressedReadStream = fs.createReadStream(filePath);
|
|
603
|
-
const decompressStream = zlib.createGunzip();
|
|
604
|
-
const hash = crypto.createHash("sha256");
|
|
605
|
-
return new Promise((resolve, reject) => {
|
|
606
|
-
compressedReadStream.pipe(decompressStream);
|
|
607
|
-
decompressStream.on("data", (chunk) => {
|
|
608
|
-
hash.update(chunk);
|
|
609
|
-
});
|
|
610
|
-
decompressStream.on("end", () => {
|
|
611
|
-
const uncompressedSha256 = hash.digest("hex");
|
|
612
|
-
const isValid = uncompressedSha256 === sha256;
|
|
613
|
-
if (!isValid) {
|
|
614
|
-
return resolve(false);
|
|
615
|
-
}
|
|
616
|
-
const tree = this.deserializeTree(rootHash);
|
|
617
|
-
const combinedHash = crypto
|
|
618
|
-
.createHash("sha256")
|
|
619
|
-
.update(sha256)
|
|
620
|
-
.digest("hex");
|
|
621
|
-
const leaf = Buffer.from(combinedHash, "hex");
|
|
622
|
-
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
623
|
-
resolve(isInTree);
|
|
624
|
-
});
|
|
625
|
-
decompressStream.on("error", (err) => {
|
|
626
|
-
reject(err);
|
|
627
|
-
});
|
|
628
|
-
compressedReadStream.on("error", (err) => {
|
|
629
|
-
reject(err);
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
/**
|
|
634
|
-
* Static method to validate key integrity using a foreign Merkle tree.
|
|
635
|
-
* Verifies if a given SHA-256 hash for a key exists within the foreign tree and checks if the root hash matches.
|
|
636
|
-
*
|
|
637
|
-
* @param key - The hexadecimal key of the file.
|
|
638
|
-
* @param sha256 - The SHA-256 hash of the file.
|
|
639
|
-
* @param serializedTree - The foreign serialized Merkle tree.
|
|
640
|
-
* @param expectedRootHash - The expected root hash of the Merkle tree.
|
|
641
|
-
* @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
|
|
642
|
-
*/
|
|
643
|
-
static async validateKeyIntegrityWithForeignTree(hexkey, sha256, serializedTree, expectedRootHash, dataDir) {
|
|
644
|
-
if (!isHexString(hexkey)) {
|
|
645
|
-
throw new Error("key must be a valid hex string");
|
|
646
|
-
}
|
|
647
|
-
if (!isHexString(sha256)) {
|
|
648
|
-
throw new Error("sha256 must be a valid hex string");
|
|
649
|
-
}
|
|
650
|
-
if (!isHexString(expectedRootHash)) {
|
|
651
|
-
throw new Error("expectedRootHash must be a valid hex string");
|
|
652
|
-
}
|
|
653
|
-
// File path based on sha256
|
|
654
|
-
const filePath = path.join(dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
655
|
-
// Check if the file exists
|
|
656
|
-
if (!fs.existsSync(filePath)) {
|
|
657
|
-
throw new Error(`File at path ${filePath} does not exist`);
|
|
658
|
-
}
|
|
659
|
-
const compressedReadStream = fs.createReadStream(filePath);
|
|
660
|
-
const decompressStream = zlib.createGunzip();
|
|
661
|
-
const hash = crypto.createHash("sha256");
|
|
662
|
-
// Process file decompression and hash comparison
|
|
663
|
-
return new Promise((resolve, reject) => {
|
|
664
|
-
compressedReadStream.pipe(decompressStream);
|
|
665
|
-
decompressStream.on("data", (chunk) => {
|
|
666
|
-
hash.update(chunk);
|
|
667
|
-
});
|
|
668
|
-
decompressStream.on("end", () => {
|
|
669
|
-
const uncompressedSha256 = hash.digest("hex");
|
|
670
|
-
if (uncompressedSha256 !== sha256) {
|
|
671
|
-
console.warn(`File hash mismatch. Expected: ${sha256}, got: ${uncompressedSha256}`);
|
|
672
|
-
return resolve(false);
|
|
673
|
-
}
|
|
674
|
-
// Deserialize the foreign tree
|
|
675
|
-
const leaves = serializedTree.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
676
|
-
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
677
|
-
// Verify that the deserialized tree's root matches the expected root hash
|
|
678
|
-
const treeRootHash = tree.getRoot().toString("hex");
|
|
679
|
-
if (treeRootHash !== expectedRootHash) {
|
|
680
|
-
console.warn(`Expected root hash ${expectedRootHash}, but got ${treeRootHash}`);
|
|
681
|
-
return resolve(false);
|
|
682
|
-
}
|
|
683
|
-
// Rebuild the files map from the serialized tree
|
|
684
|
-
// @ts-ignore
|
|
685
|
-
tree.files = new Map(Object.entries(serializedTree.files).map(([key, value]) => [
|
|
686
|
-
key,
|
|
687
|
-
{ hash: value.hash, sha256: value.sha256 },
|
|
688
|
-
]));
|
|
689
|
-
// Check if the SHA-256 exists in the foreign tree's files
|
|
690
|
-
const combinedHash = crypto
|
|
691
|
-
.createHash("sha256")
|
|
692
|
-
.update(`${hexkey}/${sha256}`)
|
|
693
|
-
.digest("hex");
|
|
694
|
-
const leaf = Buffer.from(combinedHash, "hex");
|
|
695
|
-
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
696
|
-
resolve(isInTree);
|
|
697
|
-
});
|
|
698
|
-
decompressStream.on("error", (err) => {
|
|
699
|
-
reject(err);
|
|
700
|
-
});
|
|
701
|
-
compressedReadStream.on("error", (err) => {
|
|
702
|
-
reject(err);
|
|
703
|
-
});
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
exports.DataIntegrityTree = DataIntegrityTree;
|