Skip to content

Commit

Permalink
https to jsr deps
Browse files Browse the repository at this point in the history
  • Loading branch information
alessandrokonrad committed Jan 29, 2025
1 parent c33f2cb commit 86e34d3
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 43 deletions.
54 changes: 30 additions & 24 deletions src/utils/merkle_tree.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Haskell implementation: https://github.com/input-output-hk/hydra-poc/blob/master/plutus-merkle-tree/src/Plutus/MerkleTree.hs
import { concat, equals } from "https://deno.land/[email protected]/bytes/mod.ts";
import { Sha256 } from "https://deno.land/[email protected]/hash/sha256.ts";
// TODO: get rid of async sha256
import { concat, equals } from "jsr:@std/bytes";
import { toHex } from "./utils.ts";

type MerkleNode = {
Expand All @@ -20,18 +20,24 @@ export class MerkleTree {
root!: MerkleNode | null;

/** Construct Merkle tree from data, which get hashed with sha256 */
constructor(data: Array<Uint8Array>) {
this.root = MerkleTree.buildRecursively(data.map((d) => sha256(d)));
static async new(data: Array<Uint8Array>): Promise<MerkleTree> {
const tree = new this();
tree.root = await MerkleTree.buildRecursively(
await Promise.all(data.map((d) => sha256(d))),
);
return tree;
}

/** Construct Merkle tree from sha256 hashes */
static fromHashes(hashes: Array<Hash>): MerkleTree {
return new this(hashes);
static async fromHashes(hashes: Array<Hash>): Promise<MerkleTree> {
const tree = new this();
tree.root = await MerkleTree.buildRecursively(hashes);
return tree;
}

private static buildRecursively(
private static async buildRecursively(
hashes: Array<Hash>,
): MerkleNode | null {
): Promise<MerkleNode | null> {
if (hashes.length <= 0) return null;
if (hashes.length === 1) {
return {
Expand All @@ -43,11 +49,11 @@ export class MerkleTree {

const cutoff = Math.floor(hashes.length / 2);
const [left, right] = [hashes.slice(0, cutoff), hashes.slice(cutoff)];
const lnode = this.buildRecursively(left);
const rnode = this.buildRecursively(right);
const lnode = await this.buildRecursively(left);
const rnode = await this.buildRecursively(right);
if (lnode === null || rnode === null) return null;
return {
node: combineHash(lnode.node, rnode.node),
node: await combineHash(lnode.node, rnode.node),
left: lnode,
right: rnode,
};
Expand All @@ -58,8 +64,8 @@ export class MerkleTree {
return this.root.node;
}

getProof(data: Uint8Array): MerkleTreeProof {
const hash = sha256(data);
async getProof(data: Uint8Array): Promise<MerkleTreeProof> {
const hash = await sha256(data);
const proof: MerkleTreeProof = [];
const searchRecursively = (tree: MerkleNode | null) => {
if (tree && equals(tree.node, hash)) return true;
Expand Down Expand Up @@ -90,23 +96,23 @@ export class MerkleTree {
return searchRecursively(this.root);
}

static verify(
static async verify(
data: Uint8Array,
rootHash: Hash,
proof: MerkleTreeProof,
): boolean {
const hash = sha256(data);
const searchRecursively = (
): Promise<boolean> {
const hash = await sha256(data);
const searchRecursively = async (
rootHash2: Hash,
proof: MerkleTreeProof,
): boolean => {
): Promise<boolean> => {
if (proof.length <= 0) return equals(rootHash, rootHash2);
const [h, t] = [proof[0], proof.slice(1)];
if (h.left) {
return searchRecursively(combineHash(h.left, rootHash2), t);
return searchRecursively(await combineHash(h.left, rootHash2), t);
}
if (h.right) {
return searchRecursively(combineHash(rootHash2, h.right), t);
return searchRecursively(await combineHash(rootHash2, h.right), t);
}
return false;
};
Expand All @@ -129,10 +135,10 @@ export class MerkleTree {

export { concat, equals };

export function sha256(data: Uint8Array): Hash {
return new Uint8Array(new Sha256().update(data).arrayBuffer());
export async function sha256(data: Uint8Array): Promise<Hash> {
return new Uint8Array(await crypto.subtle.digest("SHA-256", data));
}

export function combineHash(hash1: Hash, hash2: Hash): Hash {
return sha256(concat(hash1, hash2));
export function combineHash(hash1: Hash, hash2: Hash): Promise<Hash> {
return sha256(concat([hash1, hash2]));
}
12 changes: 4 additions & 8 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
decode,
decodeString,
encodeToString,
} from "https://deno.land/[email protected]/encoding/hex.ts";
import { decodeHex, encodeHex } from "jsr:@std/encoding/hex";
import {
Addresses,
type Assets,
Expand All @@ -15,16 +11,16 @@ import {
import { crc8 } from "../misc/crc8.ts";

export function fromHex(hex: string): Uint8Array {
return decodeString(hex);
return decodeHex(hex);
}

export function toHex(bytes: Uint8Array): string {
return encodeToString(bytes);
return encodeHex(bytes);
}

/** Convert a Hex encoded string to a Utf-8 encoded string. */
export function toText(hex: string): string {
return new TextDecoder().decode(decode(new TextEncoder().encode(hex)));
return new TextDecoder().decode(fromHex(hex));
}

/** Convert a Utf-8 encoded string to a Hex encoded string. */
Expand Down
22 changes: 11 additions & 11 deletions tests/mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,25 +218,25 @@ Deno.test("json datum to cbor datum", () => {
assertEquals(cborDatum, Codec.encodeData(jsonDatum as DataJson));
});

Deno.test("Basic Merkle tree", () => {
Deno.test("Basic Merkle tree", async () => {
const data = [new Uint8Array([0]), new Uint8Array([1])];
const merkleTree = new MerkleTree(data);
const merkleTree = await MerkleTree.new(data);
const rootHash = merkleTree.rootHash();
const proof = merkleTree.getProof(data[0]);
assert(MerkleTree.verify(data[0], rootHash, proof));
const proof = await merkleTree.getProof(data[0]);
assert(await MerkleTree.verify(data[0], rootHash, proof));
assertEquals(merkleTree.size(), 3);
});

Deno.test("Merkle tree property test", () => {
fc.assert(
fc.property(
Deno.test("Merkle tree property test", async () => {
await fc.assert(
fc.asyncProperty(
fc.array(fc.uint8Array(), { minLength: 1 }),
(data: Uint8Array[]) => {
const merkleTree = new MerkleTree(data);
async (data: Uint8Array[]) => {
const merkleTree = await MerkleTree.new(data);
const rootHash = merkleTree.rootHash();
const index = Math.floor(Math.random() * data.length);
const proof = merkleTree.getProof(data[index]);
assert(MerkleTree.verify(data[index], rootHash, proof));
const proof = await merkleTree.getProof(data[index]);
assert(await MerkleTree.verify(data[index], rootHash, proof));
assertEquals(
merkleTree.size(),
Math.max(0, data.length + (data.length - 1)),
Expand Down

0 comments on commit 86e34d3

Please sign in to comment.