Skip to content

fix(wallet): derive masternode owner/voting keys through the Rust-side resolver - #937

Merged
romchornyi merged 1 commit into
developfrom
fix/masternode-keys-provider-derivation
Aug 8, 2026
Merged

fix(wallet): derive masternode owner/voting keys through the Rust-side resolver#937
romchornyi merged 1 commit into
developfrom
fix/masternode-keys-provider-derivation

Conversation

@romchornyi

@romchornyi romchornyi commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Issue being fixed or feature implemented

Two things, one change.

develop no longer compiles against v4.2-dev. dashpay/platform#4339 withdrew Account.derivePrivateKeyWIF(wallet:masterPath:index:) from the Swift SDK, and DerivationPathKeysModel was its only caller. Anyone who pulls a current platform checkout hits Value of type 'Account' has no member 'derivePrivateKeyWIF'.

The withdrawn call was returning the wrong keys. From the upstream removal:

It carried a derivePrivateKeyWIF(wallet:masterPath:index:) that asked callers for the account root path while the FFI applies the account's own path itself, so the path was applied twice and every derived key came from the wrong branch — silently, since the keys were well-formed.

So the Masternode Keys screen has been rendering owner/voting keys off a doubly-applied branch. They looked right — correct format, stable across launches — they simply were not the masternode's keys.

What was done?

  • Owner/voting derivation routes through ManagedPlatformWallet.providerKeyAtIndex(kind:index:includePrivate:), the same resolver the operator (BLS) and platform-node (Ed25519) families already use via ProviderKeyDeriver. ProviderKeyKind gained votingECDSA / ownerECDSA in feat(platform-wallet): derive owner/voting provider keys Rust-side platform#4338, which is what makes this possible. The resolver owns the DIP-3 path and cross-checks the derived key against the account xpub.
  • The path is deliberately not rebuilt app-side. Composing accountRootPath + index here would have been the smaller diff, but app-side path composition is exactly what allowed the double application; getting hardened-vs-soft wrong at the last level fails the same silent way.
  • Results are memoised per index, mirroring ProviderKeyDeriver — the screen renders a private-key row per index and the Ed25519 path pulls the seed through the mnemonic resolver on every call.
  • Dead members removed. accountRootPath and accountType became write-only once the derivation moved; the throwaway derivation wallet now backs only the fallback address pool, so its comment no longer claims it is "used ONLY for private-key derivation", and the type doc no longer implies this class builds the paths.

How Has This Been Tested?

Clean dashpay build against platform at v4.2-dev (bce107b61a, rust-dashcore pin 944e53a5) with a freshly built DashSDKFFI.xcframework — 0 errors, no warnings in the touched file.

Not yet verified on device against a known-good source. The derived values on the Masternode Keys screen will change, and the point of the change is that the new ones are correct — but confirming that means comparing a rendered owner/voting key against the same key from another wallet or a ProRegTx. Worth doing before this ships to anyone who might copy those keys out.

Breaking Changes

No API changes. User-visible behaviour does change: owner/voting keys shown on the Masternode Keys screen differ from previous builds. Anyone who copied a key from an earlier build should re-check it — the earlier value came from the wrong derivation branch.

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 made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Bug Fixes
    • Improved masternode owner and voting key derivation for managed wallets.
    • Enhanced private-key retrieval using the appropriate provider key type.
    • Added key caching to improve repeated key access.
    • Preserved fallback support for loading addresses from the derivation wallet.

`Account.derivePrivateKeyWIF(wallet:masterPath:index:)` asked callers for
the account root path while the FFI applied the account's own path on top,
so every owner/voting key came off a doubly-applied branch. The keys were
well-formed, so the screen looked correct while showing keys that were not
the masternode's. The SDK withdrew that call (platform#4339) after moving
the derivation Rust-side (platform#4338).

Route both families through `providerKeyAtIndex`, as the operator and
platform-node families already do: it owns the DIP-3 path and cross-checks
the derived key against the account xpub. Deliberately not rebuilding the
path app-side — composing it here is what allowed the double application.

`accountRootPath` and `accountType` become write-only with the derivation
gone, and the throwaway derivation wallet now backs only the fallback
address pool, so its comment no longer claims otherwise.

Values on the Masternode Keys screen change: the new ones are correct.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Masternode owner and voting key derivation now uses managed-wallet provider keys. The model maps key types to provider kinds, caches resolved keys by index, retains fallback address-pool loading, and reads WIF and hexadecimal values directly from provider-derived keys.

Changes

Provider-key masternode derivation

Layer / File(s) Summary
Derivation model initialization
DashWallet/Sources/UI/Menu/Tools/Masternode Keys/DerivationPathKeys/Models/DerivationPathKeysModel.swift
The model replaces account-path state with provider-key state and a managed wallet reference. Initialization maps owner and voting keys to provider kinds, rejects operator families, and prepares fallback address loading.
Provider-key resolution and output
DashWallet/Sources/UI/Menu/Tools/Masternode Keys/DerivationPathKeys/Models/DerivationPathKeysModel.swift
The model resolves and caches private provider keys through providerKeyAtIndex. WIF and hexadecimal private-key access use the cached key directly.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: quantumexplorer, llbartekll

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: masternode owner and voting key derivation now uses the Rust-side resolver.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/masternode-keys-provider-derivation

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

@romchornyi romchornyi changed the title fix(masternode): derive owner/voting keys through the Rust-side resolver fix(wallet): derive owner/voting keys through the Rust-side resolver Aug 8, 2026
@romchornyi romchornyi changed the title fix(wallet): derive owner/voting keys through the Rust-side resolver fix(wallet): derive masternode owner/voting keys through the Rust-side resolver Aug 8, 2026
@romchornyi
romchornyi merged commit cd3ed57 into develop Aug 8, 2026
3 of 5 checks passed
@romchornyi
romchornyi deleted the fix/masternode-keys-provider-derivation branch August 8, 2026 09:48
QuantumExplorer added a commit that referenced this pull request Aug 8, 2026
#936 and #937 landed independently and each brought its own memoised
wrapper over `ManagedPlatformWallet.providerKeyAtIndex` — byte-identical
bodies, one for the ECDSA owner/voting family and one for BLS/Ed25519.
Two caches to keep in step and two places to change.

Collapse both into a file-private `ProviderKeyResolver`, now the single
call site for all four families. `MasternodeProviderKeyDeriver` keeps its
distinct job — it carries the address pool the owner/voting rows join
against — and delegates derivation. Operator/evonode-operator have no
address rows, so their wrapper shell goes away entirely and the model
holds a resolver directly; `tenderdashNodeKeyBase64` moves onto the
resolver, still guarded on the Ed25519 kind.

Also drops three pieces of state left dead by #937, when derivation moved
Rust-side:

  * `wallet` — written in init, never read since. It held a `Wallet`
    owned by the `WalletManager` that `derivationWallet()` builds per
    call and that this class never retained, so it was a reference into
    a graph released at the end of init. Unused, so harmless, but not
    worth keeping.
  * `key` — written, never read.
  * the bound `network` — it selected the coin type back when this class
    composed the DIP-3 path app-side. Now a boolean test, with a comment
    for why the check stays.

No behaviour change. Same kinds, same `includePrivate: true`, same cache
lifetime (one per deriver instance). The host-wallet guard now runs
before the `derivationWallet()` guard rather than after, which cannot
change the outcome: `derivationWallet()` returns nil unless that same
wallet is present, so the guard only fires where the other would have.

Verified: clean `dashpay` build, no warnings left in the file.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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