kava-3 to kava-4 pricefeed migration (#664)

This commit is contained in:
Kevin Davis 2020-09-30 14:33:48 -04:00 committed by GitHub
parent 6e923d70a4
commit d577711056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 444 additions and 44 deletions

View File

@ -7,6 +7,8 @@ import (
v0_11bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_11"
v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
v0_11pricefeed "github.com/kava-labs/kava/x/pricefeed"
v0_9pricefeed "github.com/kava-labs/kava/x/pricefeed/legacy/v0_9"
)
// MigrateBep3 migrates from a v0.9 (or v0.10) bep3 genesis state to a v0.11 bep3 genesis state
@ -65,3 +67,32 @@ func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState {
PreviousBlockTime: v0_11bep3.DefaultPreviousBlockTime,
}
}
// MigratePricefeed migrates from a v0.9 (or v0.10) pricefeed genesis state to a v0.11 pricefeed genesis state
func MigratePricefeed(oldGenState v0_9pricefeed.GenesisState) v0_11pricefeed.GenesisState {
var newMarkets v0_11pricefeed.Markets
var newPostedPrices v0_11pricefeed.PostedPrices
var oracles []sdk.AccAddress
for _, market := range oldGenState.Params.Markets {
newMarket := v0_11pricefeed.NewMarket(market.MarketID, market.BaseAsset, market.QuoteAsset, market.Oracles, market.Active)
newMarkets = append(newMarkets, newMarket)
oracles = market.Oracles
}
// ------- add btc, xrp, busd markets --------
btcSpotMarket := v0_11pricefeed.NewMarket("btc:usd", "btc", "usd", oracles, true)
btcLiquidationMarket := v0_11pricefeed.NewMarket("btc:usd:30", "btc", "usd", oracles, true)
xrpSpotMarket := v0_11pricefeed.NewMarket("xrp:usd", "xrp", "usd", oracles, true)
xrpLiquidationMarket := v0_11pricefeed.NewMarket("xrp:usd:30", "xrp", "usd", oracles, true)
busdSpotMarket := v0_11pricefeed.NewMarket("busd:usd", "busd", "usd", oracles, true)
busdLiquidationMarket := v0_11pricefeed.NewMarket("busd:usd:30", "busd", "usd", oracles, true)
newMarkets = append(newMarkets, btcSpotMarket, btcLiquidationMarket, xrpSpotMarket, xrpLiquidationMarket, busdSpotMarket, busdLiquidationMarket)
for _, price := range oldGenState.PostedPrices {
newPrice := v0_11pricefeed.NewPostedPrice(price.MarketID, price.OracleAddress, price.Price, price.Expiry)
newPostedPrices = append(newPostedPrices, newPrice)
}
newParams := v0_11pricefeed.NewParams(newMarkets)
return v0_11pricefeed.NewGenesisState(newParams, newPostedPrices)
}

View File

@ -12,6 +12,7 @@ import (
"github.com/kava-labs/kava/app"
v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
v0_9pricefeed "github.com/kava-labs/kava/x/pricefeed/legacy/v0_9"
)
func TestMain(m *testing.M) {
@ -35,3 +36,16 @@ func TestMigrateBep3(t *testing.T) {
err = newGenState.Validate()
require.NoError(t, err)
}
func TestMigratePricefeed(t *testing.T) {
bz, err := ioutil.ReadFile(filepath.Join("testdata", "pricefeed-v09.json"))
require.NoError(t, err)
var oldGenState v0_9pricefeed.GenesisState
cdc := app.MakeCodec()
require.NotPanics(t, func() {
cdc.MustUnmarshalJSON(bz, &oldGenState)
})
newGenState := MigratePricefeed(oldGenState)
err = newGenState.Validate()
require.NoError(t, err)
}

View File

@ -0,0 +1,152 @@
{
"params": {
"markets": [
{
"active": true,
"base_asset": "bnb",
"market_id": "bnb:usd",
"oracles": [
"kava12dyshua9nkvx9w8ywp72wdnzrc4t4mnnycz0dl",
"kava1tuxyepdrkwraa22k99w04c0wa64tgh70mv87fs",
"kava1ueak7nzesm3pnev6lngp6lgk0ry02djz8pjpcg",
"kava1sl62nqm89c780yxm3m9lp3tacmpnfljq6tytvl",
"kava1ujfrlcd0ted58mzplnyxzklsw0sqevlgxndanp",
"kava17fatl3wzxvk4rwfu3tqsctdp5x9vute67j9ufj",
"kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92",
"kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn",
"kava1pt6q4kdmwawr3thm9cd82pq7hml8u84rd0f3jy",
"kava13tpwqygswyzupqfggfgh9dmtgthgucn5wpfksh"
],
"quote_asset": "usd"
},
{
"active": true,
"base_asset": "bnb",
"market_id": "bnb:usd:30",
"oracles": [
"kava12dyshua9nkvx9w8ywp72wdnzrc4t4mnnycz0dl",
"kava1tuxyepdrkwraa22k99w04c0wa64tgh70mv87fs",
"kava1ueak7nzesm3pnev6lngp6lgk0ry02djz8pjpcg",
"kava1sl62nqm89c780yxm3m9lp3tacmpnfljq6tytvl",
"kava1ujfrlcd0ted58mzplnyxzklsw0sqevlgxndanp",
"kava17fatl3wzxvk4rwfu3tqsctdp5x9vute67j9ufj",
"kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92",
"kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn",
"kava1pt6q4kdmwawr3thm9cd82pq7hml8u84rd0f3jy",
"kava13tpwqygswyzupqfggfgh9dmtgthgucn5wpfksh"
],
"quote_asset": "usd"
}
]
},
"posted_prices": [
{
"expiry": "2020-08-19T06:14:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92",
"price": "22.925799999999998846"
},
{
"expiry": "2020-08-19T06:14:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava1pt6q4kdmwawr3thm9cd82pq7hml8u84rd0f3jy",
"price": "22.932800000000000296"
},
{
"expiry": "2020-07-28T07:30:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava1ujfrlcd0ted58mzplnyxzklsw0sqevlgxndanp",
"price": "20.084499999999998465"
},
{
"expiry": "2020-08-19T06:14:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava1ueak7nzesm3pnev6lngp6lgk0ry02djz8pjpcg",
"price": "22.932800000000000296"
},
{
"expiry": "2020-08-19T06:22:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava17fatl3wzxvk4rwfu3tqsctdp5x9vute67j9ufj",
"price": "22.866299999999998960"
},
{
"expiry": "2020-08-19T06:14:01Z",
"market_id": "bnb:usd",
"oracle_address": "kava12dyshua9nkvx9w8ywp72wdnzrc4t4mnnycz0dl",
"price": "22.932800000000000296"
},
{
"expiry": "2020-08-19T06:14:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn",
"price": "22.932800000000000296"
},
{
"expiry": "2020-08-19T06:28:00Z",
"market_id": "bnb:usd",
"oracle_address": "kava13tpwqygswyzupqfggfgh9dmtgthgucn5wpfksh",
"price": "22.836200000000001609"
},
{
"expiry": "2020-08-19T06:29:31Z",
"market_id": "bnb:usd",
"oracle_address": "kava1sl62nqm89c780yxm3m9lp3tacmpnfljq6tytvl",
"price": "22.834499999999998465"
},
{
"expiry": "2020-08-19T06:25:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava19rjk5qmmwywnzfccwzyn02jywgpwjqf60afj92",
"price": "22.967586666666665707"
},
{
"expiry": "2020-08-19T06:24:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava1pt6q4kdmwawr3thm9cd82pq7hml8u84rd0f3jy",
"price": "22.972826666666666284"
},
{
"expiry": "2020-08-19T06:22:30Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava1ueak7nzesm3pnev6lngp6lgk0ry02djz8pjpcg",
"price": "22.980756666666668053"
},
{
"expiry": "2020-07-28T08:30:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava1ujfrlcd0ted58mzplnyxzklsw0sqevlgxndanp",
"price": "20.056519999999995463"
},
{
"expiry": "2020-08-19T06:25:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava17fatl3wzxvk4rwfu3tqsctdp5x9vute67j9ufj",
"price": "22.967586666666665707"
},
{
"expiry": "2020-08-19T06:25:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava12dyshua9nkvx9w8ywp72wdnzrc4t4mnnycz0dl",
"price": "22.967586666666665707"
},
{
"expiry": "2020-08-19T06:25:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn",
"price": "22.967586666666665707"
},
{
"expiry": "2020-08-19T06:22:00Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava13tpwqygswyzupqfggfgh9dmtgthgucn5wpfksh",
"price": "22.972629999999998773"
},
{
"expiry": "2020-08-19T06:24:31Z",
"market_id": "bnb:usd:30",
"oracle_address": "kava1sl62nqm89c780yxm3m9lp3tacmpnfljq6tytvl",
"price": "22.967533333333335577"
}
]
}

View File

@ -1,81 +1,78 @@
package pricefeed
// DO NOT EDIT - generated by aliasgen tool (github.com/rhuairahrighairidh/aliasgen)
import (
"github.com/kava-labs/kava/x/pricefeed/keeper"
"github.com/kava-labs/kava/x/pricefeed/types"
)
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/types/
// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/keeper/
// nolint
const (
EventTypeMarketPriceUpdated = types.EventTypeMarketPriceUpdated
EventTypeOracleUpdatedPrice = types.EventTypeOracleUpdatedPrice
EventTypeNoValidPrices = types.EventTypeNoValidPrices
AttributeValueCategory = types.AttributeValueCategory
AttributeExpiry = types.AttributeExpiry
AttributeMarketID = types.AttributeMarketID
AttributeMarketPrice = types.AttributeMarketPrice
AttributeOracle = types.AttributeOracle
AttributeExpiry = types.AttributeExpiry
ModuleName = types.ModuleName
StoreKey = types.StoreKey
RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute
AttributeValueCategory = types.AttributeValueCategory
DefaultParamspace = types.DefaultParamspace
TypeMsgPostPrice = types.TypeMsgPostPrice
EventTypeMarketPriceUpdated = types.EventTypeMarketPriceUpdated
EventTypeNoValidPrices = types.EventTypeNoValidPrices
EventTypeOracleUpdatedPrice = types.EventTypeOracleUpdatedPrice
MaxExpiry = types.MaxExpiry
ModuleName = types.ModuleName
QuerierRoute = types.QuerierRoute
QueryGetParams = types.QueryGetParams
QueryMarkets = types.QueryMarkets
QueryOracles = types.QueryOracles
QueryRawPrices = types.QueryRawPrices
QueryPrice = types.QueryPrice
MaxExpiry = types.MaxExpiry
QueryRawPrices = types.QueryRawPrices
RouterKey = types.RouterKey
StoreKey = types.StoreKey
TypeMsgPostPrice = types.TypeMsgPostPrice
)
// nolint
var (
// functions aliases
// function aliases
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
RegisterCodec = types.RegisterCodec
ErrEmptyInput = types.ErrEmptyInput
ErrExpired = types.ErrExpired
ErrNoValidPrice = types.ErrNoValidPrice
ErrInvalidMarket = types.ErrInvalidMarket
ErrInvalidOracle = types.ErrInvalidOracle
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
CurrentPriceKey = types.CurrentPriceKey
RawPriceKey = types.RawPriceKey
DefaultGenesisState = types.DefaultGenesisState
DefaultParams = types.DefaultParams
NewCurrentPrice = types.NewCurrentPrice
NewPostedPrice = types.NewPostedPrice
NewGenesisState = types.NewGenesisState
NewMarket = types.NewMarket
NewMsgPostPrice = types.NewMsgPostPrice
NewParams = types.NewParams
DefaultParams = types.DefaultParams
ParamKeyTable = types.ParamKeyTable
NewPostedPrice = types.NewPostedPrice
NewQueryWithMarketIDParams = types.NewQueryWithMarketIDParams
ParamKeyTable = types.ParamKeyTable
RawPriceKey = types.RawPriceKey
RegisterCodec = types.RegisterCodec
// variable aliases
ModuleCdc = types.ModuleCdc
CurrentPricePrefix = types.CurrentPricePrefix
RawPriceFeedPrefix = types.RawPriceFeedPrefix
KeyMarkets = types.KeyMarkets
DefaultMarkets = types.DefaultMarkets
ErrAssetNotFound = types.ErrAssetNotFound
ErrEmptyInput = types.ErrEmptyInput
ErrExpired = types.ErrExpired
ErrInvalidMarket = types.ErrInvalidMarket
ErrInvalidOracle = types.ErrInvalidOracle
ErrNoValidPrice = types.ErrNoValidPrice
KeyMarkets = types.KeyMarkets
ModuleCdc = types.ModuleCdc
RawPriceFeedPrefix = types.RawPriceFeedPrefix
)
// nolint
type (
Keeper = keeper.Keeper
CurrentPrice = types.CurrentPrice
CurrentPrices = types.CurrentPrices
GenesisState = types.GenesisState
Market = types.Market
Markets = types.Markets
CurrentPrice = types.CurrentPrice
PostedPrice = types.PostedPrice
PostedPrices = types.PostedPrices
SortDecs = types.SortDecs
MsgPostPrice = types.MsgPostPrice
Params = types.Params
PostedPrice = types.PostedPrice
PostedPrices = types.PostedPrices
QueryWithMarketIDParams = types.QueryWithMarketIDParams
SortDecs = types.SortDecs
)

View File

@ -9,11 +9,13 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, gs GenesisState) {
// Set the markets and oracles from params
keeper.SetParams(ctx, gs.Params)
// Iterate through the posted prices and set them in the store
// Iterate through the posted prices and set them in the store if they are not expired
for _, pp := range gs.PostedPrices {
_, err := keeper.SetPrice(ctx, pp.OracleAddress, pp.MarketID, pp.Price, pp.Expiry)
if err != nil {
panic(err)
if pp.Expiry.After(ctx.BlockTime()) {
_, err := keeper.SetPrice(ctx, pp.OracleAddress, pp.MarketID, pp.Price, pp.Expiry)
if err != nil {
panic(err)
}
}
}
params := keeper.GetParams(ctx)

View File

@ -0,0 +1,193 @@
package v0_9
import (
"errors"
"fmt"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - pricefeed state that must be provided at genesis
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
PostedPrices PostedPrices `json:"posted_prices" yaml:"posted_prices"`
}
// NewGenesisState creates a new genesis state for the pricefeed module
func NewGenesisState(p Params, pp []PostedPrice) GenesisState {
return GenesisState{
Params: p,
PostedPrices: pp,
}
}
type Params struct {
Markets Markets `json:"markets" yaml:"markets"` // Array containing the markets supported by the pricefeed
}
type Market struct {
// TODO: rename to ID
MarketID string `json:"market_id" yaml:"market_id"`
BaseAsset string `json:"base_asset" yaml:"base_asset"`
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
Oracles []sdk.AccAddress `json:"oracles" yaml:"oracles"`
Active bool `json:"active" yaml:"active"`
}
// String implement fmt.Stringer
func (m Market) String() string {
return fmt.Sprintf(`Asset:
Market ID: %s
Base Asset: %s
Quote Asset: %s
Oracles: %s
Active: %t`,
m.MarketID, m.BaseAsset, m.QuoteAsset, m.Oracles, m.Active)
}
// Validate performs a basic validation of the market params
func (m Market) Validate() error {
if strings.TrimSpace(m.MarketID) == "" {
return errors.New("market id cannot be blank")
}
if err := sdk.ValidateDenom(m.BaseAsset); err != nil {
return fmt.Errorf("invalid base asset: %w", err)
}
if err := sdk.ValidateDenom(m.QuoteAsset); err != nil {
return fmt.Errorf("invalid quote asset: %w", err)
}
seenOracles := make(map[string]bool)
for i, oracle := range m.Oracles {
if oracle.Empty() {
return fmt.Errorf("oracle %d is empty", i)
}
if seenOracles[oracle.String()] {
return fmt.Errorf("duplicated oracle %s", oracle)
}
seenOracles[oracle.String()] = true
}
return nil
}
// Markets array type for oracle
type Markets []Market
// Validate checks if all the markets are valid and there are no duplicated
// entries.
func (ms Markets) Validate() error {
seenMarkets := make(map[string]bool)
for _, m := range ms {
if seenMarkets[m.MarketID] {
return fmt.Errorf("duplicated market %s", m.MarketID)
}
if err := m.Validate(); err != nil {
return err
}
seenMarkets[m.MarketID] = true
}
return nil
}
// String implements fmt.Stringer
func (ms Markets) String() string {
out := "Markets:\n"
for _, m := range ms {
out += fmt.Sprintf("%s\n", m.String())
}
return strings.TrimSpace(out)
}
// CurrentPrice struct that contains the metadata of a current price for a particular market in the pricefeed module.
type CurrentPrice struct {
MarketID string `json:"market_id" yaml:"market_id"`
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
type PostedPrice struct {
MarketID string `json:"market_id" yaml:"market_id"`
OracleAddress sdk.AccAddress `json:"oracle_address" yaml:"oracle_address"`
Price sdk.Dec `json:"price" yaml:"price"`
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,
}
}
// Validate performs a basic check of a PostedPrice params.
func (pp PostedPrice) Validate() error {
if strings.TrimSpace(pp.MarketID) == "" {
return errors.New("market id cannot be blank")
}
if pp.OracleAddress.Empty() {
return errors.New("oracle address cannot be empty")
}
if pp.Price.IsNegative() {
return fmt.Errorf("posted price cannot be negative %s", pp.Price)
}
if pp.Expiry.IsZero() {
return errors.New("expiry time cannot be zero")
}
return nil
}
// PostedPrices type for an array of PostedPrice
type PostedPrices []PostedPrice
// Validate checks if all the posted prices are valid and there are no duplicated
// entries.
func (pps PostedPrices) Validate() error {
seenPrices := make(map[string]bool)
for _, pp := range pps {
if pp.OracleAddress != nil && seenPrices[pp.MarketID+pp.OracleAddress.String()] {
return fmt.Errorf("duplicated posted price for marked id %s and oracle address %s", pp.MarketID, pp.OracleAddress)
}
if err := pp.Validate(); err != nil {
return err
}
seenPrices[pp.MarketID+pp.OracleAddress.String()] = true
}
return nil
}
// implement fmt.Stringer
func (cp CurrentPrice) String() string {
return strings.TrimSpace(fmt.Sprintf(`Market ID: %s
Price: %s`, cp.MarketID, cp.Price))
}
// implement fmt.Stringer
func (pp PostedPrice) String() string {
return strings.TrimSpace(fmt.Sprintf(`Market ID: %s
Oracle Address: %s
Price: %s
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)
}

View File

@ -19,6 +19,17 @@ type Market struct {
Active bool `json:"active" yaml:"active"`
}
// NewMarket returns a new Market
func NewMarket(id, base, quote string, oracles []sdk.AccAddress, active bool) Market {
return Market{
MarketID: id,
BaseAsset: base,
QuoteAsset: quote,
Oracles: oracles,
Active: active,
}
}
// String implement fmt.Stringer
func (m Market) String() string {
return fmt.Sprintf(`Asset: