Skip to content

fix(realunit): concurrent double-submit can un-gate a genuinely new registration #4285

Description

@TaprootFreak

Problem

Follow-up from the review of #4283. That PR un-gates RealUnit registrations when Aktionariat's registerUser reply marks an existing share-register shareholder (Existing user found, updated your address.requiresEmailConfirmation = false), because such a shareholder never receives a confirmation email. #4283 also hardened the marker match to a start-anchored prefix so an echoed email can no longer spoof it.

One related case remains open and is not addressed by #4283, because here the marker is Aktionariat's genuine reply, not a spoof:

Concurrent double-submit of a genuinely new registration. forwardRegistration POSTs to Aktionariat outside the DB transaction, and the PR's own comment notes two concurrent callers may both POST before either persists. For a brand-new email:

  1. Caller A's POST creates the user at Aktionariat → reply Confirmation email sent to <email>.
  2. Caller B's near-simultaneous POST now finds the user exists (A just created it) → reply Existing user found, updated your address.
  3. Each caller computes existingShareholder from its own reply before the transaction. Whichever caller wins the per-wallet advisory-lock persist writes its own flag; the other becomes idempotent.
  4. If caller B wins, the row is persisted requiresEmailConfirmation = false even though a confirmation email was sent and never confirmed → the fix(realunit): gate buy quotes on the confirmed registration email and fail loud on zero-match confirms #4215 buy gate is lifted prematurely for a non-shareholder.

Impact

  • Latent, not active. Verified read-only on prod: no wallet has both reply types across the Aktionariat/Registration logs, so this has not occurred yet.
  • Aktionariat's own payAndAllocate stays the hard backstop, and KYC level 30 is still required, so the practical exposure is limited to defeating the pre-tap DFX gate (a defense-in-depth / UX signal).

Options

  1. Bind the existingShareholder un-gating to an additional proof rather than the reply alone — e.g. a RealUnit ponder holder check on the signing wallet (the wallet actually holds REALU), or only un-gate when no Confirmation email sent was logged for the same registration event.
  2. Accept the residual case, given the reactive backstop + KYC-30 + zero prod incidence, and just document it.

Not blocking #4283; the anchored marker and the backfill migration are safe to ship as-is.

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