Skip to content

platform-wallet: record DashPay payments per output, not per transaction #5601

Open

Description

@Claudius-Maginificent

TL;DR: The wallet stores at most one DashPay payment per transaction for each identity, so a second payment in the same transaction (from another contact, or in the other direction) is silently not recorded.

User story

As a DashPay user, I want every payment a contact sends me to appear in my history, even when it arrives in the same transaction as another payment.

As a wallet app developer, I want stable per-payment keys, so I can attach my own data (first-seen time, notes) to one payment without it colliding with another in the same transaction.

Scenario

Base flow

  1. One transaction carries two DashPay legs for the same identity. Examples: two contacts paid it in a single batched transaction, or it pays a contact and also receives from another contact.
  2. The background sync records incoming payments, and sent payments are recorded at send time or reconstructed from history.

Actual behavior

Payments are stored in a map keyed by the bare transaction id. Whichever leg is recorded first wins, and later legs for that transaction are skipped, by design, so as not to overwrite. The second payment never appears, and its amount is missing from the contact's history.

Expected behavior

Each leg is recorded under its own key: a stable per-output or per-(contact, direction) key. All legs appear, and re-running the sync stays idempotent.

Detailed discussion

On v5.1-dev @ 575d961fe0:

Likelihood: low. Batched multi-contact transactions are uncommon. Impact: a payment silently missing from history, with no error. That is why this is filed as a correctness gap rather than a feature.

Downstream: Dash Evo Tool used to record incoming payments under txid:vout. It now has to reconcile its older composite keys with the wallet's bare-txid keys and let the older entry shadow its twin (src/wallet_backend/dashpay_snapshot.rs::payments, marked TODO(upstream): per-output payment keys).

Proposed change

  • Key entries by a composite that cannot collide within a transaction, for example "{txid}:{vout}" for received outputs and "{txid}:sent:{contact}" for sent legs. Alternatively, make the map BTreeMap<PaymentKey, PaymentEntry> with PaymentKey { txid, counterparty, direction, vout: Option<u32> }.
  • Keep PaymentEntry unchanged, and add txid() / vout() accessors on the key.
  • Migrate stored rows: a bare-txid key becomes the composite key of its single leg. Keep reading bare keys for one release.
  • The idempotency guard then checks the composite key, so re-runs still never overwrite.

Prior work

Attribution

🤖 Co-authored by Claudius the Magnificent AI Agent

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions