-
Notifications
You must be signed in to change notification settings - Fork 54
fix(platform-wallet): local-ledger ownership guard (V27-007) #3648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3.1-dev
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,17 @@ impl PlatformAddressWallet { | |
| /// material — callers supply a seed-backed, hardware, or | ||
| /// FFI-trampoline signer per their environment (iOS routes through | ||
| /// `KeychainSigner` via `VTableSigner`). | ||
| /// | ||
| /// # Local-ledger ownership invariant (V27-007 / QA-010) | ||
| /// | ||
| /// The SDK returns post-transition states for **all** addresses touched by | ||
| /// the transition, including foreign output addresses the caller does not | ||
| /// own. Only addresses that belong to this wallet's account are written into | ||
| /// the local ledger; foreign addresses are silently skipped. The recipient | ||
| /// wallet's syncer is responsible for observing inbound credits on its own | ||
| /// addresses. Violating this invariant causes the source wallet's | ||
| /// `total_credits()` to absorb the recipient's balance, which corrupts | ||
| /// dust-gate checks and sweep address selection in teardown. | ||
| pub async fn transfer<S: Signer<PlatformAddress> + Send + Sync>( | ||
| &self, | ||
| account_index: u32, | ||
|
|
@@ -105,6 +116,15 @@ impl PlatformAddressWallet { | |
| continue; | ||
| }; | ||
| let p2pkh = PlatformP2PKHAddress::new(*hash); | ||
| // V27-007 / QA-010: skip foreign output addresses. | ||
| // The SDK returns post-transition state for every address in | ||
| // the transition (inputs + outputs). Output addresses may | ||
| // belong to a different wallet; writing their balances here | ||
| // would pollute this wallet's local ledger and corrupt | ||
| // `total_credits()`. See the method-level doc comment. | ||
| if !account.contains_platform_address(&p2pkh) { | ||
| continue; | ||
| } | ||
|
Comment on lines
116
to
+127
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: Silent skip of foreign addresses has no observability Foreign addresses are dropped with a bare source: ['claude']
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified against current head a9eae10. Finding is accurate: foreign addresses are dropped with a bare continue (transfer.rs:126, withdrawal.rs:136) and no tracing::debug!/trace! line; the only tracing in transfer.rs is the unrelated error! at :177. Not resolving: per architect adjudication this PR lands as-is as a minimal surgical correctness fix. Adding low-level observability on the skip is a reasonable enhancement but is deferred follow-up under the separate #3549 platform_addresses module-debt workstream, not in scope here. No code change in this PR. |
||
| let funds = match maybe_info { | ||
| Some(ai) => dash_sdk::platform::address_sync::AddressFunds { | ||
| balance: ai.balance, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Suggestion: No crate-local regression test pins the foreign-address skip
The entire correctness fix for QA-010 is enforced solely by control flow in
transfer()andwithdrawal(), yetrs-platform-walletadds no unit or integration test exercising a response that mixes owned input addresses with a foreign output address. The PR description defers validation to the parent #3549 e2e suite, but this protection should live with the code it guards: a future refactor of the post-broadcast ledger-update loop (moving the guard, factoring the body out, replacingcontains_platform_address) could silently reintroduce the exact ledger-corruption bug while compiling cleanly and passing unrelated coverage. Add a focused test that feeds anaddress_infosmap containing one owned and one foreignPlatformAddress::P2pkh, then asserts (a)account.address_credit_balanceonly reflects the owned entry, (b) the returnedPlatformAddressChangeSetexcludes the foreign entry, and (c)total_credits()does not absorb the foreign balance. The same fixture covers the symmetric guard inwithdrawal.rs, which has no e2e coverage today.source: ['claude', 'codex']
🤖 Fix this with AI agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verified against current head a9eae10. Finding is accurate: the QA-010/V27-007 ownership guard (transfer.rs:125-127, withdrawal.rs:135-136) is enforced solely by control flow with no crate-local regression test. Not resolving: per architect adjudication this PR lands as-is — the one-line guard is the production fix (reached via C-FFI platform_address_wallet_transfer/_withdraw). The focused mixed owned/foreign address_infos regression test you describe is valid and tracked as deferred follow-up under the separate #3549 platform_addresses module-debt workstream (memcan-tracked), not in scope for this surgical fix. No code change here.