Skip to content

Commit

Permalink
handle oracle overflow and rounding to zero for extreme values with c…
Browse files Browse the repository at this point in the history
…ross rates (#1983)

* handle oracle overflow and rounding to zero for extreme values with cross rates

* remove empty branch
  • Loading branch information
udpatil authored Dec 10, 2024
1 parent 3d077cd commit 86d0381
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
6 changes: 6 additions & 0 deletions x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ func MidBlocker(ctx sdk.Context, k keeper.Keeper) {
// Get weighted median of cross exchange rates
exchangeRate := Tally(ctx, ballot, params.RewardBand, validatorClaimMap)

// if exchange rate is somehow 0, exclude it from ballot?
if exchangeRate.IsZero() {
// skip this denom
continue
}

// Transform into the original form base/quote
if denom != referenceDenom {
exchangeRate = exchangeRateRD.Quo(exchangeRate)
Expand Down
60 changes: 60 additions & 0 deletions x/oracle/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,3 +672,63 @@ func TestEndWindowClearExcessFeeds(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 1, len(response2.Actives))
}

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

// Set vote targets
input.OracleKeeper.ClearVoteTargets(input.Ctx)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroAtomDenom)
input.OracleKeeper.SetVoteTarget(input.Ctx, utils.MicroEthDenom)

// Test overflow case
overflowRate := sdk.MustNewDecFromStr("7896044618658097711785492504343953926634992332820282019728792003956564819967.999999999999999999")
smallRate := sdk.MustNewDecFromStr("0.000000000000000001")
overflowVote := sdk.DecCoins{
sdk.NewDecCoinFromDec(utils.MicroAtomDenom, overflowRate),
sdk.NewDecCoinFromDec(utils.MicroEthDenom, smallRate),
}
makeAggregateVote(t, input, h, 0, overflowVote, 0)
makeAggregateVote(t, input, h, 0, overflowVote, 1)
makeAggregateVote(t, input, h, 0, overflowVote, 2)

// This should not panic
oracle.MidBlocker(input.Ctx, input.OracleKeeper)
oracle.EndBlocker(input.Ctx, input.OracleKeeper)

// Verify no exchange rates were set for overflowed one
rate, _, _, err := input.OracleKeeper.GetBaseExchangeRate(input.Ctx, utils.MicroAtomDenom)
require.NoError(t, err)
require.Equal(t, overflowRate, rate)
_, _, _, err = input.OracleKeeper.GetBaseExchangeRate(input.Ctx, utils.MicroEthDenom)
require.Error(t, err)

input.Ctx = input.Ctx.WithBlockHeight(1)

// Test divide by zero case
zeroVote := sdk.DecCoins{
sdk.NewDecCoinFromDec(utils.MicroAtomDenom, smallRate),
sdk.NewDecCoinFromDec(utils.MicroEthDenom, overflowRate),
}
makeAggregateVote(t, input, h, 1, zeroVote, 0)
makeAggregateVote(t, input, h, 1, zeroVote, 1)
makeAggregateVote(t, input, h, 1, zeroVote, 2)

// This should not panic
oracle.MidBlocker(input.Ctx, input.OracleKeeper)
oracle.EndBlocker(input.Ctx, input.OracleKeeper)

// Verify no exchange rates were set for either case
rate, height, _, err := input.OracleKeeper.GetBaseExchangeRate(input.Ctx, utils.MicroAtomDenom)
require.NoError(t, err)
require.Equal(t, smallRate, rate)
require.Equal(t, int64(1), height.Int64())
_, _, _, err = input.OracleKeeper.GetBaseExchangeRate(input.Ctx, utils.MicroEthDenom)
require.Error(t, err)
}
12 changes: 11 additions & 1 deletion x/oracle/types/ballot.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,17 @@ func (pb ExchangeRateBallot) ToCrossRate(bases map[string]sdk.Dec) (cb ExchangeR
vote := pb[i]

if exchangeRateRT, ok := bases[string(vote.Voter)]; ok && vote.ExchangeRate.IsPositive() {
vote.ExchangeRate = exchangeRateRT.Quo(vote.ExchangeRate)
// Quo will panic on overflow, so we wrap it in a defer/recover
func() {
defer func() {
if r := recover(); r != nil {
// if overflow, set exchange rate to 0 and power to 0
vote.ExchangeRate = sdk.ZeroDec()
vote.Power = 0
}
}()
vote.ExchangeRate = exchangeRateRT.Quo(vote.ExchangeRate)
}()
} else {
// If we can't get reference Sei exchange rate, we just convert the vote as abstain vote
vote.ExchangeRate = sdk.ZeroDec()
Expand Down

0 comments on commit 86d0381

Please sign in to comment.