Skip to content

Commit fef79eb

Browse files
committed
feat(x/oracle): add timestamp filtering for stale data
Signed-off-by: Artur Troian <troian@users.noreply.github.com>
1 parent f964af6 commit fef79eb

File tree

4 files changed

+37
-51
lines changed

4 files changed

+37
-51
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ require (
4747
google.golang.org/grpc v1.76.0
4848
gopkg.in/yaml.v3 v3.0.1
4949
gotest.tools/v3 v3.5.2
50-
pkg.akt.dev/go v0.2.0-b6
50+
pkg.akt.dev/go v0.2.0-b7
5151
pkg.akt.dev/go/cli v0.2.0-b5
5252
pkg.akt.dev/go/sdl v0.2.0-b0
5353
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,8 +3290,8 @@ nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y=
32903290
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
32913291
pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
32923292
pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
3293-
pkg.akt.dev/go v0.2.0-b6 h1:vWfPdKmMduzqEM9udDB+9jtdlHLlbuV6bzbmVyHuTZo=
3294-
pkg.akt.dev/go v0.2.0-b6/go.mod h1:8cUJoOmylVh0+UGtr0uY5bOvfIZiir1X6iDelXqxv9M=
3293+
pkg.akt.dev/go v0.2.0-b7 h1:QM1vWXUIikPHoUH4a4NWEZ1lB+ZDZOokV9/bo3r+Lu4=
3294+
pkg.akt.dev/go v0.2.0-b7/go.mod h1:8cUJoOmylVh0+UGtr0uY5bOvfIZiir1X6iDelXqxv9M=
32953295
pkg.akt.dev/go/cli v0.2.0-b5 h1:Ds+Y04gprqjzkQEp4SBc9hWUp5iynxEtNnCTHC2BeAA=
32963296
pkg.akt.dev/go/cli v0.2.0-b5/go.mod h1:L+jzPE95i0YQvuEgSDi5WlVeaSe5FpocofH+k4akNio=
32973297
pkg.akt.dev/go/sdl v0.2.0-b0 h1:IOx8FwA57r08fiuTmWNmuTVpQhzb7vGPuCPhMtxi0fo=

testutil/oracle/price_feeder.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
sdk "github.com/cosmos/cosmos-sdk/types"
88

99
oraclev1 "pkg.akt.dev/go/node/oracle/v1"
10-
sdkutil "pkg.akt.dev/go/sdkutil"
10+
"pkg.akt.dev/go/sdkutil"
1111

1212
oraclekeeper "pkg.akt.dev/node/v2/x/oracle/keeper"
1313
)
@@ -99,12 +99,13 @@ func (pf *PriceFeeder) FeedPrice(ctx sdk.Context, denom string) error {
9999
}
100100

101101
priceHealth := oraclev1.PriceHealth{
102-
Denom: denom,
103-
IsHealthy: true,
104-
HasMinSources: true,
105-
AllSourcesFresh: true,
106-
DeviationOk: true,
107-
FailureReason: []string{},
102+
Denom: denom,
103+
IsHealthy: true,
104+
HasMinSources: true,
105+
TotalSources: 1,
106+
TotalHealthySources: 1,
107+
DeviationOk: true,
108+
FailureReason: []string{},
108109
}
109110

110111
if err := pf.keeper.SetAggregatedPrice(ctx, dataID, aggregatedPrice); err != nil {

x/oracle/keeper/keeper.go

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"slices"
78
"sort"
89
"time"
910

@@ -322,10 +323,16 @@ func (k *keeper) EndBlocker(ctx context.Context) error {
322323
panic(fmt.Sprintf("failed to walk latest prices: %v", err))
323324
}
324325

326+
cutoffHeight := sctx.BlockHeight() - params.MaxPriceStalenessBlocks
327+
325328
for id, rid := range rIDs {
326329
latestData := make([]types.PriceData, 0, len(rid))
327330

328331
for _, id := range rid {
332+
if id.Height < cutoffHeight {
333+
continue
334+
}
335+
329336
state, _ := k.prices.Get(sctx, id)
330337

331338
latestData = append(latestData, types.PriceData{
@@ -496,18 +503,28 @@ func (k *keeper) calculateAggregatedPrices(ctx sdk.Context, id types.DataID, lat
496503
Denom: id.Denom,
497504
}
498505

506+
params, err := k.GetParams(ctx)
507+
if err != nil {
508+
return aggregated, err
509+
}
510+
511+
// filter out stale sources by time
512+
// todo block time is a variable, it should not be hardcoded
513+
cutoffTimestamp := ctx.BlockTime().Add(-time.Duration(params.MaxPriceStalenessBlocks) * (time.Second * 6))
514+
515+
for i := len(latestData) - 1; i >= 0; i-- {
516+
if latestData[i].State.Timestamp.Before(cutoffTimestamp) {
517+
latestData = slices.Delete(latestData, i, i+1)
518+
}
519+
}
520+
499521
if len(latestData) == 0 {
500522
return aggregated, errorsmod.Wrap(
501523
types.ErrPriceStalled,
502524
"all price sources are stale",
503525
)
504526
}
505527

506-
params, err := k.GetParams(ctx)
507-
if err != nil {
508-
return aggregated, err
509-
}
510-
511528
// Calculate TWAP for each source
512529
var twaps []sdkmath.LegacyDec //nolint:prealloc
513530
for _, source := range latestData {
@@ -624,7 +641,9 @@ func (k *keeper) getAggregatedPrice(ctx sdk.Context, denom string) (types.Aggreg
624641
// CheckPriceHealth checks if the aggregated price meets health requirements
625642
func (k *keeper) setPriceHealth(ctx sdk.Context, params types.Params, dataIDs []types.PriceDataRecordID, aggregatedPrice types.AggregatedPrice) types.PriceHealth {
626643
health := types.PriceHealth{
627-
Denom: aggregatedPrice.Denom,
644+
Denom: aggregatedPrice.Denom,
645+
TotalSources: uint32(len(dataIDs)),
646+
TotalHealthySources: aggregatedPrice.NumSources,
628647
}
629648

630649
// Check 1: Minimum number of sources
@@ -647,41 +666,7 @@ func (k *keeper) setPriceHealth(ctx sdk.Context, params types.Params, dataIDs []
647666
))
648667
}
649668

650-
// Check 3: All sources are fresh
651-
allFresh := true
652-
foundSource := false
653-
cutoffHeight := ctx.BlockHeight() - params.MaxPriceStalenessBlocks
654-
655-
for _, did := range dataIDs {
656-
foundSource = true
657-
allFresh = allFresh && did.Height >= cutoffHeight
658-
//return !allFresh, nil
659-
}
660-
661-
//err := k.latestPrices.Walk(ctx, nil, func(key types.PriceDataID, value int64) (bool, error) {
662-
// if key.Denom != aggregatedPrice.Denom || key.BaseDenom != sdkutil.DenomUSD {
663-
// return false, nil
664-
// }
665-
//
666-
// foundSource = true
667-
// allFresh = allFresh && value >= cutoffHeight
668-
// return !allFresh, nil
669-
//})
670-
671-
//if err != nil {
672-
// allFresh = false
673-
//}
674-
675-
if !foundSource {
676-
allFresh = false
677-
}
678-
679-
if !allFresh {
680-
health.FailureReason = append(health.FailureReason, "one or more price sources are stale")
681-
}
682-
683-
health.AllSourcesFresh = allFresh
684-
health.IsHealthy = health.HasMinSources && health.DeviationOk && health.AllSourcesFresh
669+
health.IsHealthy = health.HasMinSources && health.DeviationOk
685670

686671
err := k.pricesHealth.Set(ctx, types.DataID{Denom: health.Denom, BaseDenom: sdkutil.DenomUSD}, health)
687672
// if there is an error when storing price health, something went horribly wrong

0 commit comments

Comments
 (0)