The deployed code shows:
function expireRound(uint256 roundId) external { // <-- NO ACCESS CONTROL
Round storage r = rounds[roundId];
require(!r.settled && !r.expired, "E40");
require(block.timestamp > r.revealCloseAt + 300, "E51"); // 5 min delay
r.expired = true;
emit RoundExpired(roundId);
}
vs other functions:
function postRound(...) external onlyOracle // Has access control
function settleRound(...) external onlyOracle // Has access control
function openEpoch(...) external onlyOracleOrOwner // Has access control
Attack Flow:
- Oracle posts round N (10-minute windows)
- Agents commit answers, reveal answers
- Oracle is supposed to settle within 5 minutes after reveal window
- Attacker waits 5 minutes + calls expireRound(N)
- settleRound is BLOCKED (line 353: require(!r.expired))
- All credits for that round are permanently lost
- epochCredits never gets populated with correct answers from that round
- Participants lose rewards they earned
The deployed code shows:
function expireRound(uint256 roundId) external { // <-- NO ACCESS CONTROL
Round storage r = rounds[roundId];
require(!r.settled && !r.expired, "E40");
require(block.timestamp > r.revealCloseAt + 300, "E51"); // 5 min delay
r.expired = true;
emit RoundExpired(roundId);
}
vs other functions:
function postRound(...) external onlyOracle // Has access control
function settleRound(...) external onlyOracle // Has access control
function openEpoch(...) external onlyOracleOrOwner // Has access control
Attack Flow: