@dignetwork/dig-sdk 0.0.1-alpha.4 → 0.0.1-alpha.6
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 +160 -0
- package/dist/DataIntegrityTree/DataIntegrityTree.d.ts.map +1 -0
- package/dist/DataIntegrityTree/DataIntegrityTree.js +648 -0
- package/dist/DataIntegrityTree/DataLayerError.d.ts +6 -0
- package/dist/DataIntegrityTree/DataLayerError.d.ts.map +1 -0
- package/dist/DataIntegrityTree/DataLayerError.js +17 -0
- package/dist/DataIntegrityTree/index.d.ts +2 -0
- package/dist/DataIntegrityTree/index.d.ts.map +1 -0
- package/dist/DataIntegrityTree/index.js +17 -0
- package/dist/DigNetwork/DigNetwork.d.ts.map +1 -1
- package/dist/DigNetwork/DigNetwork.js +0 -4
- package/dist/DigNetwork/DigPeer.js +2 -2
- package/dist/blockchain/DataStore.d.ts +1 -1
- package/dist/blockchain/DataStore.d.ts.map +1 -1
- package/dist/blockchain/DataStore.js +2 -2
- package/dist/blockchain/FullNodePeer.d.ts.map +1 -1
- package/dist/blockchain/FullNodePeer.js +22 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/directoryUtils.d.ts +1 -1
- package/dist/utils/directoryUtils.d.ts.map +1 -1
- package/package.json +1 -2
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { MerkleTree } from "merkletreejs";
|
|
2
|
+
import { Readable } from "stream";
|
|
3
|
+
export interface DataIntegrityTreeOptions {
|
|
4
|
+
storeDir?: string;
|
|
5
|
+
storageMode?: "local" | "unified";
|
|
6
|
+
rootHash?: string;
|
|
7
|
+
disableInitialize?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* DataStoreManager class to manage Merkle tree operations.
|
|
11
|
+
*/
|
|
12
|
+
declare class DataIntegrityTree {
|
|
13
|
+
private storeId;
|
|
14
|
+
private storeBaseDir;
|
|
15
|
+
private storeDir;
|
|
16
|
+
private dataDir;
|
|
17
|
+
files: Map<string, {
|
|
18
|
+
hash: string;
|
|
19
|
+
sha256: string;
|
|
20
|
+
}>;
|
|
21
|
+
private tree;
|
|
22
|
+
constructor(storeId: string, options?: DataIntegrityTreeOptions);
|
|
23
|
+
static from(storeId: string, options: DataIntegrityTreeOptions): DataIntegrityTree;
|
|
24
|
+
/**
|
|
25
|
+
* Load the manifest file.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
private _loadManifest;
|
|
29
|
+
/**
|
|
30
|
+
* Load the latest tree from the manifest file.
|
|
31
|
+
* @private
|
|
32
|
+
*/
|
|
33
|
+
private _loadLatestTree;
|
|
34
|
+
/**
|
|
35
|
+
* Save a binary stream to the store's data directory.
|
|
36
|
+
* @param sha256 - The SHA-256 hash of the buffer.
|
|
37
|
+
* @returns The write stream for the file.
|
|
38
|
+
*/
|
|
39
|
+
private _createWriteStream;
|
|
40
|
+
/**
|
|
41
|
+
* Stream file from one path to another.
|
|
42
|
+
* @param src - The source file path.
|
|
43
|
+
* @param dest - The destination file path.
|
|
44
|
+
*/
|
|
45
|
+
private _streamFile;
|
|
46
|
+
/**
|
|
47
|
+
* Upsert a key with a binary stream to the Merkle tree.
|
|
48
|
+
* Compresses the file, calculates the SHA-256 of the uncompressed file, and stores it.
|
|
49
|
+
* @param readStream - The binary data stream.
|
|
50
|
+
* @param key - The hexadecimal key for the binary data.
|
|
51
|
+
*/
|
|
52
|
+
upsertKey(readStream: Readable, key: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Delete a key from the Merkle tree.
|
|
55
|
+
* @param key - The hexadecimal key to delete.
|
|
56
|
+
*/
|
|
57
|
+
deleteKey(key: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* List all keys in the Merkle tree.
|
|
60
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
61
|
+
* @returns The list of keys.
|
|
62
|
+
*/
|
|
63
|
+
listKeys(rootHash?: string | null): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Rebuild the Merkle tree from the current files.
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
private _rebuildTree;
|
|
69
|
+
/**
|
|
70
|
+
* Get the root of the Merkle tree.
|
|
71
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
72
|
+
* @returns The Merkle root.
|
|
73
|
+
*/
|
|
74
|
+
getRoot(): string;
|
|
75
|
+
/**
|
|
76
|
+
* Serialize the Merkle tree to a JSON object.
|
|
77
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
78
|
+
* @returns The serialized Merkle tree.
|
|
79
|
+
*/
|
|
80
|
+
serialize(rootHash?: string | null): object;
|
|
81
|
+
/**
|
|
82
|
+
* Deserialize a JSON object to a Merkle tree.
|
|
83
|
+
* @param rootHash - The root hash of the tree.
|
|
84
|
+
* @returns The deserialized Merkle tree.
|
|
85
|
+
*/
|
|
86
|
+
deserializeTree(rootHash: string): MerkleTree;
|
|
87
|
+
private appendRootHashToManifest;
|
|
88
|
+
/**
|
|
89
|
+
* Commit the current state of the Merkle tree.
|
|
90
|
+
*/
|
|
91
|
+
commit(): string | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Clear pending changes and revert to the latest committed state.
|
|
94
|
+
*/
|
|
95
|
+
clearPendingRoot(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Check if a file exists for a given key.
|
|
98
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
99
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
100
|
+
* @returns A boolean indicating if the file exists.
|
|
101
|
+
*/
|
|
102
|
+
hasKey(hexKey: string, rootHash?: string | null): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Get a readable stream for a file based on its key, with decompression.
|
|
105
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
106
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
107
|
+
* @returns The readable stream for the file.
|
|
108
|
+
*/
|
|
109
|
+
getValueStream(hexKey: string, rootHash?: string | null): Readable;
|
|
110
|
+
/**
|
|
111
|
+
* Delete all leaves from the Merkle tree.
|
|
112
|
+
*/
|
|
113
|
+
deleteAllLeaves(): void;
|
|
114
|
+
getSHA256(hexKey: string, rootHash?: string): string | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* Get a proof for a file based on its key and SHA-256 hash.
|
|
117
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
118
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
119
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
120
|
+
* @returns The proof for the file as a hex string.
|
|
121
|
+
*/
|
|
122
|
+
getProof(hexKey: string, sha256: string, rootHash?: string | null): string;
|
|
123
|
+
/**
|
|
124
|
+
* Verify a proof for a file against the Merkle tree.
|
|
125
|
+
* @param proofObjectHex - The proof object as a hex string.
|
|
126
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
127
|
+
* @returns True if the proof is valid, false otherwise.
|
|
128
|
+
*/
|
|
129
|
+
verifyProof(proofObjectHex: string, sha256: string): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Get the difference between two Merkle tree roots.
|
|
132
|
+
* @param rootHash1 - The first root hash.
|
|
133
|
+
* @param rootHash2 - The second root hash.
|
|
134
|
+
* @returns An object containing the added and deleted keys and their SHA-256 hashes.
|
|
135
|
+
*/
|
|
136
|
+
getRootDiff(rootHash1: string, rootHash2: string): {
|
|
137
|
+
added: Map<string, string>;
|
|
138
|
+
deleted: Map<string, string>;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Verify the integrity of a file based on its SHA-256 hash and check if it is in the specified Merkle root.
|
|
142
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
143
|
+
* @param root - The root hash to check against.
|
|
144
|
+
* @returns True if the file integrity is verified and it is in the Merkle root, false otherwise.
|
|
145
|
+
*/
|
|
146
|
+
verifyKeyIntegrity(sha256: string, rootHash: string): Promise<boolean>;
|
|
147
|
+
/**
|
|
148
|
+
* Static method to validate key integrity using a foreign Merkle tree.
|
|
149
|
+
* Verifies if a given SHA-256 hash for a key exists within the foreign tree and checks if the root hash matches.
|
|
150
|
+
*
|
|
151
|
+
* @param key - The hexadecimal key of the file.
|
|
152
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
153
|
+
* @param serializedTree - The foreign serialized Merkle tree.
|
|
154
|
+
* @param expectedRootHash - The expected root hash of the Merkle tree.
|
|
155
|
+
* @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
|
|
156
|
+
*/
|
|
157
|
+
static validateKeyIntegrityWithForeignTree(key: string, sha256: string, serializedTree: object, expectedRootHash: string): boolean;
|
|
158
|
+
}
|
|
159
|
+
export { DataIntegrityTree };
|
|
160
|
+
//# sourceMappingURL=DataIntegrityTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataIntegrityTree.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataIntegrityTree.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAuClC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,cAAM,iBAAiB;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,CAAa;gBAEb,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B;WAiDrD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,iBAAiB;IAIzF;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;;OAIG;YACW,WAAW;IAazB;;;;;OAKG;IACG,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+EjE;;;OAGG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAW5B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM,EAAE;IAalD;;;OAGG;IACH,OAAO,CAAC,YAAY;IAOpB;;;;OAIG;IACH,OAAO,IAAI,MAAM;IAIjB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM;IAqBjD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAwB7C,OAAO,CAAC,wBAAwB;IAkBhC;;OAEG;IACH,MAAM,IAAI,MAAM,GAAG,SAAS;IAoC5B;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAIxB;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IA+B/D;;;;;OAKG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,QAAQ;IAuCxE;;OAEG;IACH,eAAe,IAAI,IAAI;IAKvB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhE;;;;;;OAMG;IACH,QAAQ,CACN,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAM,GAAG,IAAW,GAC7B,MAAM;IAsCT;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IA4B5D;;;;;OAKG;IACH,WAAW,CACT,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB;QAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IA+B/D;;;;;OAKG;IACG,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0D5E;;;;;;;;;OASG;IACH,MAAM,CAAC,mCAAmC,CACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,GACvB,OAAO;CAgDX;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,648 @@
|
|
|
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
|
+
const manifest = this._loadManifest();
|
|
98
|
+
if (manifest.includes(options.rootHash)) {
|
|
99
|
+
this.tree = this.deserializeTree(options.rootHash);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw new DataLayerError_1.default(404, `Root hash ${options.rootHash} not found`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.tree = this._loadLatestTree();
|
|
107
|
+
}
|
|
108
|
+
// Commit the empty Merkle tree immediately upon creation
|
|
109
|
+
if (!options.disableInitialize && this.tree.getLeafCount() === 0) {
|
|
110
|
+
this.commit();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
static from(storeId, options) {
|
|
114
|
+
return new DataIntegrityTree(storeId, { ...options, disableInitialize: true });
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Load the manifest file.
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
_loadManifest() {
|
|
121
|
+
const manifestPath = path.join(this.storeDir, "manifest.dat");
|
|
122
|
+
if (fs.existsSync(manifestPath)) {
|
|
123
|
+
return fs.readFileSync(manifestPath, "utf8").trim().split("\n");
|
|
124
|
+
}
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Load the latest tree from the manifest file.
|
|
129
|
+
* @private
|
|
130
|
+
*/
|
|
131
|
+
_loadLatestTree() {
|
|
132
|
+
const manifest = this._loadManifest();
|
|
133
|
+
if (manifest.length > 0) {
|
|
134
|
+
const latestRootHash = manifest[manifest.length - 1];
|
|
135
|
+
return this.deserializeTree(latestRootHash);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return new merkletreejs_1.MerkleTree([], crypto_js_1.SHA256, { sortPairs: true });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Save a binary stream to the store's data directory.
|
|
143
|
+
* @param sha256 - The SHA-256 hash of the buffer.
|
|
144
|
+
* @returns The write stream for the file.
|
|
145
|
+
*/
|
|
146
|
+
_createWriteStream(sha256) {
|
|
147
|
+
const subDirs = sha256.match(/.{1,2}/g) || [];
|
|
148
|
+
const fileDir = path.join(this.dataDir, ...subDirs.slice(0, -1));
|
|
149
|
+
const fileName = subDirs[subDirs.length - 1];
|
|
150
|
+
const fileSavePath = path.join(fileDir, fileName);
|
|
151
|
+
if (!fs.existsSync(fileDir)) {
|
|
152
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
153
|
+
}
|
|
154
|
+
return fs.createWriteStream(fileSavePath);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Stream file from one path to another.
|
|
158
|
+
* @param src - The source file path.
|
|
159
|
+
* @param dest - The destination file path.
|
|
160
|
+
*/
|
|
161
|
+
async _streamFile(src, dest) {
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
const readStream = fs.createReadStream(src);
|
|
164
|
+
const writeStream = fs.createWriteStream(dest);
|
|
165
|
+
readStream.pipe(writeStream);
|
|
166
|
+
writeStream.on("finish", resolve);
|
|
167
|
+
writeStream.on("error", reject);
|
|
168
|
+
readStream.on("error", reject);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Upsert a key with a binary stream to the Merkle tree.
|
|
173
|
+
* Compresses the file, calculates the SHA-256 of the uncompressed file, and stores it.
|
|
174
|
+
* @param readStream - The binary data stream.
|
|
175
|
+
* @param key - The hexadecimal key for the binary data.
|
|
176
|
+
*/
|
|
177
|
+
async upsertKey(readStream, key) {
|
|
178
|
+
if (!isHexString(key)) {
|
|
179
|
+
throw new Error(`key must be a valid hex string: ${key}`);
|
|
180
|
+
}
|
|
181
|
+
const uncompressedHash = crypto.createHash("sha256");
|
|
182
|
+
const gzip = zlib.createGzip();
|
|
183
|
+
let sha256;
|
|
184
|
+
const tempDir = path.join(this.storeDir, "tmp");
|
|
185
|
+
if (!fs.existsSync(tempDir)) {
|
|
186
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
const tempFilePath = path.join(tempDir, `${crypto.randomUUID()}.gz`);
|
|
189
|
+
return new Promise((resolve, reject) => {
|
|
190
|
+
const tempWriteStream = fs.createWriteStream(tempFilePath);
|
|
191
|
+
readStream.on("data", (chunk) => {
|
|
192
|
+
uncompressedHash.update(chunk);
|
|
193
|
+
});
|
|
194
|
+
readStream.pipe(gzip).pipe(tempWriteStream);
|
|
195
|
+
tempWriteStream.on("finish", async () => {
|
|
196
|
+
sha256 = uncompressedHash.digest("hex");
|
|
197
|
+
const finalWriteStream = this._createWriteStream(sha256);
|
|
198
|
+
const finalPath = finalWriteStream.path;
|
|
199
|
+
// Ensure the directory exists before copying the file
|
|
200
|
+
const finalDir = path.dirname(finalPath);
|
|
201
|
+
if (!fs.existsSync(finalDir)) {
|
|
202
|
+
fs.mkdirSync(finalDir, { recursive: true });
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
await this._streamFile(tempFilePath, finalPath);
|
|
206
|
+
await unlink(tempFilePath);
|
|
207
|
+
const combinedHash = crypto
|
|
208
|
+
.createHash("sha256")
|
|
209
|
+
.update(`${key}/${sha256}`)
|
|
210
|
+
.digest("hex");
|
|
211
|
+
if (Array.from(this.files.values()).some((file) => file.hash === combinedHash)) {
|
|
212
|
+
console.log(`No changes detected for key: ${key}`);
|
|
213
|
+
return resolve();
|
|
214
|
+
}
|
|
215
|
+
if (this.files.has(key)) {
|
|
216
|
+
this.deleteKey(key);
|
|
217
|
+
}
|
|
218
|
+
console.log(`Inserted key: ${key}`);
|
|
219
|
+
this.files.set(key, {
|
|
220
|
+
hash: combinedHash,
|
|
221
|
+
sha256: sha256,
|
|
222
|
+
});
|
|
223
|
+
this._rebuildTree();
|
|
224
|
+
resolve();
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
reject(err);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
tempWriteStream.on("error", (err) => {
|
|
231
|
+
reject(err);
|
|
232
|
+
});
|
|
233
|
+
readStream.on("error", (err) => {
|
|
234
|
+
reject(err);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Delete a key from the Merkle tree.
|
|
240
|
+
* @param key - The hexadecimal key to delete.
|
|
241
|
+
*/
|
|
242
|
+
deleteKey(key) {
|
|
243
|
+
if (!isHexString(key)) {
|
|
244
|
+
throw new Error("key must be a valid hex string");
|
|
245
|
+
}
|
|
246
|
+
if (this.files.has(key)) {
|
|
247
|
+
this.files.delete(key);
|
|
248
|
+
this._rebuildTree();
|
|
249
|
+
console.log(`Deleted key: ${key}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* List all keys in the Merkle tree.
|
|
254
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
255
|
+
* @returns The list of keys.
|
|
256
|
+
*/
|
|
257
|
+
listKeys(rootHash = null) {
|
|
258
|
+
if (rootHash && !isHexString(rootHash)) {
|
|
259
|
+
throw new Error("rootHash must be a valid hex string");
|
|
260
|
+
}
|
|
261
|
+
if (rootHash) {
|
|
262
|
+
const tree = this.deserializeTree(rootHash);
|
|
263
|
+
// @ts-ignore
|
|
264
|
+
return Array.from(tree.files.keys());
|
|
265
|
+
}
|
|
266
|
+
return Array.from(this.files.keys());
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Rebuild the Merkle tree from the current files.
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
_rebuildTree() {
|
|
273
|
+
const leaves = Array.from(this.files.values()).map(({ hash }) => Buffer.from(hash, "hex"));
|
|
274
|
+
this.tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get the root of the Merkle tree.
|
|
278
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
279
|
+
* @returns The Merkle root.
|
|
280
|
+
*/
|
|
281
|
+
getRoot() {
|
|
282
|
+
return this.tree.getRoot().toString("hex");
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Serialize the Merkle tree to a JSON object.
|
|
286
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
287
|
+
* @returns The serialized Merkle tree.
|
|
288
|
+
*/
|
|
289
|
+
serialize(rootHash = null) {
|
|
290
|
+
if (rootHash && !isHexString(rootHash)) {
|
|
291
|
+
throw new Error("rootHash must be a valid hex string");
|
|
292
|
+
}
|
|
293
|
+
if (rootHash) {
|
|
294
|
+
const tree = this.deserializeTree(rootHash);
|
|
295
|
+
return {
|
|
296
|
+
root: tree.getRoot().toString("hex"),
|
|
297
|
+
leaves: tree.getLeaves().map((leaf) => leaf.toString("hex")),
|
|
298
|
+
// @ts-ignore
|
|
299
|
+
files: Object.fromEntries(tree.files),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
root: this.getRoot(),
|
|
304
|
+
leaves: this.tree.getLeaves().map((leaf) => leaf.toString("hex")),
|
|
305
|
+
files: Object.fromEntries(this.files),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Deserialize a JSON object to a Merkle tree.
|
|
310
|
+
* @param rootHash - The root hash of the tree.
|
|
311
|
+
* @returns The deserialized Merkle tree.
|
|
312
|
+
*/
|
|
313
|
+
deserializeTree(rootHash) {
|
|
314
|
+
if (!isHexString(rootHash)) {
|
|
315
|
+
throw new Error("rootHash must be a valid hex string");
|
|
316
|
+
}
|
|
317
|
+
const treeFilePath = path.join(this.storeDir, `${rootHash}.dat`);
|
|
318
|
+
if (!fs.existsSync(treeFilePath)) {
|
|
319
|
+
throw new Error(`Tree file ${treeFilePath} does not exist`);
|
|
320
|
+
}
|
|
321
|
+
const data = JSON.parse(fs.readFileSync(treeFilePath, "utf8"));
|
|
322
|
+
const leaves = data.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
323
|
+
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
324
|
+
// @ts-ignore
|
|
325
|
+
tree.files = new Map(Object.entries(data.files).map(([key, value]) => [
|
|
326
|
+
key,
|
|
327
|
+
{ hash: value.hash, sha256: value.sha256 },
|
|
328
|
+
]));
|
|
329
|
+
// @ts-ignore
|
|
330
|
+
this.files = tree.files;
|
|
331
|
+
return tree;
|
|
332
|
+
}
|
|
333
|
+
appendRootHashToManifest(rootHash) {
|
|
334
|
+
const manifestPath = path.join(this.storeDir, "manifest.dat");
|
|
335
|
+
// Read the current manifest file
|
|
336
|
+
const manifestContent = fs.existsSync(manifestPath)
|
|
337
|
+
? fs.readFileSync(manifestPath, "utf-8").trim().split("\n")
|
|
338
|
+
: [];
|
|
339
|
+
// Check if the last entry is the same as the rootHash to avoid duplicates
|
|
340
|
+
const latestRootHash = manifestContent.length > 0 ? manifestContent[manifestContent.length - 1] : null;
|
|
341
|
+
if (latestRootHash !== rootHash) {
|
|
342
|
+
// Append the new rootHash if it is not the same as the last one
|
|
343
|
+
fs.appendFileSync(manifestPath, `${rootHash}\n`);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
console.log(`Root hash ${rootHash} is already at the end of the file. Skipping append.`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Commit the current state of the Merkle tree.
|
|
351
|
+
*/
|
|
352
|
+
commit() {
|
|
353
|
+
const emptyRootHash = "0000000000000000000000000000000000000000000000000000000000000000";
|
|
354
|
+
const rootHash = this.tree.getLeafCount() === 0 ? emptyRootHash : this.getRoot();
|
|
355
|
+
const manifest = this._loadManifest();
|
|
356
|
+
const latestRootHash = manifest.length > 0 ? manifest[manifest.length - 1] : null;
|
|
357
|
+
if (rootHash === latestRootHash && rootHash !== emptyRootHash) {
|
|
358
|
+
console.log("No changes to commit. Aborting commit.");
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
this.appendRootHashToManifest(rootHash);
|
|
362
|
+
const treeFilePath = path.join(this.storeDir, `${rootHash}.dat`);
|
|
363
|
+
if (!fs.existsSync(path.dirname(treeFilePath))) {
|
|
364
|
+
fs.mkdirSync(path.dirname(treeFilePath), { recursive: true });
|
|
365
|
+
}
|
|
366
|
+
const serializedTree = this.serialize();
|
|
367
|
+
if (rootHash === emptyRootHash) {
|
|
368
|
+
serializedTree.root = emptyRootHash;
|
|
369
|
+
}
|
|
370
|
+
fs.writeFileSync(treeFilePath, JSON.stringify(serializedTree));
|
|
371
|
+
console.log(`Committed new root`);
|
|
372
|
+
console.log(this.tree.toString());
|
|
373
|
+
return rootHash;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Clear pending changes and revert to the latest committed state.
|
|
377
|
+
*/
|
|
378
|
+
clearPendingRoot() {
|
|
379
|
+
this.tree = this._loadLatestTree();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Check if a file exists for a given key.
|
|
383
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
384
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
385
|
+
* @returns A boolean indicating if the file exists.
|
|
386
|
+
*/
|
|
387
|
+
hasKey(hexKey, rootHash = null) {
|
|
388
|
+
if (!isHexString(hexKey)) {
|
|
389
|
+
throw new Error("key must be a valid hex string");
|
|
390
|
+
}
|
|
391
|
+
if (rootHash && !isHexString(rootHash)) {
|
|
392
|
+
throw new Error("rootHash must be a valid hex string");
|
|
393
|
+
}
|
|
394
|
+
let sha256;
|
|
395
|
+
if (rootHash) {
|
|
396
|
+
const tree = this.deserializeTree(rootHash);
|
|
397
|
+
// @ts-ignore
|
|
398
|
+
sha256 = tree.files.get(hexKey)?.sha256;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
sha256 = this.files.get(hexKey)?.sha256;
|
|
402
|
+
}
|
|
403
|
+
if (!sha256) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
407
|
+
// Check if the file exists at the calculated path
|
|
408
|
+
return fs.existsSync(filePath);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Get a readable stream for a file based on its key, with decompression.
|
|
412
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
413
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
414
|
+
* @returns The readable stream for the file.
|
|
415
|
+
*/
|
|
416
|
+
getValueStream(hexKey, rootHash = null) {
|
|
417
|
+
if (!isHexString(hexKey)) {
|
|
418
|
+
throw new Error("key must be a valid hex string");
|
|
419
|
+
}
|
|
420
|
+
if (rootHash && !isHexString(rootHash)) {
|
|
421
|
+
throw new Error("rootHash must be a valid hex string");
|
|
422
|
+
}
|
|
423
|
+
let sha256;
|
|
424
|
+
if (rootHash) {
|
|
425
|
+
const tree = this.deserializeTree(rootHash);
|
|
426
|
+
// @ts-ignore
|
|
427
|
+
sha256 = tree.files.get(hexKey)?.sha256;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
sha256 = this.files.get(hexKey)?.sha256;
|
|
431
|
+
}
|
|
432
|
+
if (!sha256) {
|
|
433
|
+
throw new Error(`File with key ${hexKey} not found.`);
|
|
434
|
+
}
|
|
435
|
+
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
436
|
+
if (!fs.existsSync(filePath)) {
|
|
437
|
+
throw new Error(`File at path ${filePath} does not exist`);
|
|
438
|
+
}
|
|
439
|
+
// Create a read stream and pipe it through a decompression stream using the same algorithm (gzip)
|
|
440
|
+
const readStream = fs.createReadStream(filePath);
|
|
441
|
+
const decompressStream = zlib.createGunzip();
|
|
442
|
+
// Return the combined stream as a generic Readable stream
|
|
443
|
+
return readStream.pipe(decompressStream);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Delete all leaves from the Merkle tree.
|
|
447
|
+
*/
|
|
448
|
+
deleteAllLeaves() {
|
|
449
|
+
this.files.clear();
|
|
450
|
+
this._rebuildTree();
|
|
451
|
+
}
|
|
452
|
+
getSHA256(hexKey, rootHash) {
|
|
453
|
+
if (!rootHash) {
|
|
454
|
+
return this.files.get(hexKey)?.sha256;
|
|
455
|
+
}
|
|
456
|
+
const tree = this.deserializeTree(rootHash);
|
|
457
|
+
// @ts-ignore
|
|
458
|
+
return tree.files.get(hexKey)?.sha256;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Get a proof for a file based on its key and SHA-256 hash.
|
|
462
|
+
* @param hexKey - The hexadecimal key of the file.
|
|
463
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
464
|
+
* @param rootHash - The root hash of the tree. Defaults to the latest root hash.
|
|
465
|
+
* @returns The proof for the file as a hex string.
|
|
466
|
+
*/
|
|
467
|
+
getProof(hexKey, sha256, rootHash = null) {
|
|
468
|
+
if (!isHexString(hexKey)) {
|
|
469
|
+
throw new Error("key must be a valid hex string");
|
|
470
|
+
}
|
|
471
|
+
if (!isHexString(sha256)) {
|
|
472
|
+
throw new Error("sha256 must be a valid hex string");
|
|
473
|
+
}
|
|
474
|
+
if (rootHash && !isHexString(rootHash)) {
|
|
475
|
+
throw new Error("rootHash must be a valid hex string");
|
|
476
|
+
}
|
|
477
|
+
if (!rootHash) {
|
|
478
|
+
const manifest = this._loadManifest();
|
|
479
|
+
rootHash = manifest[manifest.length - 1];
|
|
480
|
+
}
|
|
481
|
+
const tree = this.deserializeTree(rootHash);
|
|
482
|
+
const combinedHash = (0, crypto_js_1.SHA256)(`${hexKey}/${sha256}`).toString();
|
|
483
|
+
const leaf = Buffer.from(combinedHash, "hex");
|
|
484
|
+
const proof = tree.getProof(leaf);
|
|
485
|
+
// Convert the proof to a single hex string
|
|
486
|
+
const proofHex = proof.map((p) => p.data.toString("hex")).join("");
|
|
487
|
+
// Create an object with the key, rootHash, and proofHex
|
|
488
|
+
const proofObject = {
|
|
489
|
+
key: hexKey,
|
|
490
|
+
rootHash: rootHash,
|
|
491
|
+
proof: proofHex,
|
|
492
|
+
};
|
|
493
|
+
// Convert the proofObject to JSON and then to a hex string
|
|
494
|
+
const proofObjectHex = Buffer.from(JSON.stringify(proofObject)).toString("hex");
|
|
495
|
+
return proofObjectHex;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Verify a proof for a file against the Merkle tree.
|
|
499
|
+
* @param proofObjectHex - The proof object as a hex string.
|
|
500
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
501
|
+
* @returns True if the proof is valid, false otherwise.
|
|
502
|
+
*/
|
|
503
|
+
verifyProof(proofObjectHex, sha256) {
|
|
504
|
+
if (!isHexString(proofObjectHex)) {
|
|
505
|
+
throw new Error("proofObjectHex must be a valid hex string");
|
|
506
|
+
}
|
|
507
|
+
if (!isHexString(sha256)) {
|
|
508
|
+
throw new Error("sha256 must be a valid hex string");
|
|
509
|
+
}
|
|
510
|
+
// Convert the proofObjectHex back to a proof object
|
|
511
|
+
const proofObject = JSON.parse(Buffer.from(proofObjectHex, "hex").toString("utf8"));
|
|
512
|
+
const { key, rootHash, proof } = proofObject;
|
|
513
|
+
const tree = this.deserializeTree(rootHash);
|
|
514
|
+
const combinedHash = (0, crypto_js_1.SHA256)(`${key}/${sha256}`).toString();
|
|
515
|
+
const leaf = Buffer.from(combinedHash, "hex");
|
|
516
|
+
// Convert the proofHex string back to the proof array
|
|
517
|
+
const proofBufferArray = [];
|
|
518
|
+
for (let i = 0; i < proof.length; i += 64) {
|
|
519
|
+
proofBufferArray.push(Buffer.from(proof.slice(i, i + 64), "hex"));
|
|
520
|
+
}
|
|
521
|
+
const proofArray = proofBufferArray.map((data) => ({ data }));
|
|
522
|
+
return tree.verify(proofArray, leaf, Buffer.from(rootHash, "hex"));
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Get the difference between two Merkle tree roots.
|
|
526
|
+
* @param rootHash1 - The first root hash.
|
|
527
|
+
* @param rootHash2 - The second root hash.
|
|
528
|
+
* @returns An object containing the added and deleted keys and their SHA-256 hashes.
|
|
529
|
+
*/
|
|
530
|
+
getRootDiff(rootHash1, rootHash2) {
|
|
531
|
+
if (!isHexString(rootHash1) || !isHexString(rootHash2)) {
|
|
532
|
+
throw new Error("rootHash1 and rootHash2 must be valid hex strings");
|
|
533
|
+
}
|
|
534
|
+
const tree1 = this.deserializeTree(rootHash1);
|
|
535
|
+
const tree2 = this.deserializeTree(rootHash2);
|
|
536
|
+
// @ts-ignore
|
|
537
|
+
const files1 = tree1.files;
|
|
538
|
+
// @ts-ignore
|
|
539
|
+
const files2 = tree2.files;
|
|
540
|
+
const added = new Map();
|
|
541
|
+
const deleted = new Map();
|
|
542
|
+
files1.forEach((value, key) => {
|
|
543
|
+
if (!files2.has(key)) {
|
|
544
|
+
deleted.set(key, value.sha256);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
files2.forEach((value, key) => {
|
|
548
|
+
if (!files1.has(key)) {
|
|
549
|
+
added.set(key, value.sha256);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return { added, deleted };
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Verify the integrity of a file based on its SHA-256 hash and check if it is in the specified Merkle root.
|
|
556
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
557
|
+
* @param root - The root hash to check against.
|
|
558
|
+
* @returns True if the file integrity is verified and it is in the Merkle root, false otherwise.
|
|
559
|
+
*/
|
|
560
|
+
async verifyKeyIntegrity(sha256, rootHash) {
|
|
561
|
+
if (!isHexString(sha256)) {
|
|
562
|
+
throw new Error("sha256 must be a valid hex string");
|
|
563
|
+
}
|
|
564
|
+
if (!isHexString(rootHash)) {
|
|
565
|
+
throw new Error("rootHash must be a valid hex string");
|
|
566
|
+
}
|
|
567
|
+
const filePath = path.join(this.dataDir, sha256.match(/.{1,2}/g).join("/"));
|
|
568
|
+
if (!fs.existsSync(filePath)) {
|
|
569
|
+
throw new Error(`File at path ${filePath} does not exist`);
|
|
570
|
+
}
|
|
571
|
+
const compressedReadStream = fs.createReadStream(filePath);
|
|
572
|
+
const decompressStream = zlib.createGunzip();
|
|
573
|
+
const hash = crypto.createHash("sha256");
|
|
574
|
+
return new Promise((resolve, reject) => {
|
|
575
|
+
compressedReadStream.pipe(decompressStream);
|
|
576
|
+
decompressStream.on("data", (chunk) => {
|
|
577
|
+
hash.update(chunk);
|
|
578
|
+
});
|
|
579
|
+
decompressStream.on("end", () => {
|
|
580
|
+
const uncompressedSha256 = hash.digest("hex");
|
|
581
|
+
const isValid = uncompressedSha256 === sha256;
|
|
582
|
+
console.log(`SHA-256 of uncompressed file: ${uncompressedSha256}`);
|
|
583
|
+
if (!isValid) {
|
|
584
|
+
return resolve(false);
|
|
585
|
+
}
|
|
586
|
+
const tree = this.deserializeTree(rootHash);
|
|
587
|
+
const combinedHash = crypto
|
|
588
|
+
.createHash("sha256")
|
|
589
|
+
.update(`${toHex(sha256)}/${sha256}`)
|
|
590
|
+
.digest("hex");
|
|
591
|
+
const leaf = Buffer.from(combinedHash, "hex");
|
|
592
|
+
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
593
|
+
resolve(isInTree);
|
|
594
|
+
});
|
|
595
|
+
decompressStream.on("error", (err) => {
|
|
596
|
+
reject(err);
|
|
597
|
+
});
|
|
598
|
+
compressedReadStream.on("error", (err) => {
|
|
599
|
+
reject(err);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Static method to validate key integrity using a foreign Merkle tree.
|
|
605
|
+
* Verifies if a given SHA-256 hash for a key exists within the foreign tree and checks if the root hash matches.
|
|
606
|
+
*
|
|
607
|
+
* @param key - The hexadecimal key of the file.
|
|
608
|
+
* @param sha256 - The SHA-256 hash of the file.
|
|
609
|
+
* @param serializedTree - The foreign serialized Merkle tree.
|
|
610
|
+
* @param expectedRootHash - The expected root hash of the Merkle tree.
|
|
611
|
+
* @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches.
|
|
612
|
+
*/
|
|
613
|
+
static validateKeyIntegrityWithForeignTree(key, sha256, serializedTree, expectedRootHash) {
|
|
614
|
+
if (!isHexString(key)) {
|
|
615
|
+
throw new Error("key must be a valid hex string");
|
|
616
|
+
}
|
|
617
|
+
if (!isHexString(sha256)) {
|
|
618
|
+
throw new Error("sha256 must be a valid hex string");
|
|
619
|
+
}
|
|
620
|
+
if (!isHexString(expectedRootHash)) {
|
|
621
|
+
throw new Error("expectedRootHash must be a valid hex string");
|
|
622
|
+
}
|
|
623
|
+
// Deserialize the foreign tree
|
|
624
|
+
const leaves = serializedTree.leaves.map((leaf) => Buffer.from(leaf, "hex"));
|
|
625
|
+
const tree = new merkletreejs_1.MerkleTree(leaves, crypto_js_1.SHA256, { sortPairs: true });
|
|
626
|
+
// Verify that the deserialized tree's root matches the expected root hash
|
|
627
|
+
const treeRootHash = tree.getRoot().toString("hex");
|
|
628
|
+
if (treeRootHash !== expectedRootHash) {
|
|
629
|
+
console.warn(`Expected root hash ${expectedRootHash}, but got ${treeRootHash}`);
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
// Rebuild the files map from the serialized tree
|
|
633
|
+
// @ts-ignore
|
|
634
|
+
tree.files = new Map(Object.entries(serializedTree.files).map(([key, value]) => [
|
|
635
|
+
key,
|
|
636
|
+
{ hash: value.hash, sha256: value.sha256 },
|
|
637
|
+
]));
|
|
638
|
+
// Check if the SHA-256 exists in the foreign tree's files
|
|
639
|
+
const combinedHash = crypto
|
|
640
|
+
.createHash("sha256")
|
|
641
|
+
.update(`${toHex(key)}/${sha256}`)
|
|
642
|
+
.digest("hex");
|
|
643
|
+
const leaf = Buffer.from(combinedHash, "hex");
|
|
644
|
+
const isInTree = tree.getLeafIndex(leaf) !== -1;
|
|
645
|
+
return isInTree;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
exports.DataIntegrityTree = DataIntegrityTree;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataLayerError.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/DataLayerError.ts"],"names":[],"mappings":"AAAA,cAAM,cAAe,SAAQ,KAAK;IAC9B,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAe1C;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class DataLayerError extends Error {
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
// Call the parent constructor with the message
|
|
6
|
+
super(message);
|
|
7
|
+
// Set the name of the error to the class name
|
|
8
|
+
this.name = this.constructor.name;
|
|
9
|
+
// Assign the custom code
|
|
10
|
+
this.code = code;
|
|
11
|
+
// Capture the stack trace (if available in the environment)
|
|
12
|
+
if (Error.captureStackTrace) {
|
|
13
|
+
Error.captureStackTrace(this, this.constructor);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.default = DataLayerError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DataIntegrityTree/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./DataIntegrityTree"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"DigNetwork.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/DigNetwork.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAA2B;gBAEpC,OAAO,EAAE,MAAM;YAOb,eAAe;IA8ChB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDhD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;WA6BrC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAQtD,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ5C,aAAa,CACxB,aAAa,GAAE,OAAe,EAC9B,iBAAiB,GAAE,OAAc,EACjC,QAAQ,GAAE,OAAe,GACxB,OAAO,CAAC,IAAI,CAAC;YAqFF,mBAAmB;YAWnB,kBAAkB;YASlB,oBAAoB;YASpB,qBAAqB;YAuErB,cAAc;CAoB7B"}
|
|
@@ -101,7 +101,6 @@ class DigNetwork {
|
|
|
101
101
|
async uploadStore(digPeer) {
|
|
102
102
|
const { generationIndex } = await this.uploadPreflight(digPeer);
|
|
103
103
|
const filesToUpload = await (0, deltaUtils_1.getDeltaFiles)(this.dataStore.StoreId, generationIndex, path.resolve(config_1.DIG_FOLDER_PATH, "stores"));
|
|
104
|
-
console.log(filesToUpload);
|
|
105
104
|
if (!filesToUpload.length) {
|
|
106
105
|
console.log("No files to upload.");
|
|
107
106
|
return;
|
|
@@ -246,8 +245,6 @@ class DigNetwork {
|
|
|
246
245
|
}
|
|
247
246
|
async runProgressBar(total, name, task) {
|
|
248
247
|
// Using 'any' to work around TypeScript issues
|
|
249
|
-
const oldConsoleLog = console.log;
|
|
250
|
-
console.log = () => { }; // Suppress console.log output
|
|
251
248
|
const multiBar = new cli_progress_1.MultiBar({
|
|
252
249
|
clearOnComplete: false,
|
|
253
250
|
hideCursor: true,
|
|
@@ -257,7 +254,6 @@ class DigNetwork {
|
|
|
257
254
|
const progress = multiBar.create(total, 0, { name });
|
|
258
255
|
await task(progress).finally(() => {
|
|
259
256
|
multiBar.stop();
|
|
260
|
-
console.log = oldConsoleLog; // Restore console.log
|
|
261
257
|
});
|
|
262
258
|
}
|
|
263
259
|
}
|
|
@@ -9,7 +9,7 @@ const ContentServer_1 = require("./ContentServer");
|
|
|
9
9
|
const PropagationServer_1 = require("./PropagationServer");
|
|
10
10
|
const IncentiveServer_1 = require("./IncentiveServer");
|
|
11
11
|
const blockchain_1 = require("../blockchain");
|
|
12
|
-
const
|
|
12
|
+
const DataIntegrityTree_1 = require("../DataIntegrityTree");
|
|
13
13
|
const datalayer_driver_1 = require("datalayer-driver");
|
|
14
14
|
const blockchain_2 = require("../blockchain");
|
|
15
15
|
const blockchain_3 = require("../blockchain");
|
|
@@ -97,7 +97,7 @@ class DigPeer {
|
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
// Perform tree integrity validation using the datFileContent and the root hash
|
|
100
|
-
const treeCheck =
|
|
100
|
+
const treeCheck = DataIntegrityTree_1.DataIntegrityTree.validateKeyIntegrityWithForeignTree(key, fileData.sha256, datFileContent, rootHash);
|
|
101
101
|
if (!treeCheck) {
|
|
102
102
|
console.error(`Tree validation failed for file ${key}.`);
|
|
103
103
|
filesIntegrityIntact = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DataStore as DataStoreDriver, DataStoreMetadata } from "datalayer-driver";
|
|
2
2
|
import { RootHistoryItem } from "../types";
|
|
3
|
-
import { DataIntegrityTree, DataIntegrityTreeOptions } from "
|
|
3
|
+
import { DataIntegrityTree, DataIntegrityTreeOptions } from "../DataIntegrityTree";
|
|
4
4
|
import { CreateStoreUserInputs } from "../types";
|
|
5
5
|
export declare class DataStore {
|
|
6
6
|
private storeId;
|
|
@@ -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,kBAAkB,CAAC;AAY1B,OAAO,EAAE,eAAe,EAAW,MAAM,UAAU,CAAC;AAGpD,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACzB,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,kBAAkB,CAAC;AAY1B,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;AASjD,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,GAAG,SAAS;WAYnC,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,iBAAiB,IAAI,OAAO,CAAC;QACxC,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;YA0BY,iBAAiB;IAYlB,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAsB5C,mBAAmB,IAAI,OAAO,CAAC,eAAe,EAAE,GAAG,SAAS,CAAC;IAiB7D,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IA+E5B,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAKzC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAgB5B,uBAAuB,CAClC,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkBN,cAAc,CACzB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,eAAe,CAAC;IA+Cd,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiChE,iBAAiB,IAAI,MAAM,EAAE;CAUrC"}
|
|
@@ -14,7 +14,7 @@ const config_1 = require("../utils/config");
|
|
|
14
14
|
const coins_1 = require("./coins");
|
|
15
15
|
const utils_1 = require("../utils");
|
|
16
16
|
const hashUtils_1 = require("../utils/hashUtils");
|
|
17
|
-
const
|
|
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");
|
|
@@ -33,7 +33,7 @@ class DataStore {
|
|
|
33
33
|
storeDir: config_1.STORE_PATH,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
this.tree = new
|
|
36
|
+
this.tree = new DataIntegrityTree_1.DataIntegrityTree(storeId, _options);
|
|
37
37
|
}
|
|
38
38
|
get StoreId() {
|
|
39
39
|
return this.storeId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAmBxC,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IAQnB,OAAO;WAIa,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;mBAOV,eAAe;mBAkDf,UAAU;IAyB/B,OAAO,CAAC,MAAM,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"FullNodePeer.d.ts","sourceRoot":"","sources":["../../src/blockchain/FullNodePeer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAmBxC,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkD;IAC3E,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAA0B;IAChE,OAAO,CAAC,IAAI,CAAO;IAQnB,OAAO;WAIa,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;mBAOV,eAAe;mBAkDf,UAAU;IAyB/B,OAAO,CAAC,MAAM,CAAC,eAAe;mBA8CT,WAAW;IAgGzB,OAAO,IAAI,IAAI;WAIF,mBAAmB,CACrC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,OAAO,CAAC;CAwBpB"}
|
|
@@ -107,17 +107,35 @@ class FullNodePeer {
|
|
|
107
107
|
}
|
|
108
108
|
return FullNodePeer.memoizedFetchNewPeerIPs();
|
|
109
109
|
}
|
|
110
|
-
static createPeerProxy(peer
|
|
110
|
+
static createPeerProxy(peer) {
|
|
111
111
|
return new Proxy(peer, {
|
|
112
112
|
get: (target, prop) => {
|
|
113
113
|
const originalMethod = target[prop];
|
|
114
114
|
if (typeof originalMethod === "function") {
|
|
115
115
|
return async (...args) => {
|
|
116
|
+
let timeoutId;
|
|
117
|
+
// Start the timeout to forget the peer after 1 minute
|
|
118
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
119
|
+
timeoutId = setTimeout(() => {
|
|
120
|
+
FullNodePeer.cachedPeer = null;
|
|
121
|
+
reject(new Error("Operation timed out. Reconnecting to a new peer."));
|
|
122
|
+
}, 60000); // 1 minute
|
|
123
|
+
});
|
|
116
124
|
try {
|
|
117
|
-
|
|
125
|
+
// Run the original method and race it against the timeout
|
|
126
|
+
const result = await Promise.race([
|
|
127
|
+
originalMethod.apply(target, args),
|
|
128
|
+
timeoutPromise,
|
|
129
|
+
]);
|
|
130
|
+
// Clear the timeout if the operation succeeded
|
|
131
|
+
if (timeoutId) {
|
|
132
|
+
clearTimeout(timeoutId);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
118
135
|
}
|
|
119
136
|
catch (error) {
|
|
120
|
-
|
|
137
|
+
// If the error is WebSocket-related or timeout, reset the peer
|
|
138
|
+
if (error.message.includes("WebSocket") || error.message.includes("Operation timed out")) {
|
|
121
139
|
FullNodePeer.cachedPeer = null;
|
|
122
140
|
const newPeer = await FullNodePeer.getBestPeer();
|
|
123
141
|
return newPeer[prop](...args);
|
|
@@ -149,7 +167,7 @@ class FullNodePeer {
|
|
|
149
167
|
if (ip) {
|
|
150
168
|
try {
|
|
151
169
|
const peer = await datalayer_driver_1.Peer.new(`${ip}:${FULLNODE_PORT}`, false, certFile, keyFile);
|
|
152
|
-
return FullNodePeer.createPeerProxy(peer
|
|
170
|
+
return FullNodePeer.createPeerProxy(peer);
|
|
153
171
|
}
|
|
154
172
|
catch (error) {
|
|
155
173
|
console.error(`Failed to create peer for IP ${ip}: ${error.message}`);
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,4 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./blockchain"), exports);
|
|
18
18
|
__exportStar(require("./DigNetwork"), exports);
|
|
19
19
|
__exportStar(require("./utils"), exports);
|
|
20
|
-
__exportStar(require("
|
|
20
|
+
__exportStar(require("./DataIntegrityTree"), exports);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataIntegrityTree } from "
|
|
1
|
+
import { DataIntegrityTree } from "../DataIntegrityTree";
|
|
2
2
|
/**
|
|
3
3
|
* Recursively add all files in a directory to the Merkle tree, skipping the .dig, .git folders, and files in .gitignore.
|
|
4
4
|
* @param datalayer - The DataStoreManager instance.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directoryUtils.d.ts","sourceRoot":"","sources":["../../src/utils/directoryUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"directoryUtils.d.ts","sourceRoot":"","sources":["../../src/utils/directoryUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,cACZ,iBAAiB,WACnB,MAAM,YACN,MAAM,KACd,OAAO,CAAC,IAAI,CAmCd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,eAAgB,MAAM,KAAG,MAiBxD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dignetwork/dig-sdk",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"LICENSE"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@dignetwork/data-integrity-tree": "^0.0.22",
|
|
29
28
|
"bip39": "^3.1.0",
|
|
30
29
|
"chia-bls": "^1.0.2",
|
|
31
30
|
"chia-config-loader": "^1.0.1",
|