LOW severity: [MAOR-11] Oracle report sanity check positive gains limit could result in DoS. **Severity:** Low

**Path:** src/Oracle.sol:sanityCheckUpdate#L362-484

**Description:**

Once the oracle client has generated a report, it submits this report on-chain to the `Oracle.sol` contract. The Oracle contract will pass the report through a sanity check using `sanityCheckUpdate` and it...

Vulnerable code:
// 7200 slots per day * 365 days per year = 2628000 slots per year
// assuming 5% yield per year
// 5% / 2628000 = 1.9025e-8
// 1.9025e-8 per slot = 19025 PPT
maxConsensusLayerGainPerBlockPPT = 190250; // 10x approximate rate

uint256 upperBound = baselineGrossCLBalance
    + Math.mulDiv(maxConsensusLayerGainPerBlockPPT * reportSize, baselineGrossCLBalance, _PPT_DENOMINATOR);
if (newGrossCLBalance > upperBound) {
    return ("Consensus layer change above max gain", newGrossCLBalance, upperBound);
}

function sanityCheckUpdate(OracleRecord memory prevRecord, OracleRecord calldata newRecord)
        public
        view
        returns (string memory, uint256, uint256)
    {
        uint64 reportSize = newRecord.updateEndBlock - newRecord.updateStartBlock + 1;
        [..]
        {
            //
            // Consensus layer balance change from the previous period.
            //
            // Checks that the change in the consensus layer balance is within the bounds given by the maximum loss and
            // minimum gain parameters. For example, a major slashing event will cause an out of bounds loss in the
            // consensus layer.

            // The baselineGrossCLBalance represents the expected growth of our validators balance in the new period
            // given no slashings, no rewards, etc. It's used as the baseline in our upper (growth) and lower (loss)
            // bounds calculations.
            uint256 baselineGrossCLBalance = prevRecord.currentTotalValidatorBalance
                + (newRecord.cumulativeProcessedDepositAmount - prevRecord.cumulativeProcessedDepositAmount);

            // The newGrossCLBalance is the actual amount of ETH we have recorded in the consensus layer for the new
            // record period.
            uint256 newGrossCLBalance = newRecord.currentTotalValidatorBalance
                + newRecord.windowWithdrawnPrincipalAmount + newRecord.windowWithdrawnRewardAmount;

            {
                // Relative lower bound on the net decrease of ETH on the consensus layer.
                // Depending on the parameters the loss term might completely dominate over the minGain one.
                //
                // Using a minConsensusLayerGainPerBlockPPT greater than 0, the lower bound becomes an upward slope.
                // Setting minConsensusLayerGainPerBlockPPT, the lower bound becomes a constant.
                uint256 lowerBound = baselineGrossCLBalance
                    - Math.mulDiv(maxConsensusLayerLossPPM, baselineGrossCLBalance, _PPM_DENOMINATOR)
                    + Math.mulDiv(minConsensusLayerGainPerBlockPPT * reportSize, baselineGrossCLBalance, _PPT_DENOMINATOR);

                if (newGrossCLBalance < lowerBound) {
                    return ("Consensus layer change below min gain or max loss", newGrossCLBalance, lowerBound);
                }
            }
            {
                // Upper bound on the rewards generated by validators scaled linearly with time and number of active
                // validators.
                uint256 upperBound = baselineGrossCLBalance
                    + Math.mulDiv(maxConsensusLayerGainPerBlockPPT * reportSize, baselineGrossCLBalance, _PPT_DENOMINATOR);

                if (newGrossCLBalance > upperBound) {
                    return ("Consensus layer change above max gain", newGrossCLBalance, upperBound);
                }
            }
        }

        return ("", 0, 0);
    }