Skip to content

fix(swift-sdk): stored default on keyType so pre-column stores can migrate#4129

Merged
QuantumExplorer merged 1 commit into
v4.1-devfrom
claude/fix-keytype-migration-default
Jul 14, 2026
Merged

fix(swift-sdk): stored default on keyType so pre-column stores can migrate#4129
QuantumExplorer merged 1 commit into
v4.1-devfrom
claude/fix-keytype-migration-default

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jul 14, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

#4127 added PersistentCoreAddress.keyType: UInt8 with only an init-parameter default, which SwiftData lightweight migration never consults. Opening any pre-#4127 store fails migration ("Validation error missing attribute values on mandatory destination attribute: PersistentCoreAddress.keyType"), DashModelContainer.create() throws, and SwiftExampleAppApp hits fatalError — the app crash-loops on every device that has existing data.

What was done?

Stored default = 0 (ECDSA) on the property, matching the established convention for post-hoc columns (isImported, statusRaw, blockPosition). Defaulted rows read as ECDSA until the next Rust address-pool persist pulse re-tags typed entries, which happens on every load — so provider BLS/EdDSA rows self-heal immediately.

How Has This Been Tested?

Reproduced the crash on the booted simulator: pre-#4127 store + v4.1-dev tip → container load error + crash to home screen. With this fix the same store migrates, the container loads, and the app launches.

Breaking Changes

None.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility when opening existing local data stores after an update.
    • Prevented launch crashes by providing a default address key type for legacy records.
    • Existing records are interpreted consistently until their key type is refreshed.

…grate

