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

Cleanup removed oracle feeds #1204

Merged
merged 6 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions wasmbinding/test/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ func TestWasmGetOracleTwaps(t *testing.T) {
oracletypes.NewPriceSnapshotItem(oracleutils.MicroAtomDenom, oracletypes.OracleExchangeRate{ExchangeRate: sdk.NewDec(20), LastUpdate: sdk.NewInt(10)}),
}}
testWrapper.App.OracleKeeper.AddPriceSnapshot(testWrapper.Ctx, priceSnapshot)
testWrapper.App.OracleKeeper.SetVoteTarget(testWrapper.Ctx, oracleutils.MicroAtomDenom)

testWrapper.Ctx = testWrapper.Ctx.WithBlockHeight(14).WithBlockTime(time.Unix(3700, 0))

Expand Down
2 changes: 2 additions & 0 deletions x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,7 @@
// reset miss counters of all validators at the last block of slash window
if utils.IsPeriodLastBlock(ctx, params.SlashWindow) {
k.SlashAndResetCounters(ctx)
// Compare vote targets and actives and remove excess feeds
k.RemoveExcessFeeds(ctx)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods Warning

path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
}
}
33 changes: 33 additions & 0 deletions x/oracle/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,36 @@ func makeAggregateVote(t *testing.T, input keeper.TestInput, h sdk.Handler, heig
_, err := h(input.Ctx.WithBlockHeight(height), voteMsg)
require.NoError(t, err)
}

func TestEndWindowClearExcessFeeds(t *testing.T) {
input, _ := setup(t)
params := input.OracleKeeper.GetParams(input.Ctx)
params.Whitelist = types.DenomList{{Name: utils.MicroAtomDenom}}
input.OracleKeeper.SetParams(input.Ctx, params)

input.OracleKeeper.SetBaseExchangeRate(input.Ctx, utils.MicroAtomDenom, randomExchangeRate)
input.OracleKeeper.SetBaseExchangeRate(input.Ctx, utils.MicroEthDenom, randomExchangeRate)

input.OracleKeeper.ClearVoteTargets(input.Ctx)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroAtomDenom)

earlyCtx := sdk.WrapSDKContext(input.Ctx)
earlyQuerier := keeper.NewQuerier(input.OracleKeeper)

response, err := earlyQuerier.Actives(earlyCtx, &types.QueryActivesRequest{})
require.NoError(t, err)
require.Equal(t, 2, len(response.Actives))

votePeriodsPerWindow := sdk.NewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64()

input.Ctx = input.Ctx.WithBlockHeight(votePeriodsPerWindow - 1)
oracle.MidBlocker(input.Ctx, input.OracleKeeper)
oracle.EndBlocker(input.Ctx, input.OracleKeeper)

ctx := sdk.WrapSDKContext(input.Ctx)
querier := keeper.NewQuerier(input.OracleKeeper)

response2, err := querier.Actives(ctx, &types.QueryActivesRequest{})
require.NoError(t, err)
require.Equal(t, 1, len(response2.Actives))
}
37 changes: 37 additions & 0 deletions x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,33 @@
}
}

func (k Keeper) RemoveExcessFeeds(ctx sdk.Context) {
// get actives
excessActives := make(map[string]struct{})
k.IterateBaseExchangeRates(ctx, func(denom string, rate types.OracleExchangeRate) (stop bool) {
excessActives[denom] = struct{}{}
return false
})
// get vote targets
k.IterateVoteTargets(ctx, func(denom string, denomInfo types.Denom) (stop bool) {
// remove vote targets from actives
delete(excessActives, denom)
return false
})
// compare
activesToClear := make([]string, len(excessActives))
i := 0
for denom := range excessActives {
activesToClear[i] = denom
i++
}
Comment on lines +137 to +140

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
sort.Strings(activesToClear)
for _, denom := range activesToClear {
// clear exchange rates
k.DeleteBaseExchangeRate(ctx, denom)
}
}

//-----------------------------------
// Oracle delegation logic

Expand Down Expand Up @@ -452,6 +479,13 @@
denomToTimeWeightedMap := make(map[string]sdk.Dec)
denomDurationMap := make(map[string]int64)

// get targets - only calculate for the targets
targetsMap := make(map[string]struct{})
k.IterateVoteTargets(ctx, func(denom string, denomInfo types.Denom) (stop bool) {
targetsMap[denom] = struct{}{}
return false
})

