* Implement complete state machine with 17 test scenarios * Add priority-based processing order validation * Include property-based testing for state transitions * Add graph analysis for state machine integrity * Validate all payment lifecycle paths * Ensure robust error handling and logging * Remove temporary debug files and consolidate test structure
80 lines
No EOL
4.1 KiB
ReStructuredText
80 lines
No EOL
4.1 KiB
ReStructuredText
========================================
|
|
MONERO DOOM: The Great Restocking Crisis
|
|
========================================
|
|
|
|
Background Scenario
|
|
==================
|
|
|
|
The Complex Multi-Duplicate Payment Scenario:
|
|
|
|
1. **Setup**: $1 digital item, single user/quote/invoice
|
|
2. **Payment 1**: $1 payment → STATUS_RECEIVED → STATUS_CONFIRMED (normal flow, invoice finalized)
|
|
3. **Payment 2**: $1 payment (accidental duplicate) → STATUS_DOUBLEPAY_REFUND → refund processing → STATUS_DOUBLEPAY_REFUND_COMPLETE
|
|
4. **At 18 confirmations**: Payment 2 is close to restocking threshold (needs 20 for XMR mid-tier)
|
|
5. **Payment 3**: BOOM! Another $1 payment arrives (third payment to same address)
|
|
|
|
Expected Flow:
|
|
- Payment 3 detected as second duplicate (Payment 1 already STATUS_CONFIRMED)
|
|
- Payment 3 → STATUS_DOUBLEPAY_REFUND → refund cycle → STATUS_DOUBLEPAY_REFUND_COMPLETE
|
|
- Payment 2 hits 20 confirmations → triggers first restocking fee sweep
|
|
- Payment 3 eventually hits 20 confirmations → triggers second restocking fee sweep
|
|
|
|
The Restocking Fee Crisis
|
|
========================
|
|
|
|
**THE REAL PROBLEM**: The `sweep_restocking_fee` function appears designed to sweep "whatever is left" to close out the quote. This suggests it's meant to be run ONCE per quote, not multiple times per duplicate payment.
|
|
|
|
Critical Questions:
|
|
1. **Multiple Restocking Sweeps**: If Payment 2 and Payment 3 both trigger restocking fee sweeps, what happens?
|
|
2. **Wallet State**: After first restocking sweep, is there anything left for the second sweep?
|
|
3. **Quote Closure**: Does each duplicate payment try to "close out" the same quote multiple times?
|
|
4. **Race Conditions**: What if both payments hit 20 confirmations simultaneously?
|
|
|
|
The Danger Zone:
|
|
- Restocking fee function may assume it's the final operation on a quote
|
|
- Multiple calls could lead to double-sweeping or wallet conflicts
|
|
- Each duplicate might try to sweep the entire remaining balance
|
|
- Quote may be "closed" multiple times with unpredictable results
|
|
|
|
This scenario exposes a fundamental design assumption: that restocking fees are quote-level operations, not payment-level operations.
|
|
|
|
Investigation Required:
|
|
- Does `sweep_restocking_fee` properly handle multiple calls per quote?
|
|
- Are restocking fees calculated per payment or per quote?
|
|
- What safety mechanisms prevent multiple restocking sweeps?
|
|
- Should restocking be quote-level (once) or payment-level (per duplicate)?
|
|
|
|
Conclusion: The Solution
|
|
=======================
|
|
|
|
**THE PROBLEM**: The original `sweep_restocking_fee` function used `sweep_all` for XMR, which swept the entire subaddress balance. In multi-duplicate scenarios, this caused fund theft between payments sharing the same subaddress.
|
|
|
|
**THE ROOT CAUSE**: Multiple duplicate payments to the same Monero subaddress + `sweep_all` operations = Payment 2's restocking sweep steals Payment 3's funds (and potentially the original payment's funds too).
|
|
|
|
**THE SOLUTION**: Implemented simple, safe logic for both XMR and DOGE:
|
|
|
|
.. code-block:: python
|
|
|
|
# Simple logic: send min(balance, expected_fee)
|
|
amount_to_send = min(current_balance, fee_amount)
|
|
|
|
**Key Insights**:
|
|
|
|
1. **Perfect Accounting**: The 9% restocking fee is exact because refund network fees are deducted from the refund itself, not the restocking fee.
|
|
|
|
2. **Simple Logic**:
|
|
- If balance < expected fee: Send balance (whatever is available)
|
|
- If balance >= expected fee: Send expected fee (exact amount)
|
|
|
|
3. **No Fund Theft**: Each payment only takes what it's owed, never more.
|
|
|
|
4. **No Dust Issues**: Any remaining amounts stay for future operations or dust collection by separate processes.
|
|
|
|
5. **Priority System**: Restocking fee sweeps are Priority 4 (dead last) and blocked by mid-tier confirmation requirements (XMR=20, DOGE=12) to ensure refunds complete before fee collection.
|
|
|
|
**RESULT**: The MONERO DOOM scenario now works correctly:
|
|
- Payment 2 sends exactly $0.09 restocking fee
|
|
- Payment 3 sends exactly $0.09 restocking fee
|
|
- No fund theft, no accounting errors, no wallet drainage
|
|
|
|
The crisis is resolved with elegant simplicity: `min(balance, expected_fee)` prevents all forms of fund theft while ensuring proper restocking fee collection. |