Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: incentive accumulator migration #7416

Merged
merged 38 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
246e5f1
feat: scaling factor for pool uptime accumulator
p0mvn Feb 2, 2024
99022a2
changelog
p0mvn Feb 3, 2024
213cec6
updates
p0mvn Feb 5, 2024
04a91c6
updates
p0mvn Feb 5, 2024
880e3d1
Merge branch 'main' into roman/pool-incentive-scaling-factor
p0mvn Feb 5, 2024
1367d0c
handle overflows
p0mvn Feb 5, 2024
748edd8
Apply suggestions from code review
p0mvn Feb 5, 2024
3158f90
updates
p0mvn Feb 5, 2024
f76b9cb
Apply suggestions from code review
p0mvn Feb 5, 2024
f254a26
fix test and clean up
p0mvn Feb 5, 2024
e36f4a1
spelling
p0mvn Feb 5, 2024
9e10789
future proof multiplication overflow
p0mvn Feb 6, 2024
de4b9ba
clean up
p0mvn Feb 6, 2024
8a43a0b
comment
p0mvn Feb 6, 2024
bab4173
lint
p0mvn Feb 6, 2024
fea2d75
Merge branch 'main' into roman/pool-incentive-scaling-factor
p0mvn Feb 7, 2024
5ac3e97
rename
p0mvn Feb 7, 2024
2d49b83
feat: incentive accumulator upgrade handler migration
p0mvn Feb 6, 2024
c5decc9
lint
p0mvn Feb 6, 2024
b1ee555
go mod
p0mvn Feb 6, 2024
32aa8c9
fix test
p0mvn Feb 6, 2024
53bd32d
implement TestMigrateAccumulatorToScalingFactor
p0mvn Feb 7, 2024
c65aaa8
basic genesis test
p0mvn Feb 7, 2024
b58ccbf
fix comment
p0mvn Feb 7, 2024
c03d5e8
clean up tests
p0mvn Feb 7, 2024
e1f07d3
clean up helpers
p0mvn Feb 7, 2024
52b389a
add test for get position IDs by pool ID
p0mvn Feb 7, 2024
3f30364
test for getIncentiveScalingFactorForPool
p0mvn Feb 7, 2024
0004a14
upgrade handler test and tick migration
p0mvn Feb 7, 2024
ab7fa11
update spec
p0mvn Feb 7, 2024
99e3a8d
Merge branch 'main' into roman/upgrade-handler-migration
p0mvn Feb 7, 2024
c9bc00f
Merge branch 'main' into roman/upgrade-handler-migration
p0mvn Feb 8, 2024
7778fe1
updates
p0mvn Feb 8, 2024
68020b1
updates
p0mvn Feb 8, 2024
06c3ad1
feat/fix: add more pools to migrate, fix key parsing bug, add more lo…
p0mvn Feb 9, 2024
56b8146
introduce helper
p0mvn Feb 9, 2024
4ef439b
Update x/concentrated-liquidity/pool.go
p0mvn Feb 9, 2024
71b47b6
Merge branch 'main' into roman/upgrade-handler-migration
mergify[bot] Feb 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
future proof multiplication overflow
  • Loading branch information
p0mvn committed Feb 6, 2024
commit 9e10789efcd423e49320cb9e8389067b28d84cc0
4 changes: 4 additions & 0 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,7 @@ func (k Keeper) GetPoolHookContract(ctx sdk.Context, poolId uint64, actionPrefix
func ScaleUpTotalEmittedAmount(totalEmittedAmount osmomath.Dec) (scaledTotalEmittedAmount osmomath.Dec, err error) {
return scaleUpTotalEmittedAmount(totalEmittedAmount)
}

func ComputeTotalIncentivesToEmit(timeElapsedSeconds osmomath.Dec, emissionRate osmomath.Dec) (totalEmittedAmount osmomath.Dec, err error) {
return computeTotalIncentivesToEmit(timeElapsedSeconds, emissionRate)
}
43 changes: 40 additions & 3 deletions x/concentrated-liquidity/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,13 @@ func calcAccruedIncentivesForAccum(ctx sdk.Context, accumUptime time.Duration, l
}

// Total amount emitted = time elapsed * emission
totalEmittedAmount := timeElapsed.MulTruncate(incentiveRecordBody.EmissionRate)
totalEmittedAmount, err := computeTotalIncentivesToEmit(timeElapsed, incentiveRecordBody.EmissionRate)
if err != nil {
ctx.Logger().Info(types.IncentiveOverflowPlaceholderName, "pool_id", poolID, "incentive_id", incentiveRecord.IncentiveId, "time_elapsed", timeElapsed, "emission_rate", incentiveRecordBody.EmissionRate, "error", err.Error())
// Silently ignore the truncated incentive record to avoid halting the entire accumulator update.
// Continue to the next incentive record.
continue
}

// We scale up the remaining rewards to avoid truncation to zero
// when dividing by the liquidity in the accumulator.
Expand Down Expand Up @@ -289,7 +295,13 @@ func calcAccruedIncentivesForAccum(ctx sdk.Context, accumUptime time.Duration, l

// We scale up the remaining rewards to avoid truncation to zero
// when dividing by the liquidity in the accumulator.
remainingRewardsScaled := remainingRewards.MulTruncate(perUnitLiqScalingFactor)
remainingRewardsScaled, err := scaleUpTotalEmittedAmount(remainingRewards)
if err != nil {
ctx.Logger().Info(types.IncentiveOverflowPlaceholderName, "pool_id", poolID, "incentive_id", incentiveRecord.IncentiveId, "time_elapsed", timeElapsed, "emission_rate", incentiveRecordBody.EmissionRate, "error", err.Error())
// Silently ignore the truncated incentive record to avoid halting the entire accumulator update.
// Continue to the next incentive record.
continue
}
remainingIncentivesPerLiquidity := remainingRewardsScaled.QuoTruncateMut(liquidityInAccum)

emittedIncentivesPerLiquidity = sdk.NewDecCoinFromDec(incentiveRecordBody.RemainingCoin.Denom, remainingIncentivesPerLiquidity)
Expand All @@ -314,7 +326,7 @@ func scaleUpTotalEmittedAmount(totalEmittedAmount osmomath.Dec) (scaledTotalEmit

if r != nil {
telemetry.IncrCounter(1, types.IncentiveOverflowPlaceholderName)
err = types.IncentiveOverflowError{
err = types.IncentiveScalingFactorOverflowError{
PanicMessage: fmt.Sprintf("%v", r),
}
}
Expand All @@ -323,6 +335,31 @@ func scaleUpTotalEmittedAmount(totalEmittedAmount osmomath.Dec) (scaledTotalEmit
return totalEmittedAmount.MulTruncate(perUnitLiqScalingFactor), nil
}

// computeTotalIncentivesToEmit computes the total incentives to emit based on the time elapsed and emission rate.
// Returns error if timeElapsed or emissionRate are too high, causing overflow during multiplicaiton.
func computeTotalIncentivesToEmit(timeElapsedSeconds osmomath.Dec, emissionRate osmomath.Dec) (totalEmittedAmount osmomath.Dec, err error) {
defer func() {
r := recover()

if r != nil {
telemetry.IncrCounter(1, types.IncentiveOverflowPlaceholderName)
err = types.IncentiveEmissionOvrflowError{
PanicMessage: fmt.Sprintf("%v", r),
}
}
}()

// This may panic if emissionRate is too high and causes overflow during multiplication
// We are unlikely to see an overflow due to too much time elapsing since
// 100 years in seconds is roughly
// 3.15576e9 * 100 = 3.15576e11
// 60 * 60 * 24 * 365 * 100 = 3153600000 seconds
// The bit decimal bit length is 2^256 which is arond 10^77
// However, it is possible for an attacker to try and create incentives with a very high emission rate
// consisting of cheap token in the USD denomination. This is why we have the panic recovery above.
return timeElapsedSeconds.MulTruncate(emissionRate), nil
}

// findUptimeIndex finds the uptime index for the passed in min uptime.
// Returns error if uptime index cannot be found.
func findUptimeIndex(uptime time.Duration) (int, error) {
Expand Down
17 changes: 17 additions & 0 deletions x/concentrated-liquidity/incentives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3690,6 +3690,23 @@ func (s *KeeperTestSuite) TestScaledUpTotalIncentiveAmount() {
s.Require().ErrorContains(err, "overflow")
}

// This test shows that it is possible to compute the total incentives to emit without overflow.
// An error is returned if overflow occurs but no panic.
func (s *KeeperTestSuite) TestComputeTotalIncentivesToEmit() {

oneHundredYearsSecs := osmomath.NewDec(int64((time.Hour * 24 * 365 * 100).Seconds()))

totalIncentiveAmount, err := cl.ComputeTotalIncentivesToEmit(oneHundredYearsSecs, osmomath.NewDec(1))
s.Require().NoError(err)
s.Require().Equal(osmomath.NewDec(1).Mul(oneHundredYearsSecs), totalIncentiveAmount)

// The value of 1_000_000_000_000 is hand-picked to be close to the max of 2^256 so that
// when multiplied by 100 years, it overflows.
_, err = cl.ComputeTotalIncentivesToEmit(oneHundredYearsSecs, oneE60Dec.MulInt64(1_000_000_000_000))
s.Require().Error(err)
s.Require().ErrorContains(err, "overflow")
}

// scaleUptimeAccumulators scales the uptime accumulators by the scaling factor.
// This is to avoid truncation to zero in core logic when the liquidity is large.
func (s *KeeperTestSuite) scaleUptimeAccumulators(uptimeAccumulatorsToScale []sdk.DecCoins) []sdk.DecCoins {
Expand Down
12 changes: 10 additions & 2 deletions x/concentrated-liquidity/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,10 +931,18 @@ func (e InvalidActionPrefixError) Error() string {
return fmt.Sprintf("invalid action prefix (%s). Valid actions: %s", e.ActionPrefix, e.ValidActions)
}

type IncentiveOverflowError struct {
type IncentiveScalingFactorOverflowError struct {
PanicMessage string
}

func (e IncentiveOverflowError) Error() string {
func (e IncentiveScalingFactorOverflowError) Error() string {
return fmt.Sprintf("totalEmittedAmount is too high, causing overflow when applying scaling factor: %s", e.PanicMessage)
}

type IncentiveEmissionOvrflowError struct {
PanicMessage string
}

func (e IncentiveEmissionOvrflowError) Error() string {
return fmt.Sprintf("either too much time has passed since last pool update or the emission rate is too high, causing overflow: %s", e.PanicMessage)
}