Skip to content
Open
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
2 changes: 1 addition & 1 deletion ante/feeless.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (gd FeelessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool,

// If feeless, ignore fee deduction
if isFeeless {
ctx = ctx.WithPriority(math.MaxInt64)
ctx = ctx.WithPriority(math.MaxInt64 / 1_000_000)
return next(ctx, tx, simulate)
}

Expand Down
55 changes: 49 additions & 6 deletions x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,30 @@ func (k Keeper) GetPriceSnapshotOrDefault(ctx sdk.Context, timestamp int64) (typ
return priceSnapshot, nil
}

// mergePriceSnapshotItems combines two snapshot item sets and keeps the latest
// value for each denom.
func mergePriceSnapshotItems(existing, incoming types.PriceSnapshotItems) types.PriceSnapshotItems {
merged := make(map[string]types.PriceSnapshotItem, len(existing)+len(incoming))
for _, item := range existing {
merged[item.Denom] = item
}
for _, item := range incoming {
merged[item.Denom] = item
}

denoms := make([]string, 0, len(merged))
for denom := range merged {
denoms = append(denoms, denom)
}
sort.Strings(denoms)

items := make(types.PriceSnapshotItems, 0, len(denoms))
for _, denom := range denoms {
items = append(items, merged[denom])
}
return items
}

// AddPriceSnapshot stores the snapshot on the KVStore and deletes snapshots older than the lookBackDuration
// defined on the params
func (k Keeper) AddPriceSnapshot(ctx sdk.Context, snapshot types.PriceSnapshot) error {
Expand All @@ -304,31 +328,49 @@ func (k Keeper) AddPriceSnapshot(ctx sdk.Context, snapshot types.PriceSnapshot)
return err
}
lookBackDuration := params.LookbackDuration
currentTime := ctx.BlockTime().Unix()

// Reject future-dated snapshots before mutating state.
if snapshot.SnapshotTimestamp > currentTime {
return fmt.Errorf("snapshot timestamp %d is in the future", snapshot.SnapshotTimestamp)
}

// Merge with the current snapshot if we already stored one for the same timestamp.
existingSnapshot, err := k.PriceSnapshot.Get(ctx, snapshot.SnapshotTimestamp)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
return err
}
if err == nil {
snapshot.PriceSnapshotItems = mergePriceSnapshotItems(existingSnapshot.PriceSnapshotItems, snapshot.PriceSnapshotItems)
}

// Add snapshot on the KVStore
err = k.PriceSnapshot.Set(ctx, snapshot.SnapshotTimestamp, snapshot)
if err != nil {
return err
}

// Delete the snapshot that it's timestamps is older that the LookbackDuration
// Delete snapshots that are older than the lookback duration.
var timestampsToDelete []int64

err = k.PriceSnapshot.Walk(ctx, nil, func(_ int64, snapshot types.PriceSnapshot) (bool, error) {
// If the snapshot is too old, mark it for deletion
if snapshot.SnapshotTimestamp+int64(lookBackDuration) < ctx.BlockTime().Unix() {
// If the snapshot is too old, mark it for deletion.
if snapshot.SnapshotTimestamp > currentTime {
return false, fmt.Errorf("snapshot timestamp %d is in the future", snapshot.SnapshotTimestamp)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if currentTime-snapshot.SnapshotTimestamp > int64(lookBackDuration) {
timestampsToDelete = append(timestampsToDelete, snapshot.SnapshotTimestamp)
return false, nil // Continue iteration
}

// If a valid snapshot is found, stop iterating
return true, nil
// Continue checking the remaining snapshots so future-dated entries cannot hide later in the walk.
return false, nil
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if err != nil {
return err
}

// Delete all marked old snapshots
// Delete all marked old snapshots.
for _, timeToDelete := range timestampsToDelete {
err = k.PriceSnapshot.Remove(ctx, timeToDelete)
if err != nil {
Expand Down Expand Up @@ -501,3 +543,4 @@ func (k Keeper) ValidateLookBackSeconds(ctx sdk.Context, lookBackSeconds uint64)
}
return nil
}

41 changes: 41 additions & 0 deletions x/oracle/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,3 +737,44 @@ func TestRemoveExcessFeedsWithError(t *testing.T) {
_, err = oracleKeeper.ExchangeRate.Get(ctx, utils.AtomDenom)
require.NoError(t, err)
}

func TestAddPriceSnapshotMergesDuplicateTimestamp(t *testing.T) {
init := CreateTestInput(t)
oracleKeeper := init.OracleKeeper
ctx := init.Ctx.WithBlockTime(time.Unix(3500, 0))

snapshot1 := types.NewPriceSnapshot(1000, types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.KiiDenom, types.OracleExchangeRate{ExchangeRate: math.LegacyNewDec(1)}),
})
snapshot2 := types.NewPriceSnapshot(1000, types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.EthDenom, types.OracleExchangeRate{ExchangeRate: math.LegacyNewDec(2)}),
})

require.NoError(t, oracleKeeper.AddPriceSnapshot(ctx, snapshot1))
require.NoError(t, oracleKeeper.AddPriceSnapshot(ctx, snapshot2))

stored, err := oracleKeeper.GetPriceSnapshotOrDefault(ctx, 1000)
require.NoError(t, err)
require.Len(t, stored.PriceSnapshotItems, 2)
require.Equal(t, utils.EthDenom, stored.PriceSnapshotItems[0].Denom)
require.Equal(t, utils.KiiDenom, stored.PriceSnapshotItems[1].Denom)
}

func TestAddPriceSnapshotRejectsFutureTimestamp(t *testing.T) {
init := CreateTestInput(t)
oracleKeeper := init.OracleKeeper
ctx := init.Ctx.WithBlockTime(time.Unix(1000, 0))

snapshot := types.NewPriceSnapshot(1001, types.PriceSnapshotItems{
types.NewPriceSnapshotItem(utils.KiiDenom, types.OracleExchangeRate{ExchangeRate: math.LegacyNewDec(1)}),
})

err := oracleKeeper.AddPriceSnapshot(ctx, snapshot)
require.Error(t, err)
require.Contains(t, err.Error(), "future")

stored, err := oracleKeeper.GetPriceSnapshotOrDefault(ctx, 1001)
require.NoError(t, err)
require.Empty(t, stored.PriceSnapshotItems)
}