Skip to content

feat(kotlin-sdk)!: provision DashPay registration keys#4173

Merged
shumkov merged 10 commits into
v4.1-devfrom
feat/kotlin-sdk-dashpay-registration-keys
Jul 21, 2026
Merged

feat(kotlin-sdk)!: provision DashPay registration keys#4173
shumkov merged 10 commits into
v4.1-devfrom
feat/kotlin-sdk-dashpay-registration-keys

Conversation

@shumkov

@shumkov shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Fresh identities created by the Kotlin example app did not receive the DashPay
encryption/decryption key pair, so they could not use the Add Contact flow
immediately after registration. The former registration wire format also could
not carry the contract/document bounds required by those keys.

What was done?

  • Replace the positional registration-key wire format with strict rich public-key
    rows shared by registration and add-key updates.
  • Keep registration-only validation scoped to registration while rejecting
    duplicate key IDs in all decoded public-key lists, including invitation claims.
  • Define the canonical Kotlin registration-key policy: the existing four base
    keys plus DashPay ENCRYPTION/DECRYPTION keys 4 and 5 at MEDIUM security,
    bounded to the DashPay contactRequest document type.
  • Provision all six keys in one derivation pass for fresh Core-funded,
    Platform-address-funded, and shielded registrations; preserve the four-key
    set for asset-lock resume.
  • Carry the derived identity slot beside the rich rows and reject a resume key
    set whose slot does not match the tracked asset lock before JNI.
  • Freeze funding source, amount, identity index, and recovery lock before the
    first suspension so interactive form changes cannot redirect an in-flight
    registration.
  • Reserve the full six-key identity-create-from-addresses fee on the native
    BTreeMap input selected by DeductFromInput(0), before deriving or persisting
    any keys.
  • Persist private scalars through the shared persist-and-scrub primitive and
    clean up secret material on success, failure, malformed decode, and coroutine
    cancellation.
  • Enforce the six-key Core funding floor before broadcasting an asset lock.
  • Add a checked-in cross-language golden wire fixture plus Rust and Kotlin edge,
    policy, lifecycle, funding, and invitation-path regressions.

Existing identities are not backfilled by this change; they continue to use the
Add Identity Key flow. Asset-lock resume intentionally remains excluded from
DashPay-key provisioning to match the iOS behavior.

How Has This Been Tested?

The following local verification passed:

  • cargo test -p rs-unified-sdk-jni --lib (29 passed)
  • cargo test -p platform-wallet-ffi --lib (201 passed)
  • cargo clippy --workspace --all-features (passed with the locally configured
    compiler cache wrapper disabled)
  • cargo fmt --check --all
  • JAVA_HOME=/opt/homebrew/opt/openjdk@17 ANDROID_HOME=/opt/homebrew/share/android-commandlinetools ./gradlew :sdk:assembleDebug :sdk:testDebugUnitTest :app:assembleDebug :app:testDebugUnitTest :sdk:compileDebugAndroidTestKotlin

The PR-review regressions were observed failing before their fixes and passing
afterward: a 70M-credit address attempting a 30M-credit six-key registration,
a resume key set derived from a different HD slot, and mutation of form/lock
state after the immutable submission snapshot was captured.

Testnet/device registration, fetching the resulting identity to assert the
on-chain key bounds, and the Add Contact smoke test were not performed.
Those remain environment-bound manual verification; Add Contact success alone
would not prove that the expected bounds were persisted.

Breaking Changes

Public Kotlin fresh-registration entry points now consume rich IdentityPubkey
lists instead of IdentityKeyPreview lists, asset-lock resume consumes a
provenance-carrying RegistrationKeySet, and the opaque Kotlin/native
registration payload changed shape. Kotlin and native artifacts containing
this change must be released together; legacy or malformed payloads fail
closed.

Checklist:

  • I have performed a self-review of my own code
  • I have added comments to my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation
  • I have added ! to the type of the title to indicate a breaking change

For repo code owners:

  • Assign this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Added DashPay registration-key provisioning supporting full six-key creation and four-key resume.
    • Added rich public-key encoding for identity registration and updates, including per-key settings and optional DashPay contract bounds.
  • Bug Fixes

    • Strengthened validation for malformed payloads and duplicate key entries.
    • Improved private-key scrubbing on errors and cancellation to reduce sensitive material lingering.
    • Fixed identity creation submission/funding handling by snapshotting inputs and enforcing protocol fee/minimum rules.
  • Documentation

    • Added/updated DashPay registration-key specification and independent review notes.

