Description
Implement a sliding-window rate limiter for invoice payments. Each payer has a configurable cooldown period between payments on the same invoice. Additionally, implement a global per-invoice payment rate limit (max N payments within any rolling time window) to prevent coordinated payment flooding.
Acceptance Criteria
Context
_pay is in contracts/split/src/lib.rs (~line 1313)
InvoiceOptions, InvoiceCore, InvoiceExt are in contracts/split/src/types.rs
- Nonce storage pattern at
nonce_key(invoice_id, payer) is a reference for per-payer keys
env.ledger().timestamp() provides current time
- The ring buffer must use
soroban_sdk::Vec (not std) and fit within Soroban storage constraints
Description
Implement a sliding-window rate limiter for invoice payments. Each payer has a configurable cooldown period between payments on the same invoice. Additionally, implement a global per-invoice payment rate limit (max N payments within any rolling time window) to prevent coordinated payment flooding.
Acceptance Criteria
InvoiceOptionsacceptspayment_cooldown_secs: Option<u64>andmax_payments_per_window: Option<u32>andpayment_window_secs: Option<u64>_paystores last payment timestamp at key(symbol_short!("pcd"), invoice_id, payer)and panics with"payment cooldown active"if within window_paymaintains a ring-buffer of recent payment timestamps in persistent storage, evicts entries older thanpayment_window_secs, and panics with"payment rate limit exceeded"if count >=max_payments_per_windowInvoiceExt(notInvoiceCore) to keep storage efficientget_invoice_extreturns both fields correctlytest_cooldown_blocks_same_payer_within_window— same payer blocked, different payer succeedstest_rate_limit_blocks_after_n_payments— Nth+1 payment blocked regardless of payertest_rate_limit_window_resets— payments succeed again after window expirestest_cooldown_and_rate_limit_independent— both can be active simultaneouslyContext
_payis incontracts/split/src/lib.rs(~line 1313)InvoiceOptions,InvoiceCore,InvoiceExtare incontracts/split/src/types.rsnonce_key(invoice_id, payer)is a reference for per-payer keysenv.ledger().timestamp()provides current timesoroban_sdk::Vec(not std) and fit within Soroban storage constraints