e6ff57a3346e93c88d2ea00adce7fe590355e1fc
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e8bf9e20c7
|
fix(quantus-crypto): strip wasm-bindgen's fetch-based init from the shipped glue
The package could not be bundled. Every webpack consumer failed with:
Module not found: Error: Can't resolve 'quantus_crypto_bg.wasm'
in node_modules/@quantus/crypto/generated
wasm-bindgen's async `__wbg_init` contains
module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url);
and webpack resolves `new URL(..., import.meta.url)` statically, at build time,
whether or not the branch can run. The file is not in the package — the wasm
ships base64'd in bytes.js, which is the entire point of this package — so the
build failed on a code path we never call.
node never sees it, which is why ten Rust tests, twelve consumer assertions and a
browser probe all passed while the package was unusable in a bundler. It took a
real extension build to surface, and that is the useful lesson: this package's
consumers bundle, and nothing in its own test suite does.
So the dead init is removed after bindgen runs. Shipping a second copy of the
wasm to satisfy a path we do not use would be the wide fix; deleting generated
code we never call is the narrow one.
The stripper asserts the shape it expects and throws if wasm-bindgen changes it,
rather than silently no-opping — a build that quietly stopped stripping would
ship the broken package again. It also re-checks that no reference to the .wasm
filename survives.
Published as 0.1.1.
Refs quantus/wasm#1, quantus/extension#2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
|
||
|
|
a6d3685c59
|
refactor(quantus-crypto): drop the @polkadot/wasm-util dependency
It cost more than it saved. Two problems, the second only visible once quantus/common tried to consume this package: Its index re-exports packageDetect, whose only job is a side effect registering with @polkadot/util — a peer dependency inherited for nothing. Deep imports (/base64, /fflate) avoided that. But it is a workspace package, so a symlinked consumer resolves its dependencies through *this* repo's node_modules, where @polkadot/wasm-util points at the package source rather than its build and carries no exports map. Node follows symlinks to their realpath, so `@polkadot/wasm-util/base64` failed to resolve from quantus/common no matter which yarn protocol was used — portal: and link: behave the same once the realpath is taken. So: fflate directly for zlib inflate, and fifteen lines for base64 rather than a dependency at all. Deliberately not atob or Buffer.from — the first is browser-only, the second node-only, and this runs in an MV3 service worker, a Worker, node tests and a bundled extension page. The package is now self-contained apart from fflate, which resolves normally from any checkout. Size is unchanged at 234,292 raw / 109,649 zlib / 146,200 base64. Refs quantus/wasm#1, quantus/common#2 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f |
||
|
|
8323e442d9
|
test(quantus-crypto): browser probes for the constraints node cannot test
cargo test and the consumer test both run in node, which is neither a browser nor a service worker. Two probes cover the rest. The automated one is a module Worker served under the exact extension_pages CSP from both manifests. A module Worker has no window and no document, which is the property that matters — an MV3 service worker has neither either. On Firefox: hasDOM: false hasWindow: false initWasm: ok (9.0 ms cold) keygen: 1.0 ms account: matches quantus-cli sign: 3.0 ms (4627 bytes) verify: 1.0 ms ok ctx sep: ok (rejected under spec-147 ctx) 9 ms to base64-decode 146 KB, inflate it to 234 KB and instantiate. That is the number the MV3 lifetime question turns on — a worker killed between messages pays it on every wake — and it settles the cold-start concern raised in quantus/wasm#1. Those are ML-DSA-87 timings, the larger parameter set, so 65 is cheaper still. The CSP is enforced, not merely declared: an earlier version of the page used an inline script and Firefox blocked it, which is why main.js is a separate file. The manual one is a real MV3 extension whose service worker imports the package at module scope. Loading an unpacked extension needs an OS file dialog, so it cannot be driven from here and is documented for a human to load. It covers chrome.runtime messaging and the real kill-and-restart lifecycle rather than a stand-in for it. Refs quantus/wasm#1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f |
||
|
|
b882f914e7
|
build(quantus-crypto): buildable, installable and tested as a package
The JS build now runs end to end and the built package has been consumed the way quantus/common will consume it. Four things had to be worked out. polkadot-dev-build-ts will not build this package. It returns early for any name not starting with @polkadot/, in both buildJs and when collecting locals for import rewriting. Renaming into someone else's scope to satisfy a string check would be worse than not using the tool, and nothing is lost: this package needs no deno variant, no rollup bundle, no cross-package import rewriting. A plain tsc build lives in scripts/build-quantus-js.sh, which also keeps `yarn build:js` byte-identical to upstream's behaviour. binaryen 105 silently breaks the wasm. Upstream pins version_105 (2021), which predates the externref tables wasm-bindgen 0.2.128 emits; wasm-opt "optimises" the table into something that fails at instantiation with `WebAssembly.Table. grow(): failed to grow table by 4`. The wasm is valid before wasm-opt and broken after, every cargo test still passes, and it only surfaces when a consumer tries to init. install-build-deps.sh now fetches binaryen 123 alongside, exactly as it does a second wasm-bindgen. The wasm-util dependency is imported deeply. Its package index re-exports packageDetect, whose only job is a side effect registering with @polkadot/util — a peer dependency we would inherit for nothing. base64 and fflate are pure functions with no dependencies, so the deep paths are both lighter and honest. ESM only, and the CJS scaffolding is removed. The consumers are ESM and the wasm-bindgen glue is ESM-only, so a CJS variant would mean a second generated glue or hand-written marshalling. Revisit if quantus/common's CJS build needs it. Also: the pack step must run after tsc, which clears build/; the checked-in bindings are refreshed by the build so they cannot drift; and both test suites are wired into the repo's test script, which previously ran wasm-crypto's only. The consumer test stages a real node_modules layout rather than testing in place, because in this repo node_modules/@polkadot/wasm-util symlinks to the package source, which carries no exports map — so a deep import resolves for a real consumer and fails here for reasons that have nothing to do with our package. Staging tests module resolution too, which is half of what can break in a published package. It is also what caught the binaryen fault. Post-wasm-opt: 234,292 raw / 109,649 zlib / 146,200 base64 — smaller than upstream's entire wasm-crypto blob (335,277 / 168,782 / 225,044). Refs quantus/wasm#1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f |
||
|
|
1f1f5729ab
|
feat(quantus-crypto): JS surface, build scripts and CSP-safe sync init
Wraps the crate for JS consumers and adds the build that produces it.
Init deliberately avoids fetch and avoids @polkadot/wasm-bridge. The consumer is
an MV3 service worker under `script-src 'self' 'wasm-unsafe-eval'`, which can
compile WASM but not usefully fetch it, and which can be cold-started between any
two messages; callers like pair.sign() are synchronous and have no await to give.
So the WASM is zlib-compressed and base64'd into bytes.js at build time and
instantiated with wasm-bindgen's initSync. Bridge is not usable here regardless:
it implements the 0.2.79 JS-heap ABI and this crate builds with 0.2.128, which
uses externref tables.
build-quantus.sh is separate from build-wasm.sh rather than folded into it,
because that script drives the nightly-2022-06-24 + xargo build wasm-crypto
needs. install-build-deps.sh gains a second wasm-bindgen for the same reason —
the two ABIs cannot share a binary. No asm.js step: wasm2js over ML-DSA would be
enormous and slow, and every context we ship into permits wasm.
bytes.js is emitted in both module systems, with the CJS copy under a directory
carrying its own {"type":"commonjs"} — the package is "type": "module" and node
otherwise refuses to load an exports.-style file from it.
Proven end to end against the real build output: base64 -> inflate -> initSync
with no fetch, crystal_alice's account id matching the CLI through the JS path,
sig||pk matching the runtime's fixed-array size, and JsError surfacing as a JS
exception across the boundary.
Sizes are read from the crate rather than exposed as constants to copy. They are
consensus-critical and a drifted JS constant would re-frame every byte after the
signature while looking entirely healthy.
Refs quantus/wasm#1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
|
||
|
|
6eb04f63ac
|
feat(quantus-crypto): ML-DSA, Poseidon2 and HD derivation as a separate crate
Wraps the chain's own crypto crates for the browser: qp-rusty-crystals-dilithium
(ML-DSA-65 and ML-DSA-87), qp-poseidon-core for the account-id hash, and
qp-rusty-crystals-hdwallet for BIP44 derivation. Nothing is reimplemented — a
browser wallet that disagreed with the chain about a key or a signature would
emit well-formed output the chain rejects, with nothing on this side able to
tell.
A separate crate rather than more files in wasm-crypto, because the two cannot
share a Cargo graph. wasm-crypto builds with nightly-2022-06-24 against a
2019-era dependency set; the ML-DSA crates use inline `const {}` blocks that
need Rust >= 1.79. Bumping the older one would mean rewriting upstream's
sr25519/ed25519 build, which is the thing most worth leaving alone so rebases
stay boring. wasm-crypto is untouched here.
The scheme selector is the chain's own signature-enum variant index (0 for
ML-DSA-87, 1 for ML-DSA-65), so the number threaded through this API is the
byte that ends up on the wire and there is no mapping to get backwards. Key and
signature sizes are exported rather than left for JS to hardcode: they are
consensus-critical and a drifted constant would mis-frame every byte after the
signature while looking healthy.
Logic is split from the #[wasm_bindgen] wrappers because JsError cannot be
constructed off-wasm, which made every error path untestable by cargo test —
and the error paths are what most needs testing.
Verified against the `quantus` CLI 2.2.2 as an independent oracle, not against
our own output: the three dev-genesis account ids, and HD derivation at both
schemes' default paths from the public Substrate dev phrase. Context separation
is pinned too — a signature made under QUANTUS_EXTRINSIC must not verify under
the empty context, which is what makes the spec-148 boundary detectable rather
than a silent chain rejection.
Refs quantus/wasm#1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
|