Skip to content

Commit

Permalink
chore: add explanation to rounding error test case
Browse files Browse the repository at this point in the history
  • Loading branch information
danielattilasimon committed Jan 2, 2025
1 parent 3ca74e1 commit 73fbf86
Showing 1 changed file with 32 additions and 0 deletions.
32 changes: 32 additions & 0 deletions test/Governance.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2504,6 +2504,38 @@ abstract contract GovernanceTest is Test {
);
}

// We find that a user's unallocated voting power can't be turned negative through manipulation, which is
// demonstrated in the next test.
//
// Whenever a user withdraws LQTY, they can lose more voting power than they should, due to rounding error in the
// calculation of their remaining offset:
//
// unallocatedOffset -= FLOOR(lqtyDecrease * unallocatedOffset / unallocatedLQTY)
// unallocatedLQTY -= lqtyDecrease
//
// For reference, unallocated voting power at time `t` is calculated as:
//
// unallocatedLQTY * t - unallocatedOffset
//
// The decrement of `unallocatedOffset` is rounded down, consequently `unallocatedOffset` is rounded up, in turn the
// voting power is rounded down. So when time a user has some relatively small positive unallocated voting power and
// a significant amount of unallocated LQTY, and withdraws a tiny amount of LQTY (corresponding to less than a unit
// of voting power), they lose a full unit of voting power.
//
// One might think that this can be done repeatedly in an attempt to manipulate unallocated voting power into
// negative range, thus being able to allocate negative voting power to an initiative (if done very close to the
// end of the present epoch), which would be bad as it would result in insolvency in initiatives that distribute
// rewards in proportion to voting power allocated by voters (such as `BribeInitiative`).
//
// However, we find that this manipulation stops being effective once unallocated voting power reaches zero. Having
// zero unallocated voting power means:
//
// unallocatedLQTY * t - unallocatedOffset = 0
// unallocatedLQTY * t = unallocatedOffset
//
// Thus when unallocated voting power is zero, `unallocatedOffset` is a multiple of `unallocatedLQTY`, so there can
// be no more rounding error when re-calculating `unallocatedOffset` on withdrawals.

function test_WhenWithdrawingTinyAmounts_VotingPowerDoesNotTurnNegativeDueToRoundingError(
uint256 initialVotingPower,
uint256 numWithdrawals
Expand Down

0 comments on commit 73fbf86

Please sign in to comment.