Gauge weight reduction via `GaugeController.change_gauge_weight()` breaks global accounting, potentially causing both outsized reward distribution and loss of user rewards
Audit: 2023-08-verwa-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L185-L206
https://github.com/code-423n4/2023-08-verwa/blob/main/src/LendingLedger.sol#L145-L182
https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L173-L183
https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L146-L161
https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L124-L132


# Vulnerability details

## Impact
Users may be given outsized rewards; therefore it is possible for `LendingLedger`'s CANTO balance to drop below the reward amount of a valid user claim, in which case that claim will fail. Furthermore, gauge decay accounting is made inaccurate.

## Proof of Concept
Note the below code and inline comments for `GaugeController.change_gauge_weight()`:
```
    function _change_gauge_weight(address _gauge, uint256 _weight) internal {
        uint256 old_gauge_weight = _get_weight(_gauge); // current weight of the gauge
        uint256 old_sum = _get_sum(); // global weight
        uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK;

        points_weight[_gauge][next_time].bias = _weight; // gauge's weight is set to the _weight argument. For the presented case, _weight is less than the current gauge weight
        time_weight[_gauge] = next_time; // gauge's "time checkpoint" is updated, not relevant for this issue

        uint256 new_sum = old_sum + _weight - old_gauge_weight; // new_sum is less than old_sum for this case
        points_sum[next_time].bias = new_sum; // global bias is updated
        time_sum = next_time; // global "time checkpoint" is updated, not relevant for this issue
    }

    /// @notice Allows governance to overwrite gauge weights
    /// @param _gauge Gauge address
    /// @param _weight New weight
    function change_gauge_weight(address _gauge, uint256 _weight) public onlyGovernance { // all logic is handled by the internal function _change_gauge_weight
        _change_gauge_weight(_gauge, _weight);
    }
```
We see here that the weight (bias) is updated for both the global state and the individual gauge, while slope is not updated at all. Consider the following: let the equation $f(t) = b - mt$ represent the gauge's decay equation before the weight is reduced by some amount $k$. (Note that $m$, or slope, is always positive in the protocol's code.) After the weight is reduced by calling `change_gauge_weight()`, the equation becomes $f(t) = -k + b -mt$. The slope is unchanged, but the t-axis intercept has changed from $t_{before} = b/m$ to $t_{after} = (-k + b)/m$, the latter of which is a smaller value. Now, because global "slope changes" (slope values that should be subtracted from the global slope upon their decay equations equaling zero) are stored in the `changes_sum` timestamp to slope mapping, which is not modified by `change_gauge_weight()`, there is a time period with length determined by the value of $t_1 - t_2$ during wh