Root + Impact
The checking for PoolStates::isTerminal of outcome is not performed
The checking for the terminal state is only done through IAttackRegistry.ContractState
checking with outcome is not performed
so the checking for terminal state is inaccurate
Description
On PoolStates library we can see it has a function to check if the outcome is in terminal ot not
library PoolStates {
enum Outcome {
UNRESOLVED,
SURVIVED,
CORRUPTED,
EXPIRED
}
function isTerminal(Outcome outcome) internal pure returns (bool) {
return outcome != Outcome.UNRESOLVED;
}
}
But it is never used
Like on _observePoolState, it is checking for if the state is terminal ot not through IAttackRegistry.ContractState
and then calling for _markRiskWindowEnd
function _observePoolState() internal returns (IAttackRegistry.ContractState state) {
state = _getAgreementState();
if (
!scopeLocked && state != IAttackRegistry.ContractState.NOT_DEPLOYED
&& state != IAttackRegistry.ContractState.NEW_DEPLOYMENT
) {
scopeLocked = true;
emit ScopeLocked(block.timestamp);
}
if (riskWindowStart == 0 && _isActiveRiskState(state)) {
_markRiskWindowStart();
}
if (riskWindowEnd == 0 && _isTerminalState(state)) {
_markRiskWindowEnd();
}
}
function _isActiveRiskState(IAttackRegistry.ContractState s) internal pure returns (bool) {
return s == IAttackRegistry.ContractState.UNDER_ATTACK || s == IAttackRegistry.ContractState.PROMOTION_REQUESTED;
}
function _isTerminalState(IAttackRegistry.ContractState s) internal pure returns (bool) {
return s == IAttackRegistry.ContractState.PRODUCTION || s == IAttackRegistry.ContractState.CORRUPTED;
}
It is checking for _isTerminalState, but it does not check for the outcome
as the outcome can be in a terminal state too
so as a result calling the _markRiskWindowEnd(); is inaccurate
if (riskWindowEnd == 0 && _isTerminalState(state)) {
_markRiskWindowEnd();
}
Recommended Mitigation
When checking for the terminal, also check with the outcome too, using PoolStates::isTerminal
Like an example
- if (riskWindowEnd == 0 && _isTerminalState(state)) {
+ if ((riskWindowEnd == 0 && PoolStates.isTerminal(outcome)) || (riskWindowEnd == 0 && _isTerminalState(state))) {
_markRiskWindowEnd();
}