Vulnerability: `reportOutOfOrderValidatorExits` does not updates the heap order
Severity: high

Description:
When an operator's validator exits without a withdrawal request, the owner can invoke the `reportOutOfOrderValidatorExits` function to increase the `exited` portion of the operator validators. However, this action does not update the heap. Consequently, during subsequent allocation or deallocation processes, the heap may incorrectly mark validators as `exited`.\\nFirst, let's see how the utilization is determined for native ETH deposits for operators which is calculated as: `operatorShares.allocation.divWad(operatorShares.cap)` where as the allocation is the total `deposited` validators and the `cap` is predetermined value by the owner of the registry.\\nWhen the heap is retrieved from the storage, here how it is fetched:\\n```\\nfunction getOperatorUtilizationHeapForETH(RioLRTOperatorRegistryStorageV1.StorageV1 storage s)\\n        internal\\n        view\\n        returns (OperatorUtilizationHeap.Data memory heap)\\n    {\\n        uint8 numActiveOperators = s.activeOperatorCount;\\n        if (numActiveOperators == 0) return OperatorUtilizationHeap.Data(new OperatorUtilizationHeap.Operator[](0), 0);\\n\\n        heap = OperatorUtilizationHeap.initialize(MAX_ACTIVE_OPERATOR_COUNT);\\n\\n        uint256 activeDeposits;\\n        IRioLRTOperatorRegistry.OperatorValidatorDetails memory validators;\\n        unchecked {\\n            uint8 i;\\n            for (i = 0; i < numActiveOperators; ++i) {\\n                uint8 operatorId = s.activeOperatorsByETHDepositUtilization.get(i);\\n\\n                // Non-existent operator ID. We've reached the end of the heap.\\n                if (operatorId == 0) break;\\n\\n                validators = s.operatorDetails[operatorId].validatorDetails;\\n                activeDeposits = validators.deposited - validators.exited;\\n                heap.operators[i + 1] = OperatorUtilizationHeap.Operator({\\n                    id: operatorId,\\n                    utilization: activeDeposits.divWad(validators.cap)\\n                });\\n            }\\n            heap.count = i;\\n        }\\n    }\\n```\\n\\nas we can see, the heap is always assumed to be order in the storage when the registry fetches it initially. There are no ordering of the heap when requesting the heap initially.\\nWhen, say the deallocation happens via an user withdrawal request, the queue can exit early if the operator in the heap has "0" room:\\n```\\n function deallocateETHDeposits(uint256 depositsToDeallocate) external onlyCoordinator returns (uint256 depositsDeallocated, OperatorETHDeallocation[] memory deallocations) {\\n        deallocations = new OperatorETHDeallocation[](s.activeOperatorCount);\\n\\n\\n        OperatorUtilizationHeap.Data memory heap = s.getOperatorUtilizationHeapForETH();\\n        if (heap.isEmpty()) revert NO_AVAILABLE_OPERATORS_FOR_DEALLOCATION();\\n\\n\\n        uint256 deallocationIndex;\\n        uint256 remainingDeposits = depositsToDeallocate;\\n\\n\\n        bytes memory pubKeyBatch;\\n        while (remainingDeposits > 0) {\\n            uint8 operatorId = heap.getMax().id;\\n\\n\\n            OperatorDetails storage operator = s.operatorDetails[operatorId];\\n            OperatorValidatorDetails memory validators = operator.validatorDetails;\\n            -> uint256 activeDeposits = validators.deposited - validators.exited;\\n\\n\\n            // Exit early if the operator with the highest utilization rate has no active deposits,\\n            // as no further deallocations can be made.\\n            -> if (activeDeposits == 0) break;\\n             .\\n        }\\n        .\\n    }\\n```\\n\\n`reportOutOfOrderValidatorExits` increases the "exited" part of the operators validator:\\n```\\nfunction reportOutOfOrderValidatorExits(uint8 operatorId, uint256 fromIndex, uint256 validatorCount) external {\\n       .\\n       .\\n        // Swap the position of the validators starting from the `fromIndex` with the validators that were next in line to be exited.\\n        VALIDATOR_DETAILS_POSITION.swapValidatorDetails(operatorId, fromIndex, validators.exited, validatorCount);\\n        -> operator.validatorDetails.exited += uint40(validatorCount);\\n\\n        emit OperatorOutOfOrderValidatorExitsReported(operatorId, validatorCount);\\n    }\\n```\\n\\nNow, knowing all these above, let's do an example where calling `reportOutOfOrderValidatorExits` can make the heap work wrongly and exit prematurely.\\nAssume there are 3 operators which has native ETH deposits. operatorId 1 -> utilization 5% operatorId 2 -> utilization 10% operatorId 3 -> utilization 15%\\nsuch operators would be ordered in the heap as: heap.operators[1] -> operatorId: 1, utilization: 5 heap.operators[2] -> operatorId: 2, utilization: 10 heap.operators[3] -> operatorId: 3, utilization: 15 heap.getMin() -> operatorId: 1, utilization: 5 heap.getMax() -> operatorId:3, utilization 15\\nnow, let's say the "cap" is 100 for all of the operators which means that: operatorId 1 -> validator.deposits = 5, validator.exit = 0 operatorId 2 -> validator.deposits = 10, validator.exit = 0 operatorId 3 -> validator.deposits = 15, validator.exit = 0\\nLet's assume that the operator 3 exits 15 validator from beacon chain without prior to a user request, which is a reason for owner to call `reportOutOfOrderValidatorExits` to increase the exited validators.\\nWhen the owner calls `reportOutOfOrderValidatorExits` for the operatorId 3, the exited will be 15 for the operatorId 3. After the call the operators validator balances will be: operatorId 1 -> validator.deposits = 5, validator.exit = 0 operatorId 2 -> validator.deposits = 10, validator.exit = 8 operatorId 3 -> validator.deposits = 15, validator.exit = 15\\nhence, the utilizations will be: operatorId 1 -> utilization 5% operatorId 2 -> utilization 10% operatorId 3 -> utilization 0%\\nwhich means now the operatorId 3 has the lowest utilization and should be the first to get deposits and last to unwind deposits from. However, the heap is not re-ordered meaning that the minimum in the heap is still opeartorId 1 and the maximum is still operatorId 3!\\nNow, when a user tries to withdraw, the first deallocation target will be the operatorId 3 because the heap thinks that it is the most utilized still.\\nHence, the user will not be able to request the withdrawal!\\nCoded PoC:\\n```\\n// forge test --match-contract OperatorUtilizationHeapTest --match-test test_RemovingValidatorMessesTheHeap -vv\\n    function test_RemovingValidatorMessesTheHeap() public {\\n        OperatorUtilizationHeap.Data memory heap = OperatorUtilizationHeap.initialize(5);\\n\\n        // @review initialize and order 3 operators \\n        heap.insert(OperatorUtilizationHeap.Operator({id: 1, utilization: 5}));\\n        heap.store(heapStore);\\n\\n        heap.insert(OperatorUtilizationHeap.Operator({id: 2, utilization: 10}));\\n        heap.store(heapStore);\\n\\n        heap.insert(OperatorUtilizationHeap.Operator({id: 3, utilization: 15}));\\n        heap.store(heapStore);\\n\\n        // @review mimick how the heap can be fetched from the storage initially\\n        uint8 numActiveOperators = 3;\\n        OperatorUtilizationHeap.Data memory newHeap = OperatorUtilizationHeap.initialize(64);\\n        uint8 i;\\n        for (i = 0; i < numActiveOperators; ++i) {\\n            uint8 operatorId = heapStore.get(i);\\n            if (operatorId == 0) break;\\n\\n            newHeap.operators[i+1] = OperatorUtilizationHeap.Operator({\\n                   id: operatorId,\\n                   utilization: heap.operators[operatorId].utilization\\n            });\\n        }\\n        newHeap.count = i;\\n\\n        // @review assume the reportValidatorAndExits called, and now the utilization is "0"\\n        heap.updateUtilizationByID(3, 0);\\n        // @review this should be done, but the heap is not stored! \\n        // heap.store(heapStore);\\n\\n        console.log("1st", heap.operators[1].id);\\n        console.log("2nd", heap.operators[2].id);\\n        console.log("3rd", heap.operators[3].id);\\n        console.log("origin heaps min", heap.getMin().id);\\n        console.log("origin heaps max", heap.getMax().id);\\n\\n        console.log("1st", newHeap.operators[1].id);\\n        console.log("2nd", newHeap.operators[2].id);\\n        console.log("3rd", newHeap.operators[3].id);\\n        console.log("new heaps min", newHeap.getMin().id);\\n        console.log("new heaps max", newHeap.getMax().id);\\n\\n        // @review mins and maxs are mixed\\n        assertEq(newHeap.getMin().id, 1);\\n        assertEq(heap.getMin().id, 3);\\n        assertEq(heap.getMax().id, 2);\\n        assertEq(newHeap.getMax().id, 3);\\n    }\\n```\\n

Impact:
Heap can be mixed, withdrawals and deposits can fail, hence I will label this as high.

Recommended mitigation:
update the utilization in the reportOutOfOrderValidatorExits function