Skip to content

Add WASM bindings (offline DIGStore spend bundles) + npm package CI - #33

Open
MichaelTaylor3d wants to merge 1 commit into
mainfrom
wasm-bindings-plan
Open

Add WASM bindings (offline DIGStore spend bundles) + npm package CI#33
MichaelTaylor3d wants to merge 1 commit into
mainfrom
wasm-bindings-plan

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented May 30, 2026

Copy link
Copy Markdown
Contributor

What this adds

WebAssembly bindings for the DataLayer driver, exposing the offline subset of the NAPI surface so a browser can construct DIGStore spend bundles without a node. Plus a CI job that builds the wasm artifact, runs a wasm-vs-NAPI parity test, and publishes @dignetwork/datalayer-driver-wasm.

Networking is intentionally absent from the wasm crate — no Peer, Tls, connect, sync, or broadcast. Reading coins and broadcasting the finished bundle stay the consumer's job. The core crate gained a native default feature that gates the async client and its tokio runtime, which is what makes a wasm32-unknown-unknown build possible at all.

Rebase onto main (4.0.0)

The branch was based on 3.0.0. main has since landed a large dependency upgrade, so the 14 branch commits were squashed to one and the conflict set resolved once rather than replayed per commit.

Six files conflicted: Cargo.toml, Cargo.lock, src/error.rs, src/types.rs, src/lib.rs, src/wallet.rs.

The substantive conflict was the dependency shape. main upgraded chia-* 0.26 → 0.36.1 and chia-wallet-sdk 0.30 → 0.34.0; this branch had split the SDK facade into individual sub-crates so the wasm build never reaches chia-sdk-client. Both were kept: main's versions with the branch's split, at chia-sdk-{driver,signer,types,utils} 0.34.0, with chia-wallet-sdk itself now optional behind native. The split is load-bearing rather than stylistic — chia-wallet-sdk's lib.rs re-exports chia_sdk_client and chia_sdk_test unconditionally, so depending on the facade at all drags tokio and TLS onto the wasm path regardless of features.

main had independently migrated off the chia meta-crate to the same individual chia-* crates the branch needed, so that half of the branch's work was already done upstream and the dep:chia entry was dropped.

The remaining conflicts were mechanical: main used chia_wallet_sdk::{driver,client,coinset,utils} paths where the branch used the sub-crates directly and cfg-gated the native-only ones. Resolved toward the branch in every case, keeping main's new code intact.

The lockfile was regenerated from main's rather than merged by hand.

Two things the rebase forced beyond conflict resolution

getrandom 0.3 landed on the wasm path. chia-sdk-driver 0.34 depends on it directly, and it compile_error!s for wasm32-unknown-unknown unless a backend is named. The feature alone is insufficient — it also needs a cfg. Both halves are committed: the wasm_js feature via an aliased getrandom_v03 dependency, and --cfg getrandom_backend="wasm_js" scoped to the wasm target in .cargo/config.toml. wasm/GETRANDOM.md records why removing either one breaks the build. Both getrandom majors are now on the wasm path (0.2 arrives via chialisp); each needs its own backend selection.

Import gating in src/wallet.rs. main's new native-only code pulled several imports into use on the native path that are unused on the wasm path, which -D warnings would have failed. Seven import groups were split so each side is warning-clean.

The money path — what to look at first

Every bundle builder delegates to the core crate rather than reimplementing a spend layout. mintStore calls datalayer_driver::mint_store, oracleSpend calls oracle_spend, and so on for melt, both update paths, sendXch, addFee, and createServerCoin. The wasm crate contributes serde conversion at the JS boundary and nothing else — no second construction of any puzzle or solution.

wasm/tests/parity.mjs asserts that byte-for-byte. It builds a mintStore bundle through both the wasm and NAPI paths from identical deterministic inputs and compares each coin spend's puzzleReveal and solution, the launcher id, the aggregated signature, and the final serialized bundle hex, then repeats the comparison for melt and sendXch. It is load-bearing: perturbing one metadata field on the NAPI side fails it at coinSpends[0].solution.

Its limit is worth stating. It compares wasm against NAPI, which share the same Rust core, so it proves the wasm boundary does not corrupt a bundle — it cannot detect a wrong-but-consistent layout in the core itself. That is the intended scope, since the core is the same builder NAPI already ships.

Four functions accept raw private key bytes and sign in-process: signCoinSpends, signMessage, secretKeyToPublicKey, and masterSecretKeyToWalletSyntheticSecretKey. This is NAPI parity, and nothing is transmitted anywhere, but in a browser it means the key is in JavaScript memory where any script on the origin can reach it. There is no seed or mnemonic entry point. wasm/README.md now states this plainly and steers callers toward building unsigned bundles here and signing them elsewhere. A reviewer should decide deliberately whether these four belong in a browser-targeted package at all — the builders themselves take only public keys and puzzle hashes, so the package is fully useful without them.

Version

4.1.0, derived off main's current 4.0.0 — minor, for compatible new capability. The pre-rebase number was chosen against 3.0.0. Cargo.toml, wasm/Cargo.toml, wasm/package.json, and wasm/package-lock.json agree; Cargo.lock is regenerated.

Verification

All local, on the rebased tree:

  • cargo clippy --all-targets --all-features -- -D warnings — clean (the repo's own gate command)
  • cargo fmt --all -- --check — clean
  • cargo machete — no unused dependencies
  • cargo check -p datalayer-driver and --no-default-features — both warning-free, which is the real test of the feature gating
  • cargo check -p datalayer-driver-wasm --target wasm32-unknown-unknown — clean
  • cargo test --workspace — 5 passed, 0 failed
  • wasm-pack build --target nodejs against the upgraded 0.34 SDK — succeeds
  • node wasm/tests/parity.mjs — all parity checks pass, and fails correctly under a deliberate tamper

Notes for the merge

@dignetwork/datalayer-driver-wasm already exists on npm at 3.0.0, so this is not a first publish and does not need the manual 2FA step a new scoped package would.

main's own latest CI run is red on its Publish to NPM job, independent of this branch and present before the rebase.

napi/package.json (3.0.0) and napi/package-lock.json (0.1.38) disagree with Cargo.toml on main already; that drift was left alone rather than folded into this PR.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

1 participant