YieldToken holders are able to effectively steal collateral from PrincipalToken holders when there is a temporary down-rebase in the ERC4626 token's yield
Audit: 2024-02-spectra-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-02-spectra/blob/main/src/libraries/PrincipalTokenUtil.sol#L55-L130


# Vulnerability details

## Impact

For Spectra's system to work as intended, the PrincipalToken holders are supposed to be able to withdraw their deposited collateral in full in the following cases: value of the ERC4626 collateral increases, or value of the collateral remains the same. However, this is not the case when there is a temporary down-rebase (ERC4626 collateral loses value), prior to an increase back to e.g. the same value when the PrincipalToken holder first deposited. When this occurs, the YieldToken holder(s) is able to steal a percentage of the collateral from the PrincipalToken holder equal to the temporary down-rebase percentage, even though in absolute terms, there was no loss of the underlying value of the ERC4626 collateral.

## Proof of Concept

Consider the following simplified flow where the value of the ERC4626 collateral of the PrincipalToken first decreases by 90%, then increases by the same amount (in absolute terms), such that there is ultimately no loss of yield. In this case, the YieldToken holder can take certain actions to steal 90% of the underlying collateral from the PrincipalToken holder.

(1) Consider an example where UserA deposits collateral through calling `PrincipalToken:deposit`. We assume that the current `ibtRate` is 1/1, meaning it will be 1e27, when converted to RAY. The starting `ptRate` will also be k1e27. For simplicity, we assume that there are no fees, meaning the shares minted to UserA when they deposit 1e18 of the `ibt` token will be (from `PrincipalToken:_convertIBTsToShares`):
```solidity
shares = _ibts.mulDiv(
	_ibtRate,
	_ptRate,
	_roundUp ? Math.Rounding.Ceil : Math.Rounding.Floor
);
```
This will be `1e18 * 1e27 / 1e27 = 1e18`. Therefore UserA will have 1e18 PrincipalTokens. We then assume he sells the 1e18 associated YieldTokens to UserB. Note that this sets `ibtRateOfUser[..]` and `ptRateOfUser[_user]` for both users to be 1e27.

(2) Now, let's assume there is a down rebase, where the ERC4626 collateral has lost 90% of its value. In this scenario, UserB can call `UpdateYield(..)` on any arbitrary address, and this will update the prevailing ptRate and ibtRate by triggering the following call:
```solidity
(uint256 _ptRate, uint256 _ibtRate) = _updatePTandIBTRates();
```
This will set the current ptRate to 1e26.

(3) Now lets assume this temporary downturn in the value of the ERC4626 token is fully corrected (meaning compared to when UserA first deposited, the IBTRate is exactly the same). In this case, let's see what has happened to UserB's yield when they call `updateYield`. Firstly, the prevailing ptRate is now 1e26 (as it can never increase), whereas the prevailing ibtRate is 1e27 (as it increased again). In the call to `UpdateYield` for UserB, it will trigger `PrincipalTokenUtil._computeYield(..)` which has the following logic:
```solidity
...
if (_oldPTRate > _pt