Normal behavior: after ConfidencePool observes a terminal state and sets riskWindowEnd, no new stake should be accepted because the covered risk window has ended.
I observed that stake() only checks the current live registry state. It does not check riskWindowEnd. If the registry is later rewound/replaced to a deposit-allowed state, a late user can stake after the risk window ended and still receive a positive quadratic bonus score.
Likelihood:
This occurs when riskWindowEnd has already been sealed and the Safe Harbor registry pointer/state is later replaced, migrated, or rewound to a deposit-allowed state such as NEW_DEPLOYMENT.
The code already protects withdrawals from registry rewind with local latches, but stake() does not apply an equivalent terminal-latch check.
Impact:
A post-terminal staker can be included in snapshotTotalStaked despite taking no covered risk.
The PoC shows the late staker captures more than 90e18 of a 100e18 bonus, while the real risk-bearing staker receives less than 2e18.
Place this file at test/unit/PostTerminalRewindStakePoC.t.sol:
Run:
Observed output:
This fix makes riskWindowEnd a permanent local terminal latch for deposits. After the pool has observed the risk window ending, future registry rewinds or migrations cannot reopen staking. This keeps the bonus formula consistent because every snapshotted staker must have entered before or during the covered risk window, not after T = riskWindowEnd.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.