k.IteratePriceSnapshotsReverse(ctx, func(snapshot types.PriceSnapshot) (stop bool) {
stop = false
snapshotTimestamp := snapshot.SnapshotTimestamp
Expand All @@ -468,6 +502,9 @@
snapshotPriceItems := snapshot.PriceSnapshotItems
for _, priceItem := range snapshotPriceItems {
denom := priceItem.Denom
if _, ok := targetsMap[denom]; !ok {
continue
}

_, exists := denomToTimeWeightedMap[denom]
if !exists {
Expand Down
132 changes: 132 additions & 0 deletions x/oracle/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ func TestExchangeRate(t *testing.T) {
input.OracleKeeper.IterateBaseExchangeRates(input.Ctx, handler)

require.Equal(t, 2, numExchangeRates)

// eth removed
input.OracleKeeper.ClearVoteTargets(input.Ctx)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroSeiDenom)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroAtomDenom)
// should remove eth
input.OracleKeeper.RemoveExcessFeeds(input.Ctx)

numExchangeRates = 0
input.OracleKeeper.IterateBaseExchangeRates(input.Ctx, handler)
require.Equal(t, 1, numExchangeRates)

}

func TestIterateSeiExchangeRates(t *testing.T) {
Expand Down Expand Up @@ -681,3 +693,123 @@ func TestCalculateTwaps(t *testing.T) {
require.Error(t, err)
require.Equal(t, types.ErrInvalidTwapLookback, err)
}

func TestCalculateTwapsWithUnsupportedDenom(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ yay test

input := CreateTestInput(t)

_, err := input.OracleKeeper.CalculateTwaps(input.Ctx, 3600)
require.Error(t, err)
require.Equal(t, types.ErrNoTwapData, err)

priceSnapshots := types.PriceSnapshots{
types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroAtomDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(40),
LastUpdate: sdk.NewInt(1800),
}),
}, 1200),
types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroEthDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(10),
LastUpdate: sdk.NewInt(3600),
}),
types.NewPriceSnapshotItem(utils.MicroAtomDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(20),
LastUpdate: sdk.NewInt(3600),
}),
}, 3600),
types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroEthDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(20),
LastUpdate: sdk.NewInt(4500),
}),
types.NewPriceSnapshotItem(utils.MicroAtomDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(40),
LastUpdate: sdk.NewInt(4500),
}),
}, 4500),
}
for _, snap := range priceSnapshots {
input.OracleKeeper.SetPriceSnapshot(input.Ctx, snap)
}
// eth removed
input.OracleKeeper.ClearVoteTargets(input.Ctx)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroAtomDenom)

input.Ctx = input.Ctx.WithBlockTime(time.Unix(5400, 0))
twaps, err := input.OracleKeeper.CalculateTwaps(input.Ctx, 3600)
require.NoError(t, err)
require.Equal(t, 1, len(twaps))
atomTwap := twaps[0]
require.Equal(t, utils.MicroAtomDenom, atomTwap.Denom)
require.Equal(t, int64(3600), atomTwap.LookbackSeconds)
require.Equal(t, sdk.NewDec(35), atomTwap.Twap)

input.Ctx = input.Ctx.WithBlockTime(time.Unix(6000, 0))

// we still expect the out of range data point from 1200 to be kept for calculating TWAP for the full interval
input.OracleKeeper.AddPriceSnapshot(input.Ctx, types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroAtomDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(30),
LastUpdate: sdk.NewInt(6000),
}),
}, 6000))

input.Ctx = input.Ctx.WithBlockTime(time.Unix(6600, 0))

twaps, err = input.OracleKeeper.CalculateTwaps(input.Ctx, 3600)
require.NoError(t, err)
require.Equal(t, 1, len(twaps))
atomTwap = twaps[0]
require.Equal(t, utils.MicroAtomDenom, atomTwap.Denom)
require.Equal(t, int64(3600), atomTwap.LookbackSeconds)

expectedTwap, _ := sdk.NewDecFromStr("33.333333333333333333")
require.Equal(t, expectedTwap, atomTwap.Twap)

// test with shorter lookback
// microeth is not in this one because it's not in the snapshot used for TWAP calculation
twaps, err = input.OracleKeeper.CalculateTwaps(input.Ctx, 300)
require.NoError(t, err)
require.Equal(t, 1, len(twaps))
atomTwap = twaps[0]
require.Equal(t, utils.MicroAtomDenom, atomTwap.Denom)
require.Equal(t, int64(300), atomTwap.LookbackSeconds)
require.Equal(t, sdk.NewDec(30), atomTwap.Twap)

input.OracleKeeper.AddPriceSnapshot(input.Ctx, types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroAtomDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(20),
LastUpdate: sdk.NewInt(6000),
}),
}, 6600))

input.OracleKeeper.AddPriceSnapshot(input.Ctx, types.NewPriceSnapshot(types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.MicroEthDenom, types.OracleExchangeRate{
ExchangeRate: sdk.NewDec(20),
LastUpdate: sdk.NewInt(6900),
}),
}, 6900))

input.Ctx = input.Ctx.WithBlockTime(time.Unix(6900, 0))

// the older interval weight should be appropriately shorted to the start of the lookback interval,
// so the TWAP should be 25 instead of 26.666 because the 20 and 30 are weighted 50-50 instead of 33.3-66.6
twaps, err = input.OracleKeeper.CalculateTwaps(input.Ctx, 600)
require.NoError(t, err)
require.Equal(t, 1, len(twaps))
atomTwap = twaps[0]
require.Equal(t, utils.MicroAtomDenom, atomTwap.Denom)
require.Equal(t, int64(600), atomTwap.LookbackSeconds)
require.Equal(t, sdk.NewDec(25), atomTwap.Twap)

// test error when lookback too large
_, err = input.OracleKeeper.CalculateTwaps(input.Ctx, 3700)
require.Error(t, err)
require.Equal(t, types.ErrInvalidTwapLookback, err)

// test error when lookback is 0
_, err = input.OracleKeeper.CalculateTwaps(input.Ctx, 0)
require.Error(t, err)
require.Equal(t, types.ErrInvalidTwapLookback, err)
}
Loading