When the registry first enters an active-risk state, ConfidencePool is supposed to latch riskWindowStart and reset the global stake-time accumulators. This observed-risk latch is later used by the permissionless claimExpired() backstop to distinguish a real observed-risk CORRUPTED agreement from the no-risk fallback.
The issue is that _markRiskWindowStart() multiplies the full live stake by an absolute timestamp squared. A sufficiently large but valid ERC20 stake can be accepted before risk starts, then make every later active-risk observation revert with arithmetic overflow. As a result, riskWindowStart remains zero, and a later CORRUPTED agreement can fall through to EXPIRED instead of the intended auto-CORRUPTED backstop.
Likelihood:
This occurs when a pool uses a standard ERC20 with a very large supply/decimals and a staker deposits an amount large enough that totalEligibleStake * block.timestamp ** 2 is near type(uint256).max.
This occurs when the active-risk state is first observed after that large stake was accepted, because _markRiskWindowStart() is evaluated by pokeRiskWindow(), stake(), contributeBonus(), withdraw(), flagOutcome(), or claimExpired().
Impact:
The observed-risk latch can become permanently unsealable while the registry is active-risk, disabling the permissionless auto-CORRUPTED backstop.
A staker with the large position can recover principal through EXPIRED after the agreement becomes CORRUPTED, instead of having the position swept to recoveryAddress.
Add this test to test/unit/ConfidencePool.k2Bonus.t.sol:
Run:
Expected result:
The test passes because the active-risk observation reverts in _markRiskWindowStart(), leaving riskWindowStart == 0. The later claimExpired() call then skips the auto-CORRUPTED branch and resolves as EXPIRED.
Cap the maximum tracked stake so the timestamp-squared accounting cannot overflow.
Alternatively, refactor the accounting to store risk-window-relative time offsets instead of absolute timestamp-squared values.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.