Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
139 changes: 91 additions & 48 deletions src/DataIntegrityTree/DataIntegrityTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,14 +784,17 @@ 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(
hexkey: string,
sha256: string,
serializedTree: object,
expectedRootHash: string,
dataDir: string
dataDir: string,
verifiedSha256: boolean
): Promise<boolean> {
if (!isHexString(hexkey)) {
throw new Error("key must be a valid hex string");
Expand All @@ -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<boolean> {
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 };
Loading