relayer/docs/idempotency-audit.md
All three request types (PublicDecrypt, UserDecrypt, InputProof) have complete ASCII diagrams showing:
| Method | Transition | WHERE Clause | Status |
|---|---|---|---|
update_status_to_processing | queued → processing | WHERE req_status = 'queued' | ✅ CORRECT |
update_status_to_timed_out | queued/receipt_received → timed_out | WHERE req_status IN ('queued', 'receipt_received') | ✅ CORRECT |
update_status_to_tx_in_flight | processing → tx_in_flight | WHERE req_status = 'processing' | ✅ CORRECT |
update_status_to_receipt_received_on_tx_success | tx_in_flight → receipt_received | WHERE req_status = 'tx_in_flight' | ✅ CORRECT |
update_status_to_failure_on_tx_failed | processing/tx_in_flight → failure | WHERE req_status IN ('processing', 'tx_in_flight') | ✅ CORRECT (TX failures only) |
complete_req_with_res | receipt_received → completed | WHERE req_status = 'receipt_received' | ✅ CORRECT |
| Method | Transition | WHERE Clause | Status |
|---|---|---|---|
update_status_to_processing | queued → processing | WHERE req_status = 'queued' | ✅ CORRECT |
update_status_to_timed_out | queued/receipt_received → timed_out | WHERE req_status IN ('queued', 'receipt_received') | ✅ CORRECT |
update_status_to_tx_in_flight | processing → tx_in_flight | WHERE req_status = 'processing' | ✅ CORRECT |
update_status_to_receipt_received_on_tx_success | tx_in_flight → receipt_received | WHERE req_status = 'tx_in_flight' | ✅ CORRECT |
update_status_to_failure_on_tx_failed | processing/tx_in_flight → failure | WHERE req_status IN ('processing', 'tx_in_flight') | ✅ CORRECT (TX failures only) |
insert_share_and_complete_if_threshold_reached | receipt_received → completed | WHERE req_status = 'receipt_received' | ✅ CORRECT (requires gw_reference_id) |
| Method | Transition | WHERE Clause | Status |
|---|---|---|---|
update_status_to_tx_in_flight | processing → tx_in_flight | WHERE req_status = 'processing' | ✅ CORRECT |
update_input_proof_status_to_receipt_received | tx_in_flight → receipt_received | WHERE req_status = 'tx_in_flight' | ✅ CORRECT |
update_status_to_failure | processing/tx_in_flight/receipt_received → failure | WHERE req_status IN ('processing', 'tx_in_flight', 'receipt_received') | ✅ CORRECT (generic failure) |
accept_and_complete_input_proof_req | receipt_received → completed | WHERE req_status = 'receipt_received' | ✅ CORRECT |
reject_and_complete_input_proof_req | receipt_received → failure | WHERE req_status = 'receipt_received' | ✅ CORRECT |
Diagram matches code: ✅
Diagram matches code: ✅
Diagram matches code: ✅
processing (NOT queued) - no Readiness QueueSingle-state transitions use exact equality:
WHERE req_status = 'queued' -- queued → processing
WHERE req_status = 'processing' -- processing → tx_in_flight
WHERE req_status = 'tx_in_flight' -- tx_in_flight → receipt_received
WHERE req_status = 'receipt_received' -- receipt_received → completed
Important: receipt_received ONLY from tx_in_flight because:
sendRawTransactionSync sets receipt_received after getting the TX receipttx_in_flight BEFORE sendRawTransactionSync runstx_in_flightTwo-state transitions for timeout scenarios:
WHERE req_status IN ('queued', 'receipt_received') -- → timed_out
-- Reason: Timeout from readiness queue (queued) OR response timeout (receipt_received)
Two-state transitions for TX failure scenarios:
WHERE req_status IN ('processing', 'tx_in_flight') -- → failure (TX)
-- Reason: TX failures only during TX lifecycle, not before/after
❌ Eliminated all NOT IN (terminal_states) patterns
✅ Replaced with explicit state checks
This ensures:
receipt_received transition: Changed from ('queued', 'tx_in_flight') to ('processing', 'tx_in_flight')✅ All changes compiled successfully: cargo build --bin fhevm-relayer
✅ Query cache updated: cargo sqlx prepare
All idempotency checks are correctly implemented across all three request types.
The hybrid approach (explicit state checks instead of NOT IN) has been successfully applied throughout the codebase, ensuring robust idempotency and preventing race conditions.