PR #4127 declared PersistentCoreAddress.keyType without a stored
default — the init-parameter default is invisible to SwiftData
lightweight migration, so opening any store with existing rows fails
("Validation error missing attribute values on mandatory destination
attribute"), DashModelContainer.create() throws, and the app hits
fatalError on launch: every device with existing data crash-loops
until reinstall. Reproduced on the simulator against a pre-#4127
store; with the stored default the same store migrates and launches.

Defaulted rows read as ECDSA (0) until the next Rust address-pool
persist pulse re-tags typed entries, which happens on every load —
same convention as isImported / statusRaw / blockPosition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ffc8403-dfc2-4c07-89ab-66718b9559d2

📥 Commits

Reviewing files that changed from the base of the PR and between 88949b7 and 6177500.

📒 Files selected for processing (1)
  • packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentCoreAddress.swift

📝 Walkthrough

Walkthrough

PersistentCoreAddress.keyType now has a stored default of 0, and its documentation explains how SwiftData uses that default when loading existing stores during lightweight migration.

Changes

SwiftData migration

Layer / File(s) Summary
Persisted key type default
packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentCoreAddress.swift
PersistentCoreAddress.keyType now defaults to 0, with documentation describing legacy-row loading during SwiftData migration.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Possibly related PRs

  • dashpay/platform#4127: Adds the SwiftData keyType default that supports persistence of typed provider keys in core-address rows.

Suggested reviewers: lklimek, llbartekll

🚥 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 accurately summarizes the key change: adding a stored default to keyType so existing SwiftData stores can migrate.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 claude/fix-keytype-migration-default

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 14, 2026

Copy link
Copy Markdown
Collaborator

⛔ Blockers found — Sonnet deferred (commit 6177500)
Canonical validated blockers: 1

@QuantumExplorer QuantumExplorer merged commit 1b1aa48 into v4.1-dev Jul 14, 2026
9 checks passed
@QuantumExplorer QuantumExplorer deleted the claude/fix-keytype-migration-default branch July 14, 2026 17:06
@github-actions github-actions Bot added this to the v4.1.0 milestone Jul 14, 2026

@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

Exact HEAD 6177500 and the supplied full range were verified. The stored default makes legacy stores openable, but it does not perform the claimed provider-key migration, leaving legacy Ed25519 platform-node ownership data unavailable after restoration.

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 (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet: not run (deferred by blocker gate)

🔴 1 blocking

🤖 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/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentCoreAddress.swift`:
- [BLOCKING] packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentCoreAddress.swift:34: Legacy provider keys are not re-tagged on load
  Pre-#4127 address rows serialized only ECDSA keys, so BLS and Ed25519 rows have an empty `publicKey`; the Ed25519 batch was stored separately in the since-removed `PersistentAccount.derivedPlatformNodeKeys`. After this default allows the store to open, `buildCoreAddressPoolBuffer` forwards those empty keys with length zero, Rust decodes them as `public_key: None`, and `restore_address_pool` overwrites the managed-pool entry. `FFIPersister::load` only reads the snapshot and never invokes the persistence callback, so the claimed load-time re-tagging does not occur. BLS ownership matching can still derive keys from its xpub, but its typed persisted entries remain empty; hardened Ed25519 children cannot be derived from the public xpub, leaving platform-node ownership matching and persisted key display unavailable. Add an explicit legacy conversion that preserves pre-derived typed entries and migrates the old Ed25519 batch into typed core-address rows, with an on-disk pre-#4127 migration test.

@QuantumExplorer

Copy link
Copy Markdown
Member Author

Scope note: this branch now carries a second commit pinning rust-dashcore to the head of rust-dashcore#893 — the fix for the OTHER startup crash on v4.1-dev (rust-dashcore#892, a dash-spv debug SIGABRT crash-loop when a wallet's scan floor sits below the stored filter range). Verified live on the previously crash-looping simulator: the new discard-and-redownload path fired and the app runs cleanly. The pin will be re-pointed to the #893 merge commit once it lands; together with the keyType migration default, this makes v4.1-dev launchable again on devices with existing data.

@QuantumExplorer

Copy link
Copy Markdown
Member Author

rust-dashcore#893 merged — pin re-pointed to the merge commit (19690d31) in 3ad8740. This PR is now merge-ready: it carries the two fixes that make v4.1-dev launchable on devices with existing data (keyType migration default + the sparse-filter crash-loop fix), both verified live on-device.

QuantumExplorer added a commit that referenced this pull request Jul 15, 2026
…rows + land the #893 crash-fix pin

Follow-up to #4129 (merged before its review completed) addressing
thepastaclaw's blocking finding, plus the rust-dashcore bump that was
pushed to the #4129 branch after its merge and never landed:

- restore_address_pool: a legacy row persisted before the typed-key
  column (empty key -> public_key: None) no longer strips the typed BLS
  key the gap-limit prederivation put at the same index — pre-typed-key
  stores otherwise lose in-memory operator pubkeys at load and
  masternode operator-ownership matching silently breaks. No-op for
  post-migration rows (they always carry their key). Test pins both
  directions. Legacy Ed25519 platform-node keys are hardened-only and
  their old account-level batch was dropped by the schema migration, so
  they cannot be recovered at load; per the pre-release convention those
  stores re-derive on delete+re-import.

- PersistentCoreAddress.keyType doc: corrected the false "re-tags on
  every load" claim — load only reads the snapshot; re-tagging happens
  on the next persist pulse.

- Bump rust-dashcore to 19690d31 (merged #893, fixes #892): the
  sparse-filter startup crash-loop fix, verified live on the previously
  crash-looping simulator state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@QuantumExplorer

Copy link
Copy Markdown
Member Author

Follow-up for the post-merge blocking finding: #4132. Confirmed the finding — the plain upsert did strip prederived typed BLS keys for legacy key-less rows, and the "re-tags on every load" claim in this PR's comment was wrong (load only reads the snapshot). #4132 restores typed-key preservation for legacy rows (no-op for post-#4127 rows), corrects the doc, and also lands the rust-dashcore#893 crash-fix pin that was pushed to this branch after the merge. Legacy Ed25519 platform-node rows cannot be recovered at load (hardened-only; the removed batch column's data was already dropped by the schema migration) — per the pre-release convention they re-derive on delete+re-import.

🤖 Addressed by Claude Code

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