Skip to content

Commit

Permalink
Meter allowance lockstep (#553)
Browse files Browse the repository at this point in the history
changes
  • Loading branch information
shaspitz authored Dec 3, 2022
1 parent 9904325 commit c238f4b
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 3 deletions.
16 changes: 13 additions & 3 deletions x/ccv/provider/keeper/throttle.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,27 @@ func (k Keeper) InitializeSlashMeter(ctx sdktypes.Context) {
}

// CheckForSlashMeterReplenishment checks if the slash meter should be replenished, and if so, replenishes it.
// Note: initial slash meter replenish time is set in InitGenesis
// Note: initial "last slash meter full time" is set in InitGenesis.
func (k Keeper) CheckForSlashMeterReplenishment(ctx sdktypes.Context) {

lastFullTime := k.GetLastSlashMeterFullTime(ctx)
replenishPeriod := k.GetSlashMeterReplenishPeriod(ctx)

// Replenish slash meter if enough time has passed since the last time it was full.
if ctx.BlockTime().UTC().After(lastFullTime.Add(replenishPeriod)) {
k.ReplenishSlashMeter(ctx)
}
// If slash meter is full, update most recent time the slash meter was full to current block time.
if k.GetSlashMeter(ctx).Equal(k.GetSlashMeterAllowance(ctx)) {

// If slash meter is full, or more than full considering updated allowance/total power,
allowance := k.GetSlashMeterAllowance(ctx)
if k.GetSlashMeter(ctx).GTE(allowance) {

// set the most recent time the slash meter was full to current block time.
k.SetLastSlashMeterFullTime(ctx, ctx.BlockTime())

// Ensure the slash meter is not greater than allowance,
// considering current total voting power.
k.SetSlashMeter(ctx, allowance)
}
}

Expand Down
90 changes: 90 additions & 0 deletions x/ccv/provider/keeper/throttle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,96 @@ func TestSlashMeterReplenishment(t *testing.T) {
}
}

// TestSlashMeterAllowanceChanges tests the behavior of a full slash meter when the
// allowance is changed from total voting power changing.
func TestSlashMeterAllowanceChanges(t *testing.T) {

providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
defer ctrl.Finish()

now := time.Now()
ctx = ctx.WithBlockTime(now)

params := providertypes.DefaultParams()
params.SlashMeterReplenishFraction = "0.1"
params.SlashMeterReplenishPeriod = time.Hour
providerKeeper.SetParams(ctx, params)

// Mock total power to be 1000
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetLastTotalPower(
// Expect two calls, once for initialization, once for allowance check
ctx).Return(sdktypes.NewInt(1000)).Times(2),
)

// Initialize the slash meter (this would happen in InitGenesis)
providerKeeper.InitializeSlashMeter(ctx)

// Confirm slash meter is full, and allowance is expected value via params
require.Equal(t, sdktypes.NewInt(100), providerKeeper.GetSlashMeterAllowance(ctx))
require.Equal(t, sdktypes.NewInt(100), providerKeeper.GetSlashMeter(ctx))

// Mutate context so mocked total power is less than before
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Microsecond)) // Don't add enough time for replenishment
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetLastTotalPower(
// Expect two calls, once for replenish check, once for allowance check
ctx).Return(sdktypes.NewInt(500)).Times(2),
)

// Replenishment should not happen here, but slash meter should be decremented to new allowance
providerKeeper.CheckForSlashMeterReplenishment(ctx)
require.Equal(t, sdktypes.NewInt(50), providerKeeper.GetSlashMeterAllowance(ctx))
require.Equal(t, sdktypes.NewInt(50), providerKeeper.GetSlashMeter(ctx))

// Mutate context so mocked total power is again less than before,
// with ctx time set to a time that will replenish meter
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(5 * time.Hour))
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetLastTotalPower(
// Expect three calls, once for replenish check,
// once for replenishment, once for allowance check
ctx).Return(sdktypes.NewInt(100)).Times(3),
)

// Replenishment should happen here, slash meter should be decremented to new allowance regardless
providerKeeper.CheckForSlashMeterReplenishment(ctx)
require.Equal(t, sdktypes.NewInt(10), providerKeeper.GetSlashMeterAllowance(ctx))
require.Equal(t, sdktypes.NewInt(10), providerKeeper.GetSlashMeter(ctx))

// Mutate context so mocked total power is now more than before
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Microsecond)) // Don't add enough time for replenishment
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetLastTotalPower(
// Expect two calls, once for replenish check, once for allowance check
ctx).Return(sdktypes.NewInt(5000)).Times(2),
)

//
// Important: Without a replenishment, the meter should remain at its previous value
//

// Replenishment should not happen here, slash meter should remain at previous value
providerKeeper.CheckForSlashMeterReplenishment(ctx)
require.Equal(t, sdktypes.NewInt(500), providerKeeper.GetSlashMeterAllowance(ctx))
require.Equal(t, sdktypes.NewInt(10), providerKeeper.GetSlashMeter(ctx))

// Mutate context so mocked total power is again more than before,
// with ctx time set to a time that will replenish meter
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(5 * time.Hour))
gomock.InOrder(
mocks.MockStakingKeeper.EXPECT().GetLastTotalPower(
// Expect three calls, once for replenish check,
// once for replenishment, once for allowance check
ctx).Return(sdktypes.NewInt(10000)).Times(3),
)

// Replenishment should happen here, slash meter should be set to new allowance
providerKeeper.CheckForSlashMeterReplenishment(ctx)
require.Equal(t, sdktypes.NewInt(1000), providerKeeper.GetSlashMeterAllowance(ctx))
require.Equal(t, sdktypes.NewInt(1000), providerKeeper.GetSlashMeter(ctx))
}

// TestNegativeSlashMeter tests behavior of the slash meter when it goes negative,
// and also the fact that the replenishment allowance becomes lower as total
// voting power becomes lower from slashing.
Expand Down

0 comments on commit c238f4b

Please sign in to comment.