feat(wasm-sdk): add prepare_* APIs for idempotent document state transitions#3091
Draft
thepastaclaw wants to merge 25 commits into
Draft
feat(wasm-sdk): add prepare_* APIs for idempotent document state transitions#3091thepastaclaw wants to merge 25 commits into
thepastaclaw wants to merge 25 commits into
Conversation
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.
Issue
Closes #3090
Problem
The high-level document APIs (
documentCreate,documentReplace,documentDelete) in the WASM SDK atomically bundle nonce management, ST construction, signing, broadcasting, and waiting. On timeout, callers cannot rebroadcast the same signed ST — retrying creates a duplicate with a new nonce.Solution
Implements Option A (Two-Phase API) from the issue: add
prepare_*variants for each document operation that return a signedStateTransitionwithout broadcasting:prepareDocumentCreate()— build, sign, return STprepareDocumentReplace()— build, sign, return STprepareDocumentDelete()— build, sign, return STThese pair with the already-existing
broadcastStateTransition()andwaitForResponse()methods inbroadcast.rs.Usage Pattern
This gives applications full control over retry and caching strategy while leveraging Platform's built-in duplicate ST rejection.
Changes
packages/wasm-sdk/src/state_transitions/document.rs:prepareDocumentCreate()— builds and signs a create ST without broadcastingprepareDocumentReplace()— builds and signs a replace ST without broadcastingprepareDocumentDelete()— builds and signs a delete ST without broadcastingCompatibility note: one-shot revision guards
The existing one-shot
documentCreate()anddocumentReplace()APIs remain available, but this PR now rejects revision/API mismatches instead of silently routing them through the opposite transition type. In particular:documentCreate()accepts an unset revision orINITIAL_REVISION; documents with any other explicit revision now fail withInvalidArgumentinstead of being treated as replace transitions.documentReplace()requires a revision greater thanINITIAL_REVISION; missing, zero, or initial revisions now fail withInvalidArgumentinstead of being treated as create transitions.Consumers that reused stale documents or relied on the previous silent create/replace routing should audit those call sites before upgrading.
Rust SDK consumers should also note the related
PutDocument::put_to_platformtightening: replace calls withrevision = Some(0)are now rejected instead of being routed through the create branch, and create calls that passdocument_state_transition_entropy = Some(entropy)now validate thatdocument.idmatchesDocument::generate_document_id_v0(...)before nonce allocation. The in-treers-platform-walletcaller usesrevision: Nonefor creates andSome(current_revision + 1)for updates, so it is unaffected.Compatibility note: structure validation error ordering
ensure_valid_state_transition_structurenow treats DPP's structure-validation sentinel (UnsupportedFeatureErrorentries whose feature name starts with"structure validation") as a no-op only when every error is that sentinel. Mixed validation results are not filtered: every original diagnostic is preserved, but non-UnsupportedFeatureErrorfailures are reordered ahead of unsupported-feature entries so the typed primary error returned to callers is the actionable validation failure. Non-sentinelUnsupportedFeatureErrorentries (for example, unsupported sub-features inside an otherwise validated transition) still surface as real errors.Compatibility note: native document builders
Native rs-sdk document create/replace builders now honor
PutSettings.user_fee_increaseandPutSettings.state_transition_creation_optionspassed throughwith_settings(...)when dedicated builder setters were not used, matching the delete builder and high-level SDK paths. Delete buildersign(...)now rolls back allocated identity-contract nonces on pre-broadcast failures, andsign_with_nonce(...)runs local structure validation before returning.Testing
The existing document operation tests validate the build/sign/broadcast pipeline. The prepare variants reuse the same construction logic, stopping before broadcast. The
broadcastStateTransitionandwaitForResponsemethods are already tested inbroadcast.rs. Manual testing with the yappr application (which prompted this issue) confirms the two-phase pattern works correctly.Validation
Build verification
# WASM SDK builds successfully with new prepare_* APIs cargo check -p wasm-sdkExisting test coverage
The
prepare_*methods reuse the same internal construction paths as the existing all-in-one document operations:prepareDocumentCreate/prepareDocumentReplacedelegate to strict rs-sdk helpers that validate create-vs-replace intent, validate create entropy/document-id consistency, allocate the identity-contract nonce, build, sign, and roll back the nonce on pre-broadcast failuresprepareDocumentDeleteuses the shared rs-sdk delete helper, the same build/sign/validate path used by the existingdocumentDeleteflowbroadcastStateTransition()andwaitForResponse()(the "execute" half) are already tested inbroadcast.rsExisting CI test suites (
cargo test -p wasm-sdk, platform integration tests) validate these shared code paths.Manual / integration testing
prepareDocumentCreate()returns a signedStateTransitiontoBytes(), cached, then broadcast viabroadcastStateTransition()Input validation
prepareDocumentReplacerejects documents withINITIAL_REVISION(guards against accidental create-as-replace)prepareDocumentCreatevalidates entropy is present and exactly 32 bytesprepareDocumentDeleteaccepts both fullDocumentinstances and minimal identifier objectsSummary by CodeRabbit