shumkov and others added 7 commits July 21, 2026 06:26
…keys

Reviewed design (two rounds: feasibility/security, then an independent
codex call-graph cross-check) for provisioning the DashPay
ENCRYPTION/DECRYPTION key pair at identity-registration time so a freshly
created Android identity can use its own Add Contact flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The four Android identity-registration JNI paths (Core-funded, resume,
Platform-address, shielded-from-pool) previously sent a bare
(keyId, pubkey) blob and reconstructed each key's DPP role positionally in
Rust via role_for_registration_key_id. That layout can't carry the
contract bounds a DashPay ENCRYPTION/DECRYPTION key needs, so a freshly
registered identity had no encryption key and its own Add Contact flow
rejected it (select_own_encryption_key).

Switch registration to the same rich key-row wire format the identity
add-key update path already uses:

- Extract a pure &[u8] parser (parse_pubkey_rows) into a new pubkey_rows
  module, shared by the update and registration seams and unit-testable
  without a JVM. It is strict: truncation, trailing bytes, invalid
  role/bounds/boolean discriminants, interior-NUL doc types, and negative
  key IDs are all rejected (wire-skew detection).
- decode_registration_pubkeys_blob layers the registration-only invariants
  (>=1 key, no duplicate key IDs, key ID 0 = MASTER+AUTHENTICATION found by
  id not row position) on top; the shared update decoder gets neither, so
  ordinary add-key updates without key 0 still parse.
- DecodedPubkeyRow::to_ffi keeps FFI pointer creation + the borrow contract
  in one place; every registration call site keeps the owning Vec alive
  across the FFI call.
- Add explicit duplicate-key-id rejection to the shared FFI
  decode_identity_pubkeys (its BTreeMap previously last-wins silently),
  which also covers its fifth caller, the invitation-claim path.
- Delete role_for_registration_key_id, decode_pubkeys_blob, and the dead
  role constants; refresh the now-stale positional-role rustdoc.

A checked-in golden fixture (shared byte-for-byte with the Kotlin encoder
test) pins the wire format cross-language; the Rust test asserts the
DashPay-bound rows carry the real dashpay_contract::ID_BYTES.

rs-unified-sdk-jni --lib: 28 passed (was 10).
platform-wallet-ffi --lib: 199 passed (was 196).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(SDK)

Move the per-keyId DPP role assignment that used to live in Rust
(role_for_registration_key_id) to the Kotlin side, now that the role rides
the registration wire:

- Hoist the rich encoder (encodeAddPubkeys + contractBoundsKind) out of
  IdentityUpdates into a shared IdentityPubkeyCodec, used by both the
  add-key update path and every registration path.
- Add RegistrationKeys: the canonical role table (base 4 auth/transfer keys
  + the DashPay ENCRYPTION/DECRYPTION pair at MEDIUM with contactRequest
  contract-document bounds) and the DashPay contract-id mirror (pinned to
  the Rust constant by the cross-language golden fixture; handed out as a
  fresh copy so the process-global id can't be mutated).
- The four registration entry points (registerWithWalletFunding,
  registerFromAddresses, resumeWithExistingAssetLock,
  shieldedIdentityCreateFromPool) now take List<IdentityPubkey> rich rows
  and encode via IdentityPubkeyCodec; delete the bare
  IdentityKeyPreview.encodeForRegistration.
- Resume keeps carrying the base 4-key set it originally committed to
  on-chain (the DashPay pair is fresh-funding only); its per-key
  identity-index guard is dropped (inexpressible on the rich row, and the
  outpoint/index guard remains).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on (app)

Wire the DashPay ENCRYPTION/DECRYPTION pair into the create-identity flow
so a freshly registered identity can send contact requests immediately,
matching iOS.

- New DashpayKeyProvisioning: derive the full key set in ONE pass, persist
  each private scalar to the Keystore, scrub the JVM copy (on both success
  and every failure path, including the count-mismatch rejection), and
  build the rich IdentityPubkey rows via RegistrationKeys. A dedicated
  helper rather than reusing IdentityKeyAdditionFlow, which assigns keyIds
  as max+1 (never key 0), derives per-slot, and assumes post-registration
  persistence.
- CreateIdentityScreen derives keyCount(includeDashPay) keys (6 for fresh
  funding, 4 for resume) in the single preview pass and dispatches the rich
  rows. Fresh Core / Platform-address / shielded paths provision DashPay;
  asset-lock resume is excluded (CreateIdentityFundingSource.
  includesDashPayKeys), matching iOS's own resume exclusion.
- AddIdentityKeyScreen's DashPay bounds entry now reads the SDK's single
  RegistrationKeys constant instead of a private copy.

app :testDebugUnitTest green (DashpayKeyProvisioningTest: 6-vs-4 selection,
scrub-on-success, scrub-on-partial-failure, scrub-on-count-mismatch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The registration-key provisioning and the add-key flow each had their own
store-then-zero loop for identity private keys — two places to get the
secret-scrubbing discipline right (and where the one review bug lived).

Extract that single-scalar step into IdentityKeyPersistence.storeAndScrub
(store under the pubkey hex, then zero the JVM copy on both the success and
the failure path) and route both DashpayKeyProvisioning and
IdentityKeyAdditionFlow through it. The two flows keep their distinct
keyId/derivation logic (explicit 0..N + single batch pass vs. max+1 +
per-slot) — only the dangerous persist+scrub primitive is unified.

Behavior-preserving: app :testDebugUnitTest green (DashpayKeyProvisioning 5,
IdentityKeys 6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a shared rich public-key wire format across Kotlin and Rust, registration-only validation, canonical four- and six-key DashPay provisioning, fee-aware funding logic, private-key cleanup safeguards, and extensive tests and review documentation.

Changes

DashPay registration key flow

Layer / File(s) Summary
Rich-row parsing and JNI integration
packages/rs-unified-sdk-jni/..., packages/rs-platform-wallet-ffi/...
Rust strictly parses rich registration rows, rejects malformed input and duplicate IDs, enforces registration key-ID-0 rules, and updates registration, update, funding, and invitation callers.
Kotlin registration contracts and encoding
packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/..., .../wallet/...
Defines canonical four- and six-key rows, DashPay bounds, RegistrationKeySet, and shared rich-row encoding across registration, resume, update, and shielded creation paths.
Provisioning and funding orchestration
packages/kotlin-sdk/KotlinExampleApp/...
Adds DashPay key provisioning and private-key persistence cleanup, snapshots submission state, excludes DashPay keys from resume, and makes funding validation and platform-address packing fee-aware.
Security and validation tests
packages/kotlin-sdk/.../src/test/...
Tests private-key scrubbing, cancellation cleanup, key policies, funding boundaries, resume validation, golden encoding, and registration-row behavior.
Specifications and review notes
docs/sdk/*, packages/rs-platform-wallet-ffi/src/identity_key_preview.rs
Documents implementation findings, compatibility considerations, verification results, test plans, and host-side role stamping.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: Client Only

Suggested reviewers: lklimek, quantumexplorer, llbartekll, zocolini

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant RegistrationKeys
  participant IdentityPubkeyCodec
  participant JNI
  participant Platform
  App->>RegistrationKeys: derive and build registration rows
  RegistrationKeys->>IdentityPubkeyCodec: encode rich rows
  IdentityPubkeyCodec->>JNI: send registration blob
  JNI->>JNI: parse and validate rows
  JNI->>Platform: submit IdentityPubkeyFFI rows
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: provisioning DashPay registration keys, and the breaking-change marker fits the PR scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/kotlin-sdk-dashpay-registration-keys

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

🕓 Ready for review — 48 ahead in queue (commit 2aa999c)
Queue position: 49/49

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preliminary review — Codex only

All three Codex correctness concerns are confirmed at the exact HEAD, with one clarification: Platform-address fees come from the post-spend remainder of fee-strategy input 0, not from the requested identity amount itself. Mutable submission state can diverge from the prepared keys, resume lost its HD-slot invariant, and six-key address funding is packed without reserving its increased fee. These are in-scope blocking regressions.

Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (failed), gpt-5.6-sol — general (completed), gpt-5.6-sol — security-auditor (failed), gpt-5.6-sol — security-auditor (completed), gpt-5.6-sol — ffi-engineer (failed), gpt-5.6-sol — ffi-engineer (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 3 blocking

2 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/CreateIdentityScreen.kt`:
- [BLOCKING] packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/CreateIdentityScreen.kt:215-251: Freeze the registration inputs before preparing keys
  The new source-dependent key count is captured in `registerDashPayKeys`, but after the suspending preview and persistence operations the dispatch reads the current mutable `fundingSource` and `selectedRecoveryLock`. The funding, wallet, lock, amount, and identity-index controls remain interactive while only the submit button is disabled. Switching from fresh funding to resume can therefore derive and persist six keys for the original slot, then submit them against the current four-key recovery lock and possibly a different registration index. Changing between fresh funding sources also changes the funding mechanism and amount units after validation, while switching away from resume leaves four persisted keys before the captured null amount fails. Capture one immutable submission snapshot and use it for preparation, dispatch, coordinator tracking, and navigation, or disable every relevant control until completion.
- [BLOCKING] packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/CreateIdentityScreen.kt:277-281: Reserve the six-key fee when packing Platform-address inputs
  Fresh registration now creates six keys, raising the one-input minimum fee from 28,500,000 to 41,500,000 credits (`2,000,000 + 6,500,000 × keyCount + 500,000 × inputCount`). Drive pays this fee from the remaining address balances after the requested input amounts are consumed, and the SDK's fixed strategy uses only `DeductFromInput(0)`. `packFundingInputs` currently selects amounts totaling `requiredAmount` without ensuring that the BTreeMap-first included address retains the calculated fee. For example, spending 30,000,000 from a 70,000,000-credit address leaves 40,000,000: enough for the previous four-key fee but not the new six-key fee, so this PR turns a previously valid registration into an on-chain rejection after all keys have been persisted. Before provisioning keys, pack and validate using the selected addresses' post-spend balances, the actual input count, the active key count, and the address that will occupy fee-strategy input 0; simply imposing the fee as a minimum requested identity amount would reject valid funding layouts.

In `packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityRegistration.kt`:
- [BLOCKING] packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityRegistration.kt:129-151: Resume no longer verifies that keys belong to the tracked HD slot
  Before this PR, resume accepted `IdentityKeyPreview` rows and rejected any key whose `identityIndex` differed from `lock.registrationIndex`. The replacement `IdentityPubkey` rows contain no derivation index, so that invariant was deleted. JNI and native registration accept the public keys and supplied identity index independently; signing succeeds because the Keystore signer looks up private keys by public-key bytes. Registration then records `ManagedIdentity` and each derivation breadcrumb under the supplied lock index even when the keys came from another slot. Subsequent seed-based loading derives the master key at that recorded index and cannot reproduce the registered identity. Preserve the derivation index alongside the rich rows in a registration-key-set wrapper and restore the equality check before JNI.

Base automatically changed from feat/kotlin-sdk-and-example-app to v4.1-dev July 21, 2026 10:16
@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 21, 2026
@shumkov shumkov changed the title feat(kotlin-sdk): provision DashPay encryption keys at identity registration feat(kotlin-sdk)!: provision DashPay registration keys Jul 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreview.kt (1)

43-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new destructive blob side effect.

decodeAll now always zeroes the input blob in finally (line 96), on both success and failure, but the KDoc still only describes the wire layout — it doesn't warn callers that the array they pass in gets wiped and must not be reused afterward.

📝 Suggested doc update
         /**
          * Decode the flat BLOB produced by
          * `IdentityNative.previewRegistrationKeys`. Layout (big-endian):
          * `u32 rowCount` then per row `u32 identityIndex, u16 pathLen,
          * pathUtf8, u8[33] pubkey, u8[32] privkey`.
+         *
+         * Destructively wipes [blob] to zero in a `finally` block on both
+         * success and failure, since it carries raw private-key scalars.
+         * Callers must not retain or reuse the array passed in.
          */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreview.kt`
around lines 43 - 49, Update the KDoc for IdentityKeyPreview.decodeAll to
explicitly warn that the input blob is zeroed on both successful and failed
decoding and must not be reused afterward, while preserving the existing
wire-layout documentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreview.kt`:
- Around line 43-49: Update the KDoc for IdentityKeyPreview.decodeAll to
explicitly warn that the input blob is zeroed on both successful and failed
decoding and must not be reused afterward, while preserving the existing
wire-layout documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 733df335-25ef-46e3-81cf-84d982619baa

📥 Commits

Reviewing files that changed from the base of the PR and between 033876f and e1cbe65.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • packages/kotlin-sdk/sdk/src/test/resources/golden/registration_pubkeys_v1.bin is excluded by !**/*.bin
📒 Files selected for processing (32)
  • docs/sdk/CODEX_REVIEW_NOTES.md
  • docs/sdk/CODE_REVIEW_NOTES.md
  • docs/sdk/KOTLIN_SDK_DASHPAY_REGISTRATION_KEYS_SPEC.md
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/DashpayKeyProvisioning.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/IdentityKeyAdditionFlow.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/IdentityKeyPersistence.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/AddIdentityKeyScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/CreateIdentityScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/services/DashpayKeyProvisioningTest.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/ui/identity/CreateIdentityFundingPolicyTest.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/ffi/IdentityNative.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreview.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityPubkeyCodec.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityRegistration.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityUpdates.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/RegistrationKeys.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/wallet/PlatformWalletManager.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/wallet/TeardownGate.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/IdentityAssetLockRecoveryTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreviewTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/RegistrationKeysTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/wallet/GateCoverageLintTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/wallet/TeardownGateTest.kt
  • packages/rs-platform-wallet-ffi/src/identity_key_preview.rs
  • packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs
  • packages/rs-platform-wallet-ffi/src/invitation.rs
  • packages/rs-unified-sdk-jni/Cargo.toml
  • packages/rs-unified-sdk-jni/src/funding.rs
  • packages/rs-unified-sdk-jni/src/identity.rs
  • packages/rs-unified-sdk-jni/src/lib.rs
  • packages/rs-unified-sdk-jni/src/pubkey_rows.rs
  • packages/rs-unified-sdk-jni/src/transactions.rs

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Review follow-up at 2aa999c:

  • The three validated PastaClaw blockers were addressed in e1cbe65: immutable submission snapshots prevent source/index drift across suspension; Platform-address packing reserves the six-key fee on the actual BTreeMap-first fee input; and RegistrationKeySet restores the resume HD-slot provenance guard. Regression tests were added for each issue, including red-before-green runs for the funding boundary and wrong-slot resume cases.
  • The CodeRabbit KDoc finding was valid and is addressed in 2aa999c. IdentityKeyPreview.decodeAll now documents that its private-scalar input blob is always wiped and must not be reused.

Local validation after the behavioral fixes passed the full SDK/app Gradle gate. The documentation follow-up passed focused SDK Kotlin compilation and git diff --check. CI is running again at the new HEAD. Testnet/on-chain and physical-device Add Contact verification remain explicitly not performed.

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Your plan includes PR reviews subject to rate limits. More reviews will be available in 6 minutes.

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Your plan includes PR reviews subject to rate limits. Reviews are available now.

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityPubkeyCodec.kt (1)

59-69: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Validate contractId length before writing the fixed-width wire field.

The wire format documents contractBoundsId as a fixed u8[32] field with no length prefix, and the Rust decoder reads exactly 32 bytes unconditionally. If bounds.contractId is ever not exactly 32 bytes, encode() silently misaligns every byte after it in the blob rather than failing fast — unlike pubkeyBytes/documentTypeName, which are explicitly size-checked here. Please confirm whether ContractBounds already enforces this invariant at construction; if not, add a local check.

🛡️ Proposed defensive check
             when (val bounds = k.contractBounds) {
                 null -> Unit
-                is ContractBounds.SingleContract -> dos.write(bounds.contractId)
+                is ContractBounds.SingleContract -> {
+                    require(bounds.contractId.size == 32) {
+                        "contractId must be 32 bytes, got ${bounds.contractId.size}"
+                    }
+                    dos.write(bounds.contractId)
+                }
                 is ContractBounds.SingleContractDocumentType -> {
+                    require(bounds.contractId.size == 32) {
+                        "contractId must be 32 bytes, got ${bounds.contractId.size}"
+                    }
                     dos.write(bounds.contractId)
                     val dt = bounds.documentTypeName.toByteArray(Charsets.UTF_8)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityPubkeyCodec.kt`
around lines 59 - 69, Validate that bounds.contractId is exactly 32 bytes before
writing it in both ContractBounds.SingleContract and
ContractBounds.SingleContractDocumentType branches of the IdentityPubkeyCodec
encoding flow. If ContractBounds does not already enforce this invariant at
construction, add a local require that fails before dos.write; preserve the
existing documentTypeName validation and encoding behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityPubkeyCodec.kt`:
- Around line 59-69: Validate that bounds.contractId is exactly 32 bytes before
writing it in both ContractBounds.SingleContract and
ContractBounds.SingleContractDocumentType branches of the IdentityPubkeyCodec
encoding flow. If ContractBounds does not already enforce this invariant at
construction, add a local require that fails before dos.write; preserve the
existing documentTypeName validation and encoding behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 07294730-5426-4d41-8d3c-941308b900a2

📥 Commits

Reviewing files that changed from the base of the PR and between 033876f and 2aa999c.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • packages/kotlin-sdk/sdk/src/test/resources/golden/registration_pubkeys_v1.bin is excluded by !**/*.bin
📒 Files selected for processing (32)
  • docs/sdk/CODEX_REVIEW_NOTES.md
  • docs/sdk/CODE_REVIEW_NOTES.md
  • docs/sdk/KOTLIN_SDK_DASHPAY_REGISTRATION_KEYS_SPEC.md
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/DashpayKeyProvisioning.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/IdentityKeyAdditionFlow.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/services/IdentityKeyPersistence.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/AddIdentityKeyScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/identity/CreateIdentityScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/services/DashpayKeyProvisioningTest.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/ui/identity/CreateIdentityFundingPolicyTest.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/ffi/IdentityNative.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreview.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityPubkeyCodec.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityRegistration.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/IdentityUpdates.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/identity/RegistrationKeys.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/wallet/PlatformWalletManager.kt
  • packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/wallet/TeardownGate.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/IdentityAssetLockRecoveryTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/IdentityKeyPreviewTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/identity/RegistrationKeysTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/wallet/GateCoverageLintTest.kt
  • packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/wallet/TeardownGateTest.kt
  • packages/rs-platform-wallet-ffi/src/identity_key_preview.rs
  • packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs
  • packages/rs-platform-wallet-ffi/src/invitation.rs
  • packages/rs-unified-sdk-jni/Cargo.toml
  • packages/rs-unified-sdk-jni/src/funding.rs
  • packages/rs-unified-sdk-jni/src/identity.rs
  • packages/rs-unified-sdk-jni/src/lib.rs
  • packages/rs-unified-sdk-jni/src/pubkey_rows.rs
  • packages/rs-unified-sdk-jni/src/transactions.rs

@shumkov

shumkov commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

CodeRabbit full-review follow-up: the contractId-width nitpick is already enforced and does not require a codec change. Both ContractBounds.SingleContract and ContractBounds.SingleContractDocumentType have init-time require checks for contractId.size == 32 in IdentityUpdates.kt. A ByteArray can have mutable contents but its length cannot change after construction, so IdentityPubkeyCodec cannot receive either bounds subtype with a non-32-byte field. Keeping the invariant at the shared model boundary also covers every encoder/persistence caller.

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.63%. Comparing base (6efa83b) to head (0ab30e7).
⚠️ Report is 15 commits behind head on v4.1-dev.

Additional details and impacted files
@@             Coverage Diff              @@
##           v4.1-dev    #4173      +/-   ##
============================================
+ Coverage     87.43%   87.63%   +0.19%     
============================================
  Files          2646     2624      -22     
  Lines        333982   331678    -2304     
============================================
- Hits         292024   290659    -1365     
+ Misses        41958    41019     -939     
Components Coverage Δ
dpp 88.44% <ø> (ø)
drive 86.13% <ø> (-0.02%) ⬇️
drive-abci 89.57% <ø> (+0.06%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.90% <ø> (+0.01%) ⬆️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 49.66% <ø> (+0.11%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…tch notes

Working docs from the design/review phase; not intended for the repo. Net
zero in the PR diff (added earlier in this PR, removed here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shumkov
shumkov requested a review from thepastaclaw July 21, 2026 14:33
@shumkov
shumkov merged commit ee78569 into v4.1-dev Jul 21, 2026
8 checks passed
@shumkov
shumkov deleted the feat/kotlin-sdk-dashpay-registration-keys branch July 21, 2026 14:33
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.

2 participants