diff --git a/CHANGELOG.md b/CHANGELOG.md index b794a51..bdbdefd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.0.1-alpha.105](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.104...v0.0.1-alpha.105) (2024-10-01) + + +### Features + +* add the ability to check integrity with verified sha256 ([c5e6c44](https://github.com/DIG-Network/dig-chia-sdk/commit/c5e6c443b960dfffea7fc85bfd9bcb28f4dfa34f)) + ### [0.0.1-alpha.104](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.103...v0.0.1-alpha.104) (2024-09-30) diff --git a/package-lock.json b/package-lock.json index dc31b1a..2a655dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.104", + "version": "0.0.1-alpha.105", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.104", + "version": "0.0.1-alpha.105", "license": "ISC", "dependencies": { "@dignetwork/datalayer-driver": "^0.1.28", diff --git a/package.json b/package.json index 4dacb41..404c24b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dignetwork/dig-sdk", - "version": "0.0.1-alpha.104", + "version": "0.0.1-alpha.105", "description": "", "type": "commonjs", "main": "./dist/index.js", diff --git a/src/DataIntegrityTree/DataIntegrityTree.ts b/src/DataIntegrityTree/DataIntegrityTree.ts index 509c090..c5563f4 100644 --- a/src/DataIntegrityTree/DataIntegrityTree.ts +++ b/src/DataIntegrityTree/DataIntegrityTree.ts @@ -784,6 +784,8 @@ class DataIntegrityTree { * @param sha256 - The SHA-256 hash of the file. * @param serializedTree - The foreign serialized Merkle tree. * @param expectedRootHash - The expected root hash of the Merkle tree. + * @param dataDir - The directory where the data is stored. + * @param verifiedSha256 - A boolean indicating if the SHA-256 hash has already been verified. * @returns A boolean indicating if the SHA-256 is present in the foreign tree and the root hash matches. */ static async validateKeyIntegrityWithForeignTree( @@ -791,7 +793,8 @@ class DataIntegrityTree { sha256: string, serializedTree: object, expectedRootHash: string, - dataDir: string + dataDir: string, + verifiedSha256: boolean ): Promise { if (!isHexString(hexkey)) { throw new Error("key must be a valid hex string"); @@ -802,84 +805,124 @@ class DataIntegrityTree { if (!isHexString(expectedRootHash)) { throw new Error("expectedRootHash must be a valid hex string"); } - - // File path based on sha256 + + if (!verifiedSha256) { + // Verify the file's SHA-256 hash + const isFileValid = await this.verifyFileHash(sha256, dataDir); + if (!isFileValid) { + return false; + } + } + + // Deserialize and verify the tree + const tree = this.deserializeAndVerifyTree(serializedTree, expectedRootHash); + if (!tree) { + return false; + } + + // Check if the combined hash exists in the foreign tree's leaves + const isInTree = this.checkHashInTree(tree, hexkey, sha256); + + return isInTree; + } + + // Function to verify the file's SHA-256 hash + private static async verifyFileHash( + sha256: string, + dataDir: string + ): Promise { const filePath = path.join(dataDir, sha256.match(/.{1,2}/g)!.join("/")); - + // Check if the file exists if (!fs.existsSync(filePath)) { throw new Error(`File at path ${filePath} does not exist`); } - + const compressedReadStream = fs.createReadStream(filePath); const decompressStream = zlib.createGunzip(); const hash = crypto.createHash("sha256"); - + // Process file decompression and hash comparison return new Promise((resolve, reject) => { compressedReadStream.pipe(decompressStream); - + decompressStream.on("data", (chunk) => { hash.update(chunk); }); - + decompressStream.on("end", () => { const uncompressedSha256 = hash.digest("hex"); - + if (uncompressedSha256 !== sha256) { console.warn( `File hash mismatch. Expected: ${sha256}, got: ${uncompressedSha256}` ); return resolve(false); } - - // Deserialize the foreign tree - const leaves = (serializedTree as any).leaves.map((leaf: string) => - Buffer.from(leaf, "hex") - ); - const tree = new MerkleTree(leaves, SHA256, { sortPairs: true }); - - // Verify that the deserialized tree's root matches the expected root hash - const treeRootHash = tree.getRoot().toString("hex"); - if (treeRootHash !== expectedRootHash) { - console.warn( - `Expected root hash ${expectedRootHash}, but got ${treeRootHash}` - ); - return resolve(false); - } - - // Rebuild the files map from the serialized tree - // @ts-ignore - tree.files = new Map( - Object.entries((serializedTree as any).files).map( - ([key, value]: [string, any]) => [ - key, - { hash: value.hash, sha256: value.sha256 }, - ] - ) - ); - - // Check if the SHA-256 exists in the foreign tree's files - const combinedHash = crypto - .createHash("sha256") - .update(`${hexkey}/${sha256}`) - .digest("hex"); - - const leaf = Buffer.from(combinedHash, "hex"); - const isInTree = tree.getLeafIndex(leaf) !== -1; - - resolve(isInTree); + resolve(true); }); - + decompressStream.on("error", (err) => { reject(err); }); - + compressedReadStream.on("error", (err) => { reject(err); }); }); } + + // Function to deserialize and verify the tree + private static deserializeAndVerifyTree( + serializedTree: object, + expectedRootHash: string + ): MerkleTree | null { + // Deserialize the foreign tree + const leaves = (serializedTree as any).leaves.map((leaf: string) => + Buffer.from(leaf, "hex") + ); + const tree = new MerkleTree(leaves, SHA256, { sortPairs: true }); + + // Verify that the deserialized tree's root matches the expected root hash + const treeRootHash = tree.getRoot().toString("hex"); + if (treeRootHash !== expectedRootHash) { + console.warn( + `Expected root hash ${expectedRootHash}, but got ${treeRootHash}` + ); + return null; + } + + // Rebuild the files map from the serialized tree + // @ts-ignore + tree.files = new Map( + Object.entries((serializedTree as any).files).map( + ([key, value]: [string, any]) => [ + key, + { hash: value.hash, sha256: value.sha256 }, + ] + ) + ); + + return tree; + } + + // Function to check if the combined hash exists in the tree + private static checkHashInTree( + tree: MerkleTree, + hexkey: string, + sha256: string + ): boolean { + const combinedHash = crypto + .createHash("sha256") + .update(`${hexkey}/${sha256}`) + .digest("hex"); + + const leaf = Buffer.from(combinedHash, "hex"); + const isInTree = tree.getLeafIndex(leaf) !== -1; + + return isInTree; + } + } export { DataIntegrityTree };