Skip to content

feat(merkle): offload Merkle tree hashing to a Web Worker - #42

Open
malaysiaonelove wants to merge 1 commit into
Adamantine-guild:mainfrom
malaysiaonelove:feat/merkle-worker
Open

feat(merkle): offload Merkle tree hashing to a Web Worker#42
malaysiaonelove wants to merge 1 commit into
Adamantine-guild:mainfrom
malaysiaonelove:feat/merkle-worker

Conversation

@malaysiaonelove

Copy link
Copy Markdown
Contributor

Closes #23

Description

Offloads 10,000+ keccak hash operations for bulk-claim Merkle tree generation to a Web Worker so the browser doesn't hang during heavy computation. Adds a typed worker message protocol, a Promise-based client with main-thread fallback, comprehensive unit tests, and a dashboard-mounted <BulkClaimSection /> that exercises the full path over 10,000 deterministic claim leaves.

Type of change

  • Performance improvement
  • New feature
  • Bug fix
  • Breaking change
  • Documentation

Linked issue

Closes #23

Summary

  • lib/merkle.ts — async Merkle builder (buildMerkleTree), hashData, getProof, verifyProof with progress events and AbortSignal support.
  • lib/workers/crypto.worker.ts — Dedicated Web Worker source with the typed {type: 'build', jobId, items} request and {type: 'progress' | 'result' | 'error', jobId, ...} reply protocol. Hashing happens entirely off the main thread.
  • lib/merkleClient.tsbuildMerkleTreeAsync Promise API that picks the worker path when one is constructible and falls back to a setTimeout(0)-yielding main-thread coroutine otherwise; supports AbortSignal cancellation in both paths.
  • lib/merkle.test.ts (17 tests) and lib/merkleClient.test.ts (11 tests) cover the duplicate-last-odd convention across 1-, 3-, 5-, and 7-leaf trees, proof round-trip + tamper detection, a known keccak256 vector, abort handling in both paths, progress monotonicity, and the injected mock-worker dispatch.
  • components/BulkClaimSection.tsx — 10,000-claim-leaf demo wired into app/dashboard/page.tsx with a progress bar, abort button, and sr-only polite-live announcement.
  • package.json — also restores a missing comma after the test:bundle script that was blocking npm from parsing the scripts block (pre-existing).

Test evidence

Command Result
npm run typecheck exit 0, no errors
npm run lint exit 0, no warnings
npm test (lib/merkle.test.ts) 17 / 17 passing
npm test (lib/merkleClient.test.ts) 11 / 11 passing
npm run build ok (route chunks emitted under .next/)
npm run size /dashboard 104.5 KB gzip (≤ 500 KB route cap)

Accessibility

  • The visible progress-percentage text is aria-hidden="true" so screen readers aren't spammed at every batch tick.
  • Build progress is conveyed through the role="progressbar" element with aria-valuemin/max/now/label already on it.
  • A separate <div aria-live="polite" aria-atomic="true" className="sr-only"> announces a concise summary ("Merkle root computed in N ms for N leaves") once when the build resolves. The full 64-char hex root is aria-hidden so SRs don't read it aloud.
  • Errors announce through role="alert" (implicit aria-live="assertive").
  • The Cancel button is keyboard-reachable; the section uses semantic <section>, <h2>, and the same label patterns as the rest of the dashboard.

Quality checklist

  • Follows project style guidelines (TypeScript strict, file-local use client directives, Tailwind utility classes, no any casts, JSDoc on every exported helper).
  • Self-reviewed the diff end-to-end; ran the full typecheck / lint / test / build + size round after every fix.
  • No new dependencies introduced — reuses the existing viem dependency that wagmi already pulls in.
  • Tests added for every new module (lib/merkle.ts, lib/merkleClient.ts).
  • Bundle budget respected — /dashboard route is 104.5 KB gzip, well under the 500 KB route cap (merkle + crypto chunk live in their own lazy-loaded worker chunk).
  • Web-Worker fallback to the main thread is documented in lib/merkleClient.ts and exercised by a deterministic yield-count test.
  • A cancellation / AbortSignal path is tested for both the worker and the main-thread fallback.

Reviewer focus

The most non-obvious bit is the totalOps formula in lib/merkle.ts — it walks layer sizes downwards, shifting size = Math.ceil(size / 2) before the addition so the summed value is the actual number of pair hashes the layer produces. The naïve 2n - 1 formula breaks for non-power-of-2 leaf counts (n=20 would emit 39 instead of the correct 41, and the progress bar would never reach 100%).

Closes Adamantine-guild#23.

Builds 10,000+ keccak leaf hashes off the main thread via a typed
Web Worker so the browser tab no longer hangs during bulk-claim
Merkle tree generation. Falls back to main-thread execution with
setTimeout(0) yields between hash batches when Web Workers are
unavailable (SSR, sandboxed iframes, very old browsers).

New modules
  lib/merkle.ts                 async Merkle builder + getProof/verifyProof
  lib/workers/crypto.worker.ts DedicatedWorker (build/progress/result/error)
  lib/merkleClient.ts           Promise API + dispatch + AbortSignal
  lib/merkle.test.ts            17 tests
  lib/merkleClient.test.ts      11 tests
  components/BulkClaimSection.tsx  10k-leaf demo wired into the dashboard

Updated
  app/dashboard/page.tsx mounts BulkClaimSection
  package.json restores a missing comma after test:bundle (pre-existing)

Verified
  npm run typecheck  exit 0
  npm run lint       exit 0
  npm test           43 passes (merkle 17 + merkleClient 11 + pre-existing)
  npm run build      ok (.next chunks emitted)
  npm run size       /dashboard 104.5 KB gzip (budget 500 KB)

@Lakes41 Lakes41 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do resolve conflicts @malaysiaonelove

@Lakes41

Lakes41 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

This PR cannot be merged automatically because it has merge conflicts.

Please update the branch with the latest base branch and resolve the conflicts.

After the conflicts are resolved and checks pass, the automation can review it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web Worker for heavy Cryptographic hashing

2 participants