feat(sdk): carry consensus error codes through the wallet FFI - #4838
Conversation
A token operation Platform rejected reached the mobile apps as text only: platform-wallet rendered the SDK error into `TokenError(String)`, so the FFI result, the JNI exception and the Swift error had nothing but a message left, and the example apps recognised the once-per-identity already-claimed rejection (consensus code 40722) by matching that message. platform-wallet keeps the `dash_sdk::Error` of a failed token state transition in a new `TokenOperationFailed` variant, with the same Display text, and exposes the consensus error it holds through `PlatformWalletError::consensus_error`. `PlatformWalletFFIResult` gains `consensus_code` and `consensus_kind` (basic, signature, fee, state), which the conversion from `PlatformWalletError` fills in. The result code and the message are unchanged, so existing hosts see what they saw before. Kotlin surfaces them as `DashSDKException.consensusError` and `DashSdkError.consensusError`; Swift as a `consensusRejection` case and `PlatformWalletError.consensusError`. Both example apps now compare the consensus code and no longer read the message. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 9 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: dashpay/platform/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (29)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR HygieneState: waiting-bots · commit
Self-review is an author attestation that you have read the diff: This report does not bypass CI or repository protection rules. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## v4.2-dev #4838 +/- ##
============================================
+ Coverage 73.86% 76.56% +2.70%
============================================
Files 2977 2978 +1
Lines 434179 433987 -192
============================================
+ Hits 320696 332287 +11591
+ Misses 113483 101700 -11783
🚀 New features to boost your workflow:
|
Issue being fixed or feature implemented
Follow-up to #4829, where a reviewer asked for a structured result instead of text matching.
A wallet token operation that Platform rejects reaches the mobile apps as text only.
platform-walletrendered the SDK error intoPlatformWalletError::TokenError(format!("Token claim failed: {e}")), soPlatformWalletFFIResult, the JNIDashSDKExceptionand the SwiftPlatformWalletErrorhad nothing but a message left. Both example apps therefore recognised the once-per-identity already-claimed rejection (TokenOncePerIdentityDistributionAlreadyClaimedError, consensus code 40722) by matching that message: a digit-boundary scan for40722plus the sentence rs-dpp renders. A false positive there is permanent, because the local "already claimed" record is never cleared.What was done?
rs-platform-walletPlatformWalletError::TokenOperationFailed { operation, source: dash_sdk::Error }. It keeps the SDK error instead of formatting it away, and itsDisplayis byte-identical to whatTokenErrorrendered for the same failure.PlatformWalletError::token_operation_failed(op, e)builds it for all 12 token state transitions (claim, mint, burn, transfer, freeze, unfreeze, destroy frozen funds, pause, resume, set price, purchase, config update). The structured signer key-unavailable failure still goes toSdk, so FFI code 31 is unaffected.PlatformWalletError::consensus_error()returns theConsensusErrorforSdkandTokenOperationFailed, covering the wait-stream shape (StateTransitionBroadcastError.cause), the CheckTx shape (Protocol(ConsensusError)) and theNoAvailableAddressesToRetryenvelope.rs-platform-wallet-ffiPlatformWalletFFIResultgainsconsensus_code: u32(0 when the failure was not a consensus rejection) andconsensus_kind: PlatformWalletFFIConsensusErrorKind(None,Basic,Signature,Fee,State). Rust hands the kind over so neither host derives it from the digits of the code.From<PlatformWalletError>stamps both fromconsensus_error(). The result code and message do not move: a rejected token operation is stillErrorUnknownwith the same text, so no number is claimed inERROR_CODE_REGISTRY.mdand no dedicated code is overridden.rs-unified-sdk-jniand the Kotlin SDKthrow_pwffi_resultthrowsDashSDKException(code, message, consensusCode, consensusKind)when the result carries a rejection, and the existing(Int, String)constructor otherwise.take_pwffi_errorandwallet_manager::throw_pwffishare it.PlatformConsensusError(code, kind)andConsensusErrorKind;DashSDKException.consensusError;DashSdkError.consensusErrorreads it off the native cause, so every subtype has it and no existing type or mapping changes.Swift SDK
PlatformConsensusError(code,kind),PlatformWalletError.consensusRejection(PlatformConsensusError, String)andPlatformWalletError.consensusError. The new case is produced only for the catch-all result code, so every dedicated case keeps arriving as it did.Example apps
OncePerIdentityClaimStore.isAlreadyClaimed(Kotlin) andOncePerIdentityClaimRejection.isAlreadyClaimed(Swift) compare the consensus code with 40722. The regex, the digit-boundary scanner and the message signals are deleted. No text fallback is kept: every path that can produce 40722 goes throughtoken_claim_with_external_signer, which now carries the code.How Has This Been Tested?
Run locally (macOS, arm64):
cargo test -p platform-wallet --lib error::: 22 passed (5 new).cargo test -p platform-wallet-ffi --lib error::: 50 passed (5 new).cargo clippy -p platform-wallet -p platform-wallet-ffi -p rs-unified-sdk-jni --all-targets: clean.cargo check -p platform-wallet-storage -p rs-unified-sdk-ffi: passes.cargo fmt --checkon the three edited crates: clean../gradlew :sdk:testDebugUnitTest --tests '*DashSdkErrorTest': 21 tests, 0 failures (3 new)../gradlew :app:testDebugUnitTest --tests '*TokenMaterializerOncePerIdentityTest': 11 tests, 0 failures.Not run at the time of opening:
swift testand theSwiftExampleAppTests/TokenClaimResolverTestsxcodebuild run are pending on it; I will update this PR with the results or with fixes.DashSDKException) is type-checked only; it needs a rebuilt.soand an emulator to exercise. The Android.sowas not rebuilt.Breaking Changes
None for hosts built from this tree.
Two things change shape and are worth a reviewer's eye:
PlatformWalletFFIResultgrows by two fields. Both consumers are generated from this source (Swift through the cbindgen header in the xcframework, Kotlin through an rlib dependency of the JNI crate), so they move together; a host linking a prebuilt library against an older header would not.PlatformWalletErrorgains a case, and a rejected token operation now arrives as.consensusRejectionwhere it used to arrive as.unknown. ItserrorDescriptionis the same message.Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code