Add WASM bindings (offline DIGStore spend bundles) + npm package CI - #33
Open
MichaelTaylor3d wants to merge 1 commit into
Open
Add WASM bindings (offline DIGStore spend bundles) + npm package CI#33MichaelTaylor3d wants to merge 1 commit into
MichaelTaylor3d wants to merge 1 commit into
Conversation
MichaelTaylor3d
force-pushed
the
wasm-bindings-plan
branch
from
August 8, 2026 16:01
3abf3bf to
04c8c08
Compare
Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d
force-pushed
the
wasm-bindings-plan
branch
from
August 8, 2026 16:08
04c8c08 to
7cd1718
Compare
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.
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, orbroadcast. Reading coins and broadcasting the finished bundle stay the consumer's job. The core crate gained anativedefault feature that gates the async client and its tokio runtime, which is what makes awasm32-unknown-unknownbuild possible at all.Rebase onto main (4.0.0)
The branch was based on 3.0.0.
mainhas 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.
mainupgradedchia-*0.26 → 0.36.1 andchia-wallet-sdk0.30 → 0.34.0; this branch had split the SDK facade into individual sub-crates so the wasm build never reacheschia-sdk-client. Both were kept: main's versions with the branch's split, atchia-sdk-{driver,signer,types,utils}0.34.0, withchia-wallet-sdkitself now optional behindnative. The split is load-bearing rather than stylistic —chia-wallet-sdk'slib.rsre-exportschia_sdk_clientandchia_sdk_testunconditionally, so depending on the facade at all drags tokio and TLS onto the wasm path regardless of features.mainhad independently migrated off thechiameta-crate to the same individualchia-*crates the branch needed, so that half of the branch's work was already done upstream and thedep:chiaentry was dropped.The remaining conflicts were mechanical:
mainusedchia_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-driver0.34 depends on it directly, and itcompile_error!s forwasm32-unknown-unknownunless a backend is named. The feature alone is insufficient — it also needs a cfg. Both halves are committed: thewasm_jsfeature via an aliasedgetrandom_v03dependency, and--cfg getrandom_backend="wasm_js"scoped to the wasm target in.cargo/config.toml.wasm/GETRANDOM.mdrecords why removing either one breaks the build. Both getrandom majors are now on the wasm path (0.2 arrives viachialisp); 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 warningswould 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.
mintStorecallsdatalayer_driver::mint_store,oracleSpendcallsoracle_spend, and so on for melt, both update paths,sendXch,addFee, andcreateServerCoin. The wasm crate contributes serde conversion at the JS boundary and nothing else — no second construction of any puzzle or solution.wasm/tests/parity.mjsasserts that byte-for-byte. It builds amintStorebundle through both the wasm and NAPI paths from identical deterministic inputs and compares each coin spend'spuzzleRevealandsolution, 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 atcoinSpends[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, andmasterSecretKeyToWalletSyntheticSecretKey. 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.mdnow 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 current4.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, andwasm/package-lock.jsonagree;Cargo.lockis 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— cleancargo machete— no unused dependenciescargo check -p datalayer-driverand--no-default-features— both warning-free, which is the real test of the feature gatingcargo check -p datalayer-driver-wasm --target wasm32-unknown-unknown— cleancargo test --workspace— 5 passed, 0 failedwasm-pack build --target nodejsagainst the upgraded 0.34 SDK — succeedsnode wasm/tests/parity.mjs— all parity checks pass, and fails correctly under a deliberate tamperNotes for the merge
@dignetwork/datalayer-driver-wasmalready 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 itsPublish to NPMjob, independent of this branch and present before the rebase.napi/package.json(3.0.0) andnapi/package-lock.json(0.1.38) disagree withCargo.tomlon main already; that drift was left alone rather than folded into this PR.