-
Notifications
You must be signed in to change notification settings - Fork 607
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
[Uptime Incentives]: Add authorized uptime validation for new NoLock gauges #7376
Conversation
// Assert that pool id and gauge id link meant for internally incentivized gauges is unset. | ||
_, err := s.App.PoolIncentivesKeeper.GetPoolGaugeId(s.Ctx, tc.poolId, tc.distrTo.Duration) | ||
s.Require().Error(err) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Highlighting this for review @p0mvn. The key-related change in this PR means that the "general linking" between gauges and pools is now based on duration, which means that this check is no longer necessary.
It also technically means that we can skip the "internal gauge link" component of the internal gauge setup, but that logic is not well abstracted so it would require meaningful additional work to save on the duplicate write that would be happening here.
@@ -1139,6 +1139,7 @@ func (s *KeeperTestSuite) CreateNoLockExternalGauges(clPoolId uint64, externalGa | |||
clPoolExternalGaugeId, err := s.App.IncentivesKeeper.CreateGauge(s.Ctx, numEpochsPaidOver == 1, gaugeCreator, externalGaugeCoins, | |||
lockuptypes.QueryCondition{ | |||
LockQueryType: lockuptypes.NoLock, | |||
Duration: time.Nanosecond, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to reviewer: CL gauges with unauthorized uptimes can no longer be created due to the new validation logic, so existing tests are updated to use 1ns which is authorized by default. See #7375 for discussion on backwards compatibility with gauges currently live on mainnet with unauthorized uptimes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! LGTM so far.
I will aim to review replies and run tests more before approving later today or whenever ready
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I had some recommendations for making the execution flow stricter but not a blocker
x/incentives/keeper/gauge.go
Outdated
if isExternalConcentratedPoolGauge { | ||
durations = k.clk.GetParams(ctx).AuthorizedUptimes | ||
} else if isInternalConcentratedPoolGauge { | ||
// Internal CL gauges use epoch time as their duration. This is a legacy | ||
// property that does not affect the uptime on created records, which is | ||
// determined by the gov param for internal incentive uptimes. | ||
durations = []time.Duration{k.GetEpochInfo(ctx).Duration} | ||
} else { | ||
// This branch is applicable to CFMM pool types such as balancer and stableswap. | ||
durations = k.GetLockableDurations(ctx) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make the conditional branching stricter and eliminate the likelihood of invalid field setup causing issues.
nit: the else
branch is also not covered by a test at the moment and might benefit from it
if isExternalConcentratedPoolGauge { | |
durations = k.clk.GetParams(ctx).AuthorizedUptimes | |
} else if isInternalConcentratedPoolGauge { | |
// Internal CL gauges use epoch time as their duration. This is a legacy | |
// property that does not affect the uptime on created records, which is | |
// determined by the gov param for internal incentive uptimes. | |
durations = []time.Duration{k.GetEpochInfo(ctx).Duration} | |
} else { | |
// This branch is applicable to CFMM pool types such as balancer and stableswap. | |
durations = k.GetLockableDurations(ctx) | |
} | |
if isExternalConcentratedPoolGauge { | |
durations = k.clk.GetParams(ctx).AuthorizedUptimes | |
} else if isInternalConcentratedPoolGauge { | |
// Internal CL gauges use epoch time as their duration. This is a legacy | |
// property that does not affect the uptime on created records, which is | |
// determined by the gov param for internal incentive uptimes. | |
durations = []time.Duration{k.GetEpochInfo(ctx).Duration} | |
} else if !isConcentratedPoolGauge { | |
// This branch is applicable to CFMM pool types such as balancer and stableswap. | |
durations = k.GetLockableDurations(ctx) | |
} else { | |
return 0, fmt.Errorf("invalid gauge type: %s", distrTo.LockQueryType) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you're getting at here, but this flow is not intended to serve as a check or filter where we return an error. For example, your implementation would error if CreateGauge
is called for a Group gauge, which is not what we want here.
It does seem as though this logic is somewhat unclear though. I moved this check into the block where the duration checks happen, as that probably makes it clearer (i.e. if NoLock gauge || ByDuration gauge, run this logic
implies that we are covering everything we need to with the logic branches in the original implementation). Hopefully this makes it a bit clearer.
Please let me know if I misunderstood your suggestion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…o appropriate logic branch
@@ -28,7 +28,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { | |||
} | |||
if genState.ConcentratedPoolToNoLockGauges != nil { | |||
for _, record := range genState.ConcentratedPoolToNoLockGauges.PoolToGauge { | |||
k.SetPoolGaugeIdNoLock(ctx, record.PoolId, record.GaugeId) | |||
k.SetPoolGaugeIdNoLock(ctx, record.PoolId, record.GaugeId, record.Duration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we planning for any migration PR after this? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need to - please see #7375 (if you see any problems with the reasoning in that issue please flag them!)
@@ -507,7 +507,8 @@ func (s *KeeperTestSuite) TestCreateGauge_NoLockGauges() { | |||
distrTo: lockuptypes.QueryCondition{ | |||
LockQueryType: lockuptypes.NoLock, | |||
// Note: this assumes the gauge is external | |||
Denom: "", | |||
Denom: "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add non-failing cases for the three branches that has been added to nolock createGauge?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The non-failing case should be covered by existing tests, but will re review to make sure I'm not missing anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sg! On first site of existing tests, test cases did not seem obvious in which case touches which branch. Please feel free to merge once you have re-checked!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right – since 1 nanosecond is an authorized uptime by default, the test cases with that duration should be covering the non-failing branches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should already be addressed in #7369, but if it isn’t yet, an invalid or unauthorized gauge duration should fall back to the default uptime of 1ns in this line (instead of directly using the gauge’s duration):
I just had a question about this statement. Why should we fall back in this case? Shouldn't we be able to control this so that distribute never comes across an invalid or unauthorized duration?
#7369 has a breakdown of the reasoning. Tldr is this 1. minimizes the surface for existing gauges to unintentionally trigger chain halts/block incentives, and 2. is a clever way for us to ensure uptimes can be "unauthorized" in the future. There are probably marginal gains we can make on these fronts with a meaningfully more complex implementation, but this approach seems quite elegant (and secure) to me given the current tradeoff space. As always, feel free to push back! |
…gauges (#7376) * add uptime validation logic and fix pool gauge link * add tests and clean up implementation * add changelog and clean up prints/diff * update changelog entry to be clearer * clean up comments and add additional tests * fix broken genesis tests * extract default NoLock duration to variable and add further comments * switch naming from concentrated to NoLock and move duration setup into appropriate logic branch
Closes: #7367
What is the purpose of the change
This PR adds the relevant validation logic for authorized uptimes on CL gauge durations. Given how this logic touches existing state and a heavily depended on component of our codebase, the changes have heavy comments explaining the reasoning and implications.
This PR also makes a change to how CL gauges are linked to pool IDs in state. It is important to note that this breaks the previous setup where there was a "general linking" and then an additional link for internal gauges, reducing this to a single link based on gauge duration. The implications and next steps to ensure the safety of this are outlined in great detail in #7375.
Testing and Verifying
Tests covering the validation logic are added to
gauge_test.go
. The key changes are covered by existing tests, which were how the issue was discovered in the first place.Documentation and Release Note
Unreleased
section ofCHANGELOG.md
?Where is the change documented?
x/{module}/README.md
)