feat(merkle): offload Merkle tree hashing to a Web Worker - #42
Open
malaysiaonelove wants to merge 1 commit into
Open
feat(merkle): offload Merkle tree hashing to a Web Worker#42malaysiaonelove wants to merge 1 commit into
malaysiaonelove wants to merge 1 commit into
Conversation
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
requested changes
Jul 27, 2026
Lakes41
left a comment
Contributor
There was a problem hiding this comment.
Please do resolve conflicts @malaysiaonelove
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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Linked issue
Closes #23
Summary
lib/merkle.ts— async Merkle builder (buildMerkleTree),hashData,getProof,verifyProofwith progress events andAbortSignalsupport.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.ts—buildMerkleTreeAsyncPromise API that picks the worker path when one is constructible and falls back to asetTimeout(0)-yielding main-thread coroutine otherwise; supportsAbortSignalcancellation in both paths.lib/merkle.test.ts(17 tests) andlib/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 intoapp/dashboard/page.tsxwith a progress bar, abort button, and sr-only polite-live announcement.package.json— also restores a missing comma after thetest:bundlescript that was blockingnpmfrom parsing the scripts block (pre-existing).Test evidence
npm run typechecknpm run lintnpm test(lib/merkle.test.ts)npm test(lib/merkleClient.test.ts)npm run build.next/)npm run size/dashboard104.5 KB gzip (≤ 500 KB route cap)Accessibility
aria-hidden="true"so screen readers aren't spammed at every batch tick.role="progressbar"element witharia-valuemin/max/now/labelalready on it.<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 isaria-hiddenso SRs don't read it aloud.role="alert"(implicitaria-live="assertive").<section>,<h2>, and the same label patterns as the rest of the dashboard.Quality checklist
use clientdirectives, Tailwind utility classes, noanycasts, JSDoc on every exported helper).typecheck / lint / test / build + sizeround after every fix.lib/merkle.ts,lib/merkleClient.ts)./dashboardroute is 104.5 KB gzip, well under the 500 KB route cap (merkle + crypto chunk live in their own lazy-loaded worker chunk).lib/merkleClient.tsand exercised by a deterministic yield-count test.Reviewer focus
The most non-obvious bit is the
totalOpsformula inlib/merkle.ts— it walks layer sizes downwards, shiftingsize = Math.ceil(size / 2)before the addition so the summed value is the actual number of pair hashes the layer produces. The naïve2n - 1formula 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%).