josephrw commited on
Commit
128cda5
·
verified ·
1 Parent(s): e5a1d3d

Upload src/lib/merkle.ts with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/lib/merkle.ts +62 -0
src/lib/merkle.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface FileNode {
2
+ path: string;
3
+ size: number;
4
+ }
5
+
6
+ export interface MerkleLeaf {
7
+ path: string;
8
+ size: number;
9
+ hash: string;
10
+ }
11
+
12
+ export interface MerkleTree {
13
+ root: string;
14
+ leaves: MerkleLeaf[];
15
+ levels: string[][];
16
+ leafCount: number;
17
+ }
18
+
19
+ async function sha256Hex(input: string): Promise<string> {
20
+ const encoder = new TextEncoder();
21
+ const data = encoder.encode(input);
22
+ const buf = await crypto.subtle.digest("SHA-256", data);
23
+ return Array.from(new Uint8Array(buf))
24
+ .map((b) => b.toString(16).padStart(2, "0"))
25
+ .join("");
26
+ }
27
+
28
+ export async function buildMerkleTree(files: FileNode[]): Promise<MerkleTree> {
29
+ if (!files.length) {
30
+ return { root: "", leaves: [], levels: [], leafCount: 0 };
31
+ }
32
+
33
+ const leaves: MerkleLeaf[] = [];
34
+ for (const f of files) {
35
+ const hash = await sha256Hex(`${f.path}:${f.size}`);
36
+ leaves.push({ path: f.path, size: f.size, hash });
37
+ }
38
+
39
+ leaves.sort((a, b) => a.hash.localeCompare(b.hash));
40
+
41
+ let current = leaves.map((l) => l.hash);
42
+ const levels: string[][] = [current.slice()];
43
+
44
+ while (current.length > 1) {
45
+ const next: string[] = [];
46
+ for (let i = 0; i < current.length; i += 2) {
47
+ const left = current[i];
48
+ const right = current[i + 1] ?? left;
49
+ const combined = await sha256Hex(`${left}${right}`);
50
+ next.push(combined);
51
+ }
52
+ current = next;
53
+ levels.push(current.slice());
54
+ }
55
+
56
+ return {
57
+ root: current[0] ?? "",
58
+ leaves,
59
+ levels,
60
+ leafCount: leaves.length,
61
+ };
62
+ }