Skip to content

EthTx (vote-via-tx) commits orphaned when UI registerTransaction POST fails #2186

Description

@tony8713

Summary

EthTx (vote-via-transaction) commits can be permanently orphaned when the UI's single registerTransaction POST to mana fails. The L1 StarknetCommit.commit() and the L1→L2 message consumption can both succeed on-chain, yet the L2 vote is only relayed if that one fire-and-forget POST reaches mana. There is no retry and no server-side fallback, so a lost POST means the user has paid L1 gas, the commit is registered on-chain, but the vote never lands and there is no recovery path.

Flow

  1. UI builds the Vote envelope and calls EthereumTx.initializeVoteStarknetCommit.commit(authenticator, hash) on L1. The commit hash is poseidonHashMany of the vote calldata (packages/sx.js/src/clients/starknet/ethereum-tx/index.ts getVoteHash).
  2. UI then fires a single registerTransaction POST to mana with { type, sender, hash, payload: envelope.data }:
    • apps/ui/src/composables/useActions.ts:81-87 (handleCommitEnvelope)
    • apps/ui/src/helpers/mana.ts registerTransaction / rpcCall (plain fetch, no retry, result not awaited for correctness)
    • apps/mana/src/stark/rpc.ts registerTransactionapps/mana/src/db.ts inserts a registered_transactions row.
  3. The L1→L2 message is consumed on L2; the EthTx authenticator records _commits[hash][sender] = 1.
  4. apps/mana/src/stark/registered.ts registeredTransactionsLoop (every 15s) reads unprocessed registered_transactions rows, checks _commits[hash][sender] on-chain, and calls client.vote(...) to authenticate the vote on L2.

The critical fragility: step 4 depends entirely on the row written in step 2. The on-chain _commits entry from step 3 is never independently watched by mana. If the step-2 POST is lost (CORS, transient network error, tab closed before/while the request is in flight, browser navigation), no row is ever created, the loop never sees it, and the consumed commit sits on-chain forever with no vote.

Real occurrence (repro)

  • Space: sn:0x05702362b68a350c1cae8f2a529d74fdbb502369ddcebfadac7e91da37636947 (TEST space), proposal 65 (active)
  • Voter: 0xd410007411572127c77b4c3ff88696865a589a2b
  • EthTx authenticator: 0x63c89d1c6b938b68e88db2719cf2546a121c23642974c268515238b442b0ea0
  • L1 commit tx: 0x28454598…f13de75
  • Commit hash on-chain: 0x6591768ebc4c486cb78f00e48a7ad64cd0bca5ad6eea6f4018410cf5eb0c41c
  • On L2 the EthTx authenticator records _commits[0x6591768…c41c][0xd410…89a2b] = 0x1 (message consumed).
  • mana has no registered_transactions row for this hash (confirmed via the getDataByMessageHash RPC returning empty), and the proposal shows zero votes. The user paid L1 gas; the vote never landed and there is no way for them to recover it without re-committing.

Why recovery is hard today

The relay payload (envelope.data) is not derivable purely from on-chain state. The commit hash binds the voter's choice and the metadataUri (the optional vote "reason", pinned to IPFS). When the row is lost, that exact data — needed to reproduce the commit hash and pass the relayer's _commits check — is gone unless the voter re-supplies it. This makes the orphaned-commit case effectively unrecoverable after the fact.

Proposed fixes

  • (a) Server-side self-registration (preferred): mana independently watches the StarknetCommit CommitAdded event (or the authenticator _commits storage) and reconciles/relays known commits without depending on the UI POST. This removes the single point of failure entirely. (Note: this still needs envelope.data to build the authenticate_vote call, so it should be paired with persisting the payload — e.g. UI uploads envelope.data to a durable store keyed by commit hash before/at commit time, or mana derives what it can and surfaces unmatched on-chain commits for recovery.)
  • (b) UI persistence + retry: the UI persists the pending registerTransaction payload (localStorage/IndexedDB) immediately after the L1 commit is broadcast, and retries the POST with backoff until mana acks, surviving reloads and transient failures.

Reference: PR #2183 (vote-via-transaction).

/cc relay reliability — tagging as a bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions