The raffle contract's randomness fulfillment mechanism (provide_randomness) was vulnerable to front-running and manipulation attacks. In the original implementation, an oracle could submit randomness immediately after a raffle transitioned to Drawing, potentially allowing an attacker to:
- Observe the pending raffle finalization
- Manipulate oracle behavior to favor specific outcomes
- Execute malicious transactions in the same block
To address this vulnerability, we've implemented a minimum ledger delay between randomness request and fulfillment:
- A constant
RANDOMNESS_MIN_DELAY_LEDGERS = 10is enforced - When randomness is requested (during the Drawing phase transition), the current ledger sequence is stored under
DataKey::RandomnessRequestLedger - In
provide_randomness, we check that the current ledger sequence is at least 10 ledgers higher than the request ledger - If fulfillment is attempted too early, the transaction is rejected with
Error::RandomnessTooEarly
This delay ensures there's sufficient time for:
- The market and participants to stabilize
- No same-block manipulation
- A clear window between request and fulfillment
- Drawing Lock: Exclusive lock to prevent concurrent state transitions
- Oracle Timeout: Fallback mechanism if oracle doesn't respond within 200 ledgers
- Reentrancy Guard: Prevents reentrant attacks