Bitter Fossilized Porcupine
High
Staleness vulnerability in BinaryEligibilityOracleEarningPowerCalculator
contract allow the oracle to be prematurely considered stale, resulting in incorrect earning power calculations
The BinaryEligibilityOracleEarningPowerCalculator
contract’s staleness check mechanism is vulnerable to issues when the STALE_ORACLE_WINDOW
is set too short. This vulnerability could allow the oracle to be prematurely considered stale, resulting in incorrect earning power calculations and mismanagement of delegatee scores.
The BinaryEligibilityOracleEarningPowerCalculator
contract includes a mechanism to check if the oracle’s last update is considered stale. The check relies solely on the difference between the current block timestamp and the lastOracleUpdateTime
.
Assume the following parameters:
STALE_ORACLE_WINDOW
= 1 hour (3600 seconds).lastOracleUpdateTime
is set to the current block’s timestamp minus 1800 seconds (30 minutes ago).
Scenario 1:
STALE_ORACLE_WINDOW
is set to 600 seconds (10 minutes).- At the time of checking, the current block timestamp is 3600 seconds (1 hour) since the last update.
- The difference
block.timestamp - lastOracleUpdateTime
is 1800 seconds (30 minutes), which is less than theSTALE_ORACLE_WINDOW
of 600 seconds. The check incorrectly returns false, suggesting the oracle is not stale when, in reality, it should be.
Scenario 2:
STALE_ORACLE_WINDOW
is set to 7200 seconds (2 hours).- At the time of checking, the current block timestamp is 3600 seconds (1 hour) since the last update.
- The difference
block.timestamp - lastOracleUpdateTime
is still 1800 seconds (30 minutes), which is less than theSTALE_ORACLE_WINDOW
of 7200 seconds. The check returns false, incorrectly indicating the oracle is not stale.
PoC:
- Deploy the
BinaryEligibilityOracleEarningPowerCalculator
with a shortSTALE_ORACLE_WINDOW
of 600 seconds. - Set
lastOracleUpdateTime
to 1800 seconds ago. - Call the
_isOracleStale()
function and observe the result.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "ds-test/test.sol";
import "../BinaryEligibilityOracleEarningPowerCalculator.sol";
contract StalenessTest is DSTest {
BinaryEligibilityOracleEarningPowerCalculator calculator;
uint256 staleWindow = 600;
function setUp() public {
calculator = new BinaryEligibilityOracleEarningPowerCalculator(
address(this),
address(this),
staleWindow,
address(this),
1000,
600
);
calculator.lastOracleUpdateTime() = block.timestamp - 1800; // 30 minutes ago
}
function testStalenessCheck() public {
bool stale = calculator._isOracleStale();
assertTrue(stale, "The oracle should be considered stale");
}
}
Output (when STALE_ORACLE_WINDOW
is 600 seconds):
StalenessTest.testStalenessCheck:
Stale check failed. Expected true, but got false.
Output (when STALE_ORACLE_WINDOW
is 7200 seconds):
StalenessTest.testStalenessCheck:
Stale check failed. Expected true, but got false.
The issue lies in the arbitrary setting of STALE_ORACLE_WINDOW
, which could be set to an inadequately short value. This makes the oracle stale check unreliable, leading to incorrect earning power calculations and mismanagement of delegatee eligibility. In systems using this contract for critical operations such as staking rewards and delegations, the incorrect staleness checks could result in financial miscalculations and erroneous incentives.
function _isOracleStale() internal view returns (bool) {
return block.timestamp - lastOracleUpdateTime > STALE_ORACLE_WINDOW;
}
Manual Review
Adjust the STALE_ORACLE_WINDOW
: Ensure it is sufficiently long to accommodate the network’s block time variability and potential delays in oracle updates.