pricefeed cleanup #428

[R4R] Pricefeed cleanup
This commit is contained in:
Federico Kunze 2020-04-17 23:31:55 -04:00 committed by GitHub
commit a0c2e4773b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 134 additions and 78 deletions

View File

@ -1,8 +1,8 @@
// nolint // nolint
// autogenerated code using github.com/rigelrozanski/multitool // autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories: // aliases generated for the following subdirectories:
// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/types/ // ALIASGEN: github.com/kava-labs/kava/x/pricefeed/keeper
// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/keeper/ // ALIASGEN: github.com/kava-labs/kava/x/pricefeed/types
package pricefeed package pricefeed
import ( import (
@ -31,48 +31,54 @@ const (
RouterKey = types.RouterKey RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute QuerierRoute = types.QuerierRoute
DefaultParamspace = types.DefaultParamspace DefaultParamspace = types.DefaultParamspace
RawPriceFeedPrefix = types.RawPriceFeedPrefix
CurrentPricePrefix = types.CurrentPricePrefix
MarketPrefix = types.MarketPrefix
OraclePrefix = types.OraclePrefix
TypeMsgPostPrice = types.TypeMsgPostPrice TypeMsgPostPrice = types.TypeMsgPostPrice
QueryPrice = types.QueryPrice QueryGetParams = types.QueryGetParams
QueryRawPrices = types.QueryRawPrices
QueryMarkets = types.QueryMarkets QueryMarkets = types.QueryMarkets
QueryOracles = types.QueryOracles
QueryRawPrices = types.QueryRawPrices
QueryPrice = types.QueryPrice
) )
var ( var (
// functions aliases // functions aliases
RegisterCodec = types.RegisterCodec NewKeeper = keeper.NewKeeper
ErrEmptyInput = types.ErrEmptyInput NewQuerier = keeper.NewQuerier
ErrExpired = types.ErrExpired RegisterCodec = types.RegisterCodec
ErrNoValidPrice = types.ErrNoValidPrice ErrEmptyInput = types.ErrEmptyInput
ErrInvalidMarket = types.ErrInvalidMarket ErrExpired = types.ErrExpired
ErrInvalidOracle = types.ErrInvalidOracle ErrNoValidPrice = types.ErrNoValidPrice
NewGenesisState = types.NewGenesisState ErrInvalidMarket = types.ErrInvalidMarket
DefaultGenesisState = types.DefaultGenesisState ErrInvalidOracle = types.ErrInvalidOracle
NewMsgPostPrice = types.NewMsgPostPrice NewGenesisState = types.NewGenesisState
NewParams = types.NewParams DefaultGenesisState = types.DefaultGenesisState
DefaultParams = types.DefaultParams CurrentPriceKey = types.CurrentPriceKey
ParamKeyTable = types.ParamKeyTable RawPriceKey = types.RawPriceKey
NewKeeper = keeper.NewKeeper NewCurrentPrice = types.NewCurrentPrice
NewQuerier = keeper.NewQuerier NewPostedPrice = types.NewPostedPrice
NewMsgPostPrice = types.NewMsgPostPrice
NewParams = types.NewParams
DefaultParams = types.DefaultParams
ParamKeyTable = types.ParamKeyTable
NewQueryWithMarketIDParams = types.NewQueryWithMarketIDParams
// variable aliases // variable aliases
ModuleCdc = types.ModuleCdc ModuleCdc = types.ModuleCdc
KeyMarkets = types.KeyMarkets CurrentPricePrefix = types.CurrentPricePrefix
DefaultMarkets = types.DefaultMarkets RawPriceFeedPrefix = types.RawPriceFeedPrefix
KeyMarkets = types.KeyMarkets
DefaultMarkets = types.DefaultMarkets
) )
type ( type (
Keeper = keeper.Keeper
GenesisState = types.GenesisState GenesisState = types.GenesisState
Market = types.Market Market = types.Market
Markets = types.Markets Markets = types.Markets
CurrentPrice = types.CurrentPrice CurrentPrice = types.CurrentPrice
PostedPrice = types.PostedPrice PostedPrice = types.PostedPrice
PostedPrices = types.PostedPrices
SortDecs = types.SortDecs SortDecs = types.SortDecs
MsgPostPrice = types.MsgPostPrice MsgPostPrice = types.MsgPostPrice
Params = types.Params Params = types.Params
QueryWithMarketIDParams = types.QueryWithMarketIDParams QueryWithMarketIDParams = types.QueryWithMarketIDParams
Keeper = keeper.Keeper
) )

View File

@ -25,7 +25,10 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, gs GenesisState) {
// Set the current price (if any) based on what's now in the store // Set the current price (if any) based on what's now in the store
for _, market := range params.Markets { for _, market := range params.Markets {
if market.Active { if market.Active {
rps := keeper.GetRawPrices(ctx, market.MarketID) rps, err := keeper.GetRawPrices(ctx, market.MarketID)
if err != nil {
panic(err)
}
if len(rps) > 0 { if len(rps) > 0 {
err := keeper.SetCurrentPrices(ctx, market.MarketID) err := keeper.SetCurrentPrices(ctx, market.MarketID)
if err != nil { if err != nil {
@ -44,7 +47,10 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState {
var postedPrices []PostedPrice var postedPrices []PostedPrice
for _, market := range keeper.GetMarkets(ctx) { for _, market := range keeper.GetMarkets(ctx) {
pp := keeper.GetRawPrices(ctx, market.MarketID) pp, err := keeper.GetRawPrices(ctx, market.MarketID)
if err != nil {
panic(err)
}
postedPrices = append(postedPrices, pp...) postedPrices = append(postedPrices, pp...)
} }

View File

@ -46,7 +46,10 @@ func (k Keeper) SetPrice(
// If the expiry is less than or equal to the current blockheight, we consider the price valid // If the expiry is less than or equal to the current blockheight, we consider the price valid
if expiry.After(ctx.BlockTime()) { if expiry.After(ctx.BlockTime()) {
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
prices := k.GetRawPrices(ctx, marketID) prices, err := k.GetRawPrices(ctx, marketID)
if err != nil {
return types.PostedPrice{}, err
}
var index int var index int
found := false found := false
for i := range prices { for i := range prices {
@ -58,13 +61,9 @@ func (k Keeper) SetPrice(
} }
// set the price for that particular oracle // set the price for that particular oracle
if found { if found {
prices[index] = types.PostedPrice{ prices[index] = types.NewPostedPrice(marketID, oracle, price, expiry)
MarketID: marketID, OracleAddress: oracle,
Price: price, Expiry: expiry}
} else { } else {
prices = append(prices, types.PostedPrice{ prices = append(prices, types.NewPostedPrice(marketID, oracle, price, expiry))
MarketID: marketID, OracleAddress: oracle,
Price: price, Expiry: expiry})
index = len(prices) - 1 index = len(prices) - 1
} }
@ -79,7 +78,7 @@ func (k Keeper) SetPrice(
), ),
) )
store.Set( store.Set(
[]byte(types.RawPriceFeedPrefix+marketID), k.cdc.MustMarshalBinaryBare(prices), types.RawPriceKey(marketID), k.cdc.MustMarshalBinaryBare(prices),
) )
return prices[index], nil return prices[index], nil
} }
@ -100,21 +99,21 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
validPrevPrice = false validPrevPrice = false
} }
prices := k.GetRawPrices(ctx, marketID) prices, err := k.GetRawPrices(ctx, marketID)
var notExpiredPrices []types.CurrentPrice if err != nil {
return err
}
var notExpiredPrices types.CurrentPrices
// filter out expired prices // filter out expired prices
for _, v := range prices { for _, v := range prices {
if v.Expiry.After(ctx.BlockTime()) { if v.Expiry.After(ctx.BlockTime()) {
notExpiredPrices = append(notExpiredPrices, types.CurrentPrice{ notExpiredPrices = append(notExpiredPrices, types.NewCurrentPrice(v.MarketID, v.Price))
MarketID: v.MarketID,
Price: v.Price,
})
} }
} }
if len(notExpiredPrices) == 0 { if len(notExpiredPrices) == 0 {
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
store.Set( store.Set(
[]byte(types.CurrentPricePrefix+marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}), types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}),
) )
return types.ErrNoValidPrice(k.codespace) return types.ErrNoValidPrice(k.codespace)
} }
@ -135,20 +134,17 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
} }
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
currentPrice := types.CurrentPrice{ currentPrice := types.NewCurrentPrice(marketID, medianPrice)
MarketID: marketID,
Price: medianPrice,
}
store.Set( store.Set(
[]byte(types.CurrentPricePrefix+marketID), k.cdc.MustMarshalBinaryBare(currentPrice), types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(currentPrice),
) )
return nil return nil
} }
// CalculateMedianPrice calculates the median prices for the input prices. // CalculateMedianPrice calculates the median prices for the input prices.
func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices []types.CurrentPrice) sdk.Dec { func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices types.CurrentPrices) sdk.Dec {
l := len(prices) l := len(prices)
if l == 1 { if l == 1 {
@ -169,7 +165,7 @@ func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices []types.CurrentPric
} }
func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices []types.CurrentPrice) sdk.Dec { func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices types.CurrentPrices) sdk.Dec {
sum := prices[0].Price.Add(prices[1].Price) sum := prices[0].Price.Add(prices[1].Price)
mean := sum.Quo(sdk.NewDec(2)) mean := sum.Quo(sdk.NewDec(2))
return mean return mean
@ -178,15 +174,16 @@ func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices []types.CurrentPrice)
// GetCurrentPrice fetches the current median price of all oracles for a specific market // GetCurrentPrice fetches the current median price of all oracles for a specific market
func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, sdk.Error) { func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, sdk.Error) {
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
bz := store.Get(types.CurrentPriceKey(marketID))
bz := store.Get([]byte(types.CurrentPricePrefix + marketID))
if bz == nil { if bz == nil {
return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace) return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace)
} }
var price types.CurrentPrice var price types.CurrentPrice
k.cdc.MustUnmarshalBinaryBare(bz, &price) err := k.cdc.UnmarshalBinaryBare(bz, &price)
if err != nil {
return types.CurrentPrice{}, sdk.ErrInternal(sdk.AppendMsgToErr("failed to unmarshal result", err.Error()))
}
if price.Price.Equal(sdk.ZeroDec()) { if price.Price.Equal(sdk.ZeroDec()) {
return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace) return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace)
} }
@ -194,12 +191,18 @@ func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.Current
} }
// GetRawPrices fetches the set of all prices posted by oracles for an asset // GetRawPrices fetches the set of all prices posted by oracles for an asset
func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) []types.PostedPrice { func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) (types.PostedPrices, sdk.Error) {
store := ctx.KVStore(k.key) store := ctx.KVStore(k.key)
bz := store.Get([]byte(types.RawPriceFeedPrefix + marketID)) bz := store.Get(types.RawPriceKey(marketID))
var prices []types.PostedPrice if bz == nil {
k.cdc.MustUnmarshalBinaryBare(bz, &prices) return types.PostedPrices{}, nil
return prices }
var prices types.PostedPrices
err := k.cdc.UnmarshalBinaryBare(bz, &prices)
if err != nil {
return types.PostedPrices{}, sdk.ErrInternal(sdk.AppendMsgToErr("failed to unmarshal result", err.Error()))
}
return prices, nil
} }
// Codespace return the codespace for the keeper // Codespace return the codespace for the keeper

View File

@ -67,7 +67,8 @@ func TestKeeper_GetSetPrice(t *testing.T) {
time.Now().Add(1*time.Hour)) time.Now().Add(1*time.Hour))
require.NoError(t, err) require.NoError(t, err)
// Get raw prices // Get raw prices
rawPrices := keeper.GetRawPrices(ctx, "tstusd") rawPrices, err := keeper.GetRawPrices(ctx, "tstusd")
require.NoError(t, err)
require.Equal(t, len(rawPrices), 1) require.Equal(t, len(rawPrices), 1)
require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.33")), true) require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.33")), true)
// Set price by oracle 2 // Set price by oracle 2
@ -77,7 +78,8 @@ func TestKeeper_GetSetPrice(t *testing.T) {
time.Now().Add(time.Hour*1)) time.Now().Add(time.Hour*1))
require.NoError(t, err) require.NoError(t, err)
rawPrices = keeper.GetRawPrices(ctx, "tstusd") rawPrices, err = keeper.GetRawPrices(ctx, "tstusd")
require.NoError(t, err)
require.Equal(t, len(rawPrices), 2) require.Equal(t, len(rawPrices), 2)
require.Equal(t, rawPrices[1].Price.Equal(sdk.MustNewDecFromStr("0.35")), true) require.Equal(t, rawPrices[1].Price.Equal(sdk.MustNewDecFromStr("0.35")), true)
@ -87,7 +89,8 @@ func TestKeeper_GetSetPrice(t *testing.T) {
sdk.MustNewDecFromStr("0.37"), sdk.MustNewDecFromStr("0.37"),
time.Now().Add(time.Hour*1)) time.Now().Add(time.Hour*1))
require.NoError(t, err) require.NoError(t, err)
rawPrices = keeper.GetRawPrices(ctx, "tstusd") rawPrices, err = keeper.GetRawPrices(ctx, "tstusd")
require.NoError(t, err)
require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.37")), true) require.Equal(t, rawPrices[0].Price.Equal(sdk.MustNewDecFromStr("0.37")), true)
} }

View File

@ -56,18 +56,20 @@ func queryPrice(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []by
func queryRawPrices(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) { func queryRawPrices(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) {
var requestParams types.QueryWithMarketIDParams var requestParams types.QueryWithMarketIDParams
err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) err2 := keeper.cdc.UnmarshalJSON(req.Data, &requestParams)
if err != nil { if err2 != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err2))
} }
_, found := keeper.GetMarket(ctx, requestParams.MarketID) _, found := keeper.GetMarket(ctx, requestParams.MarketID)
if !found { if !found {
return []byte{}, sdk.ErrUnknownRequest("asset not found") return []byte{}, sdk.ErrUnknownRequest("asset not found")
} }
rawPrices := keeper.GetRawPrices(ctx, requestParams.MarketID) rawPrices, err := keeper.GetRawPrices(ctx, requestParams.MarketID)
bz, err := codec.MarshalJSONIndent(keeper.cdc, rawPrices)
if err != nil { if err != nil {
return nil, err
}
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, rawPrices)
if err2 != nil {
panic("could not marshal result to JSON") panic("could not marshal result to JSON")
} }

View File

@ -15,16 +15,22 @@ const (
// DefaultParamspace default namestore // DefaultParamspace default namestore
DefaultParamspace = ModuleName DefaultParamspace = ModuleName
)
var (
// CurrentPricePrefix prefix for the current price of an asset
CurrentPricePrefix = []byte{0x00}
// RawPriceFeedPrefix prefix for the raw pricefeed of an asset // RawPriceFeedPrefix prefix for the raw pricefeed of an asset
RawPriceFeedPrefix = StoreKey + ":raw:" RawPriceFeedPrefix = []byte{0x01}
// CurrentPricePrefix prefix for the current price of an asset
CurrentPricePrefix = StoreKey + ":currentprice:"
// MarketPrefix Prefix for the assets in the pricefeed system
MarketPrefix = StoreKey + ":markets"
// OraclePrefix store prefix for the oracle accounts
OraclePrefix = StoreKey + ":oracles"
) )
// CurrentPriceKey returns the prefix for the current price
func CurrentPriceKey(marketID string) []byte {
return append(CurrentPricePrefix, []byte(marketID)...)
}
// RawPriceKey returns the prefix for the raw price
func RawPriceKey(marketID string) []byte {
return append(RawPriceFeedPrefix, []byte(marketID)...)
}

View File

@ -46,6 +46,14 @@ type CurrentPrice struct {
Price sdk.Dec `json:"price" yaml:"price"` Price sdk.Dec `json:"price" yaml:"price"`
} }
// NewCurrentPrice returns an instance of CurrentPrice
func NewCurrentPrice(marketID string, price sdk.Dec) CurrentPrice {
return CurrentPrice{MarketID: marketID, Price: price}
}
// CurrentPrices type for an array of CurrentPrice
type CurrentPrices []CurrentPrice
// PostedPrice price for market posted by a specific oracle // PostedPrice price for market posted by a specific oracle
type PostedPrice struct { type PostedPrice struct {
MarketID string `json:"market_id" yaml:"market_id"` MarketID string `json:"market_id" yaml:"market_id"`
@ -54,6 +62,19 @@ type PostedPrice struct {
Expiry time.Time `json:"expiry" yaml:"expiry"` Expiry time.Time `json:"expiry" yaml:"expiry"`
} }
// NewPostedPrice returns a new PostedPrice
func NewPostedPrice(marketID string, oracle sdk.AccAddress, price sdk.Dec, expiry time.Time) PostedPrice {
return PostedPrice{
MarketID: marketID,
OracleAddress: oracle,
Price: price,
Expiry: expiry,
}
}
// PostedPrices type for an array of PostedPrice
type PostedPrices []PostedPrice
// implement fmt.Stringer // implement fmt.Stringer
func (cp CurrentPrice) String() string { func (cp CurrentPrice) String() string {
return strings.TrimSpace(fmt.Sprintf(`Market ID: %s return strings.TrimSpace(fmt.Sprintf(`Market ID: %s
@ -68,6 +89,15 @@ Price: %s
Expiry: %s`, pp.MarketID, pp.OracleAddress, pp.Price, pp.Expiry)) Expiry: %s`, pp.MarketID, pp.OracleAddress, pp.Price, pp.Expiry))
} }
// String implements fmt.Stringer
func (ps PostedPrices) String() string {
out := "Posted Prices:\n"
for _, p := range ps {
out += fmt.Sprintf("%s\n", p.String())
}
return strings.TrimSpace(out)
}
// SortDecs provides the interface needed to sort sdk.Dec slices // SortDecs provides the interface needed to sort sdk.Dec slices
type SortDecs []sdk.Dec type SortDecs []sdk.Dec