Harden XMR signer outbound reliability and scanner state

Summary

This MR ports and completes the XMR outbound reliability hardening on top of current develop.

It focuses on making the signer sidecar safe across FROST retries, Bifrost retries, daemon race windows, restarts, and XMR scanner reconciliation. The main goal is that a signed XMR transaction only becomes durable local spend state after the sidecar can prove the transaction is daemon-visible and the outbound metadata matches the signed transaction.

What changed

FROST / DKG compatibility

  • Filter local self entries from DKG/signing maps before passing peer maps to Serai FROST internals.
  • Carry the local participant index through signing sessions so partial and combine can safely remove Bifrost-echoed self payloads.
  • Fix the single-node monovault DKG/signing path that can otherwise fail with participant-count mismatches.
  • Change DKG finalize so it stores the final key under the group key but does not auto-register a raw group-key vault. Bifrost remains responsible for registering the THOR pubkey alias through /vaults/register.

Scanner / inbound observation

  • Extract XMR arbitrary-data notes from scanned wallet outputs.
  • Persist notes in output records.
  • Preserve notes when rescans merge output state.
  • Preserve notes through collision-aware credited-output accounting.
  • Return notes in scanner transfer responses so memo-bearing XMR deposits are observed correctly.

Outbound proof and spent refs

  • Require daemon visibility before non-mocknet mark-spent mutates tracked inputs.
  • Verify imported spent refs against transaction key images.
  • Verify outbound metadata using tx_key, destination address, amount, and fee.
  • Require local wallet-output proof bytes for imported spent refs.
  • Reject unknown spent refs atomically before mutating local output state.
  • Make repeated spent-ref imports idempotent.
  • Restrict skip_verification to mocknet.

Retry and reservation safety

  • Track outgoing transfer status and timestamps.
  • Store selected spent refs on outgoing records.
  • Release reserved inputs when combine fails.
  • Release unmarked local outgoing records when broadcast fails or when the daemon does not expose the transaction after publish.
  • Require post-publish daemon visibility before returning broadcast success.
  • Preserve durable marked-spent state once import has succeeded.
  • Unreserve inputs held by expired or cleared signing sessions.

Idempotency

  • Scope idempotency by route + msg_id + payload hash.
  • Replay only exact same requests.
  • Reject same-route msg_id reuse with a different payload.
  • Avoid stale broadcast replay if the exact transaction is no longer daemon-visible.

API route fix

  • Replace Axum {param} routes with :param.
  • Restores dynamic endpoints such as:
    • /v1/monero/balance/:vault_pub_key
    • /v1/monero/outputs/:vault_pub_key
    • /v1/monero/addresses/:vault_pub_key
    • /v1/monero/chain/block/:height
    • /v1/monero/chain/tx/:tx_hash/exists

Why

The previous sidecar state model could leave local XMR outputs reserved or marked in situations where no durable daemon-visible spend existed. That could break signer retries by making the local wallet appear exhausted, or let incomplete/self-consistent metadata become a retry guard before the sidecar had verified the actual transaction.

This MR makes the durability boundary explicit:

  • FROST combine may create a signed transaction and local outgoing proof.
  • Broadcast must prove daemon visibility.
  • Mark-spent must prove transaction inputs and outbound metadata.
  • Only then are selected inputs treated as durable spent state.

Tests added

  • DKG finalize does not auto-register a raw group-key vault.
  • Router construction catches invalid Axum dynamic route syntax.
  • Signing peer payload maps filter local participant entries.
  • Idempotency hashes are stable and reject conflicting payloads.
  • Scanner scan height is vault-scoped.
  • Credited outputs preserve stored XMR notes.
  • Spent-ref marking rejects unknown refs and key-image conflicts without partial mutation.
  • Spent-ref marking is idempotent on replay.
  • Unmarked outgoing rollback unreserves inputs.
  • Marked-spent outgoing records remain durable.

Merge request reports

Loading