mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 15:55:18 +00:00
673790465d
* optimize pricefeed endblocker to iterate all markets only once to remove overhead of opening and closing iterator for each market individually. In addition, extend tests to cover 100% of abci and price updating behavior. * use test cases that can't be confused with mean to ensure median is always used
237 lines
9.2 KiB
Go
237 lines
9.2 KiB
Go
package testutil
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
tmprototypes "github.com/cometbft/cometbft/proto/tendermint/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/kava-labs/kava/app"
|
|
"github.com/kava-labs/kava/x/pricefeed/keeper"
|
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
|
)
|
|
|
|
func SetCurrentPrices_PriceCalculations(t *testing.T, f func(ctx sdk.Context, keeper keeper.Keeper)) {
|
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
|
tApp := app.NewTestApp()
|
|
ctx := tApp.NewContext(true, tmprototypes.Header{}).
|
|
WithBlockTime(time.Now().UTC())
|
|
keeper := tApp.GetPriceFeedKeeper()
|
|
|
|
params := types.Params{
|
|
Markets: []types.Market{
|
|
// valid previous price, expired prices, price change, and active
|
|
{MarketID: "asset1:usd", BaseAsset: "asset1", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
|
// same data as asset1, but not active and should be ignored
|
|
{MarketID: "asset2:usd", BaseAsset: "asset2", QuoteAsset: "usd", Oracles: addrs, Active: false},
|
|
// same data as asset1 except no valid previous price
|
|
{MarketID: "asset3:usd", BaseAsset: "asset3", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
|
// previous price set, but no valid prices
|
|
{MarketID: "asset4:usd", BaseAsset: "asset4", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
|
// same as market one except different prices
|
|
{MarketID: "asset5:usd", BaseAsset: "asset5", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
|
},
|
|
}
|
|
keeper.SetParams(ctx, params)
|
|
|
|
// need price equal to block time and after block time
|
|
blockTime := time.Now()
|
|
initialPriceExpiry := blockTime.Add(1 * time.Hour)
|
|
|
|
_, err := keeper.SetPrice(ctx, addrs[0], "asset1:usd", sdk.MustNewDecFromStr("1"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[0], "asset2:usd", sdk.MustNewDecFromStr("1"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[0], "asset4:usd", sdk.MustNewDecFromStr("1"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[0], "asset5:usd", sdk.MustNewDecFromStr("10"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
|
|
ctx = ctx.WithBlockTime(blockTime)
|
|
f(ctx, keeper)
|
|
|
|
// price should be set
|
|
price, err := keeper.GetCurrentPrice(ctx, "asset1:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.OneDec(), price.Price)
|
|
// not an active market, so price is not set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset2:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// no price posted
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset3:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// price set initially
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset4:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.OneDec(), price.Price)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset5:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("10.0"), price.Price)
|
|
|
|
_, err = keeper.SetPrice(ctx, addrs[1], "asset1:usd", sdk.MustNewDecFromStr("2"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[1], "asset2:usd", sdk.MustNewDecFromStr("2"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[1], "asset5:usd", sdk.MustNewDecFromStr("20"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
|
|
blockTime = blockTime.Add(30 * time.Minute)
|
|
ctx = ctx.WithBlockTime(blockTime)
|
|
f(ctx, keeper)
|
|
|
|
// price should be set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset1:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("1.5"), price.Price)
|
|
// not an active market, so price is not set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset2:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// no price posted
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset3:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// price set initially
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset4:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.OneDec(), price.Price)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset5:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("15.0"), price.Price)
|
|
|
|
_, err = keeper.SetPrice(ctx, addrs[2], "asset1:usd", sdk.MustNewDecFromStr("30"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[2], "asset2:usd", sdk.MustNewDecFromStr("30"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[2], "asset5:usd", sdk.MustNewDecFromStr("30"), initialPriceExpiry.Add(1*time.Hour))
|
|
require.NoError(t, err)
|
|
|
|
blockTime = blockTime.Add(15 * time.Minute)
|
|
ctx = ctx.WithBlockTime(blockTime)
|
|
f(ctx, keeper)
|
|
|
|
// price should be set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset1:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("2.0"), price.Price)
|
|
// not an active market, so price is not set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset2:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// no price posted
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset3:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// price set initially
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset4:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.OneDec(), price.Price)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset5:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("20.0"), price.Price)
|
|
|
|
blockTime = blockTime.Add(15 * time.Minute)
|
|
ctx = ctx.WithBlockTime(blockTime)
|
|
f(ctx, keeper)
|
|
|
|
// price should be set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset1:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("16"), price.Price)
|
|
// not an active market, so price is not set
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset2:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// no price posted
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset3:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
// price set initially, now expired
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset4:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset5:usd")
|
|
require.NoError(t, err)
|
|
require.Equal(t, sdk.MustNewDecFromStr("25.0"), price.Price)
|
|
|
|
blockTime = blockTime.Add(10 * time.Hour)
|
|
ctx = ctx.WithBlockTime(blockTime)
|
|
f(ctx, keeper)
|
|
|
|
// all prices expired now
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset1:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset2:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset3:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset4:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
price, err = keeper.GetCurrentPrice(ctx, "asset5:usd")
|
|
require.Equal(t, types.ErrNoValidPrice, err)
|
|
}
|
|
|
|
func SetCurrentPrices_EventEmission(t *testing.T, f func(ctx sdk.Context, keeper keeper.Keeper)) {
|
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
|
tApp := app.NewTestApp()
|
|
ctx := tApp.NewContext(true, tmprototypes.Header{}).
|
|
WithBlockTime(time.Now().UTC())
|
|
keeper := tApp.GetPriceFeedKeeper()
|
|
|
|
params := types.Params{
|
|
Markets: []types.Market{
|
|
{MarketID: "asset1:usd", BaseAsset: "asset1", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
|
},
|
|
}
|
|
keeper.SetParams(ctx, params)
|
|
|
|
blockTime := time.Now()
|
|
initialPriceExpiry := blockTime.Add(1 * time.Hour)
|
|
|
|
// post a price
|
|
_, err := keeper.SetPrice(ctx, addrs[0], "asset1:usd", sdk.MustNewDecFromStr("1"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
|
|
// reset context with fresh event manager
|
|
ctx = ctx.WithBlockTime(blockTime).WithEventManager(sdk.NewEventManager())
|
|
f(ctx, keeper)
|
|
|
|
// no previous price so no event
|
|
require.Equal(t, 0, len(ctx.EventManager().Events()))
|
|
|
|
// post same price from another oracle
|
|
_, err = keeper.SetPrice(ctx, addrs[1], "asset1:usd", sdk.MustNewDecFromStr("1"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
|
|
blockTime = blockTime.Add(10 * time.Second)
|
|
ctx = ctx.WithBlockTime(blockTime).WithEventManager(sdk.NewEventManager())
|
|
f(ctx, keeper)
|
|
|
|
// no price change so no event
|
|
require.Equal(t, 0, len(ctx.EventManager().Events()))
|
|
|
|
// post price changes
|
|
_, err = keeper.SetPrice(ctx, addrs[2], "asset1:usd", sdk.MustNewDecFromStr("2"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[3], "asset1:usd", sdk.MustNewDecFromStr("10"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
_, err = keeper.SetPrice(ctx, addrs[4], "asset1:usd", sdk.MustNewDecFromStr("10"), initialPriceExpiry)
|
|
require.NoError(t, err)
|
|
|
|
blockTime = blockTime.Add(10 * time.Second)
|
|
ctx = ctx.WithBlockTime(blockTime).WithEventManager(sdk.NewEventManager())
|
|
f(ctx, keeper)
|
|
|
|
// price is changes so event should be emitted
|
|
require.Equal(t, 1, len(ctx.EventManager().Events()))
|
|
|
|
event := ctx.EventManager().Events()[0]
|
|
|
|
// has correct event type
|
|
assert.Equal(t, types.EventTypeMarketPriceUpdated, event.Type)
|
|
// has correct attributes
|
|
marketID, found := event.GetAttribute(types.AttributeMarketID)
|
|
require.True(t, found)
|
|
marketPrice, found := event.GetAttribute(types.AttributeMarketPrice)
|
|
require.True(t, found)
|
|
// attributes have correct values
|
|
assert.Equal(t, "asset1:usd", marketID.Value)
|
|
assert.Equal(t, sdk.MustNewDecFromStr("2").String(), marketPrice.Value)
|
|
}
|