-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Rename oracle to slinky, refactor mm provider factory #272
Changes from 19 commits
7d8d191
0b0e899
adb39d8
0fcc9dd
c50a833
c1cd3ee
5960108
ebdb2aa
6d8b7fb
adeecd2
279db59
d5e7d3d
7c2230f
7a8f3b9
0da58f2
a752c03
77faa08
67614b2
a62d7d6
f19b85e
ea8f03b
f4c871e
db63e31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
"encoding/json" | ||
"fmt" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
|
@@ -34,7 +35,7 @@ | |
|
||
var ( | ||
rootCmd = &cobra.Command{ | ||
Use: "config", | ||
Use: "slinky-config", | ||
Short: "Create configuration required for running slinky.", | ||
Args: cobra.NoArgs, | ||
Run: func(_ *cobra.Command, _ []string) { | ||
|
@@ -222,77 +223,77 @@ | |
"oracle-config-path", | ||
"", | ||
"oracle.json", | ||
"path to write the oracle config file to. this file is required to run the oracle.", | ||
"Path to write the oracle config file to. This file is required to run the oracle.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&marketCfgPath, | ||
"market-config-path", | ||
"", | ||
"market.json", | ||
"path to write the market config file to. this file is required to run the oracle.", | ||
"Path to write the market config file to. This file is required to run the oracle.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&chain, | ||
"chain-id", | ||
"chain", | ||
"", | ||
"", | ||
"chain-id that we expect the oracle to be running on. ex dydx-mainnet-1, dydx-testnet-4. this should only be specified if required by the chain.", | ||
"Chain that we expect the oracle to be running on {dydx, \"\"}. This should only be specified if required by the chain.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&nodeURL, | ||
"node-http-url", | ||
"", | ||
"", | ||
"http endpoint of the cosmos sdk node corresponding to the chain id (typically something like localhost:1317). this should only be specified if required by the chain.", | ||
"Http endpoint of the cosmos sdk node corresponding to the chain (typically localhost:1317 or a remote API). This should only be specified if required by the chain.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&host, | ||
"host", | ||
"", | ||
"0.0.0.0", | ||
"host is the oracle / prometheus server host.", | ||
"Host is the oracle / prometheus server host.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&pricesPort, | ||
"port", | ||
"", | ||
"8080", | ||
"port that the oracle will make prices available on. to query prices after starting the oracle, use the following command: curl http://<host>:<port>/slinky/oracle/v1/prices", | ||
"Port that the oracle will make prices available on. To query prices after starting the oracle, use the following command: curl http://<host>:<port>/slinky/oracle/v1/prices", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&prometheusPort, | ||
"prometheus-port", | ||
"", | ||
"8002", | ||
"port that the prometheus server will listen on. to query prometheus metrics after starting the oracle, use the following command: curl http://<host>:<port>/metrics", | ||
"Port that the prometheus server will listen on. To query prometheus metrics after starting the oracle, use the following command: curl http://<host>:<port>/metrics", | ||
) | ||
rootCmd.Flags().BoolVarP( | ||
&disabledMetrics, | ||
"disable-metrics", | ||
"", | ||
false, | ||
"flag that disables the prometheus server. if this is enabled the prometheus port must be specified. to query prometheus metrics after starting the oracle, use the following command: curl http://<host>:<port>/metrics", | ||
"Flag that disables the prometheus server. If this is enabled the prometheus port must be specified. To query prometheus metrics after starting the oracle, use the following command: curl http://<host>:<port>/metrics", | ||
) | ||
rootCmd.Flags().BoolVarP( | ||
&debug, | ||
"debug-mode", | ||
"", | ||
false, | ||
"flag that enables debug mode. specifically the side-car will run in debug mode. this is useful for local development / debugging.", | ||
"Flag that enables debug mode for the side-car. This is useful for local development / debugging.", | ||
) | ||
rootCmd.Flags().DurationVarP( | ||
&updateInterval, | ||
"update-interval", | ||
"", | ||
1500*time.Millisecond, | ||
"interval at which the oracle will update the prices. this should be set to the interval desired by the chain.", | ||
"Interval at which the oracle will update the prices. This should be set to the interval desired by the chain.", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would make sure that this is set to 500 milliseconds |
||
) | ||
rootCmd.Flags().DurationVarP( | ||
&maxPriceAge, | ||
"max-price-age", | ||
"", | ||
2*time.Minute, | ||
"maximum age of a price that the oracle will accept. this should be set to the maximum age desired by the chain.", | ||
"Maximum age of a price that the oracle will accept. This should be set to the maximum age desired by the chain.", | ||
) | ||
} | ||
|
||
|
@@ -306,7 +307,7 @@ | |
func createOracleConfig() error { | ||
// If the providers is not empty, filter the providers to include only the | ||
// providers that are specified. | ||
if chain == constants.DYDXMainnet.ID || chain == constants.DYDXTestnet.ID { | ||
if strings.ToLower(chain) == constants.Dydx { | ||
// Filter out the providers that are not supported by the dYdX chain. | ||
validProviders := make(map[string]struct{}) | ||
for _, providers := range dydx.ProviderMapping { | ||
|
@@ -382,7 +383,7 @@ | |
// oracle is always started using the market map that is expected to be stored by the | ||
// market map module. | ||
func createMarketMap() error { | ||
if chain == constants.DYDXMainnet.ID || chain == constants.DYDXTestnet.ID { | ||
if strings.ToLower(chain) == constants.Dydx { | ||
fmt.Fprintf( | ||
os.Stderr, | ||
"dYdX chain requires the use of a predetermined market map. please use the market map provided by the Skip/dYdX team or the default market map provided in /config/dydx/market.json", | ||
|
@@ -397,6 +398,7 @@ | |
// TickersToProviders defines a map of tickers to their respective providers. This | ||
// contains all of the providers that are supported per ticker. | ||
tickersToProviders = make(map[string]mmtypes.Providers) | ||
tickersToPaths = make(map[string]mmtypes.Paths) | ||
) | ||
|
||
// Iterate through all of the provider ticker configurations and update the | ||
|
@@ -422,13 +424,27 @@ | |
providers := tickersToProviders[tickerStr].Providers | ||
providers = append(providers, config) | ||
tickersToProviders[tickerStr] = mmtypes.Providers{Providers: providers} | ||
|
||
if _, ok := tickersToPaths[tickerStr]; !ok { | ||
tickersToPaths[tickerStr] = mmtypes.Paths{} | ||
} | ||
paths := tickersToPaths[tickerStr].Paths | ||
paths = append(paths, mmtypes.Path{Operations: []mmtypes.Operation{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're medianizing over all paths, no? This would give extra weight to the raw feed if it alr existed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a pure median over all feeds with optional 1 hop inversion. |
||
{ | ||
CurrencyPair: ticker.CurrencyPair, | ||
Invert: false, | ||
Provider: config.Name, | ||
}, | ||
}}) | ||
tickersToPaths[tickerStr] = mmtypes.Paths{Paths: paths} | ||
} | ||
} | ||
|
||
// Create a new market map from the provider to market map. | ||
marketMap := mmtypes.MarketMap{ | ||
Tickers: tickers, | ||
Providers: tickersToProviders, | ||
Paths: tickersToPaths, | ||
} | ||
|
||
// Validate the market map. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,6 @@ | |
|
||
"github.com/skip-mev/slinky/oracle" | ||
"github.com/skip-mev/slinky/oracle/config" | ||
"github.com/skip-mev/slinky/oracle/constants" | ||
oraclemetrics "github.com/skip-mev/slinky/oracle/metrics" | ||
"github.com/skip-mev/slinky/oracle/orchestrator" | ||
"github.com/skip-mev/slinky/oracle/types" | ||
|
@@ -36,12 +35,11 @@ | |
}, | ||
} | ||
|
||
oracleCfgPath string | ||
marketCfgPath string | ||
runPprof bool | ||
profilePort string | ||
chain string | ||
updateLocalConfig bool | ||
oracleCfgPath string | ||
marketCfgPath string | ||
updateMarketCfgPath string | ||
runPprof bool | ||
profilePort string | ||
) | ||
|
||
func init() { | ||
|
@@ -50,43 +48,37 @@ | |
"oracle-config-path", | ||
"", | ||
"oracle.json", | ||
"path to the oracle config file", | ||
"Path to the oracle config file.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&marketCfgPath, | ||
"market-config-path", | ||
"", | ||
"market.json", | ||
"path to the market config file", | ||
"", | ||
"Path to the market config file. If you supplied a node URL in your config, this will not be required.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&updateMarketCfgPath, | ||
"update-market-config-path", | ||
"", | ||
"", | ||
"Path where the current market config will be written. Overwrites any pre-existing file. Requires an http-node-url/marketmap provider in your oracle.json config.", | ||
) | ||
rootCmd.Flags().BoolVarP( | ||
&runPprof, | ||
"run-pprof", | ||
"", | ||
false, | ||
"run pprof server", | ||
"Run pprof server.", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&profilePort, | ||
"pprof-port", | ||
"", | ||
"6060", | ||
"port for the pprof server to listen on", | ||
) | ||
rootCmd.Flags().StringVarP( | ||
&chain, | ||
"chain-id", | ||
"", | ||
"", | ||
"the chain id for which the side car should run for (ex. dydx-mainnet-1)", | ||
) | ||
rootCmd.Flags().BoolVarP( | ||
&updateLocalConfig, | ||
"update-local-market-config", | ||
"", | ||
true, | ||
"update the market map config when a new one is received; this will overwrite the existing config file.", | ||
"Port for the pprof server to listen on.", | ||
) | ||
rootCmd.MarkFlagsMutuallyExclusive("update-market-config-path", "market-config-path") | ||
} | ||
|
||
// start the oracle-grpc server + oracle process, cancel on interrupt or terminate. | ||
|
@@ -110,9 +102,12 @@ | |
return fmt.Errorf("failed to read oracle config file: %s", err.Error()) | ||
} | ||
|
||
marketCfg, err := types.ReadMarketConfigFromFile(marketCfgPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to read market config file: %s", err.Error()) | ||
var marketCfg mmtypes.MarketMap | ||
if marketCfgPath != "" { | ||
marketCfg, err = types.ReadMarketConfigFromFile(marketCfgPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to read market config file: %s", err.Error()) | ||
} | ||
} | ||
|
||
var logger *zap.Logger | ||
|
@@ -129,29 +124,33 @@ | |
} | ||
|
||
metrics := oraclemetrics.NewMetricsFromConfig(cfg.Metrics) | ||
aggregator, err := oraclemath.NewMedianAggregator( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought we agreed to have a single aggregation strat in the oracle (the dydx one) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the dydx aggregation. |
||
logger, | ||
marketCfg, | ||
metrics, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to create data aggregator: %w", err) | ||
} | ||
|
||
// Define the orchestrator and oracle options. These determine how the orchestrator and oracle are created & executed. | ||
orchestratorOpts := []orchestrator.Option{ | ||
orchestrator.WithLogger(logger), | ||
orchestrator.WithMarketMap(marketCfg), | ||
orchestrator.WithPriceAPIQueryHandlerFactory(oraclefactory.APIQueryHandlerFactory), // Replace with custom API query handler factory. | ||
orchestrator.WithPriceWebSocketQueryHandlerFactory(oraclefactory.WebSocketQueryHandlerFactory), // Replace with custom websocket query handler factory. | ||
orchestrator.WithMarketMapperFactory(oraclefactory.MarketMapProviderFactory), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need the marketmap provider factory to only be included in the dYdX options because it will either run the mm provider and update based on what is on dYdX main-net or it will fail to be constructed if the market map provider is not included in the oracle config. There is probably a cleaner way with dealing how to run this based per chain and whether the mm provider is desired. But I'm assuming this isnt desired behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused why this even needs to be an option? Shouldn't we be able to have casing logic like websocket / api, and have the default be a nop updater (no mm-provider? Or a mm provider that never updates) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an option that lets the orchestrator know how to construct a market map provider if one is included in the oracle.json. Reading through this again I think this looks good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The factories take care of casing based on the API/Websocket implementation |
||
orchestrator.WithAggregator(aggregator), | ||
} | ||
if updateMarketCfgPath != "" { | ||
orchestratorOpts = append(orchestratorOpts, orchestrator.WithWriteTo(updateMarketCfgPath)) | ||
} | ||
oracleOpts := []oracle.Option{ | ||
oracle.WithLogger(logger), | ||
oracle.WithUpdateInterval(cfg.UpdateInterval), | ||
oracle.WithMetrics(metrics), | ||
oracle.WithMaxCacheAge(cfg.MaxPriceAge), | ||
} | ||
|
||
if chain == constants.DYDXMainnet.ID || chain == constants.DYDXTestnet.ID { | ||
customOrchestratorOps, customOracleOpts, err := dydxOptions(logger, marketCfg, metrics) | ||
if err != nil { | ||
return fmt.Errorf("failed to create dydx orchestrator and oracle options: %w", err) | ||
} | ||
|
||
orchestratorOpts = append(orchestratorOpts, customOrchestratorOps...) | ||
oracleOpts = append(oracleOpts, customOracleOpts...) | ||
oracle.WithDataAggregator(aggregator), | ||
} | ||
|
||
// Create the orchestrator and start the orchestrator. | ||
|
@@ -219,36 +218,3 @@ | |
} | ||
return nil | ||
} | ||
|
||
// dydxOptions specifies the custom orchestrator and oracle options for dYdX. | ||
func dydxOptions( | ||
logger *zap.Logger, | ||
marketCfg mmtypes.MarketMap, | ||
metrics oraclemetrics.Metrics, | ||
) ([]orchestrator.Option, []oracle.Option, error) { | ||
// dYdX uses the median index price aggregation strategy. | ||
aggregator, err := oraclemath.NewMedianAggregator( | ||
logger, | ||
marketCfg, | ||
metrics, | ||
) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
// The oracle must be configured with the median index price aggregator. | ||
customOracleOpts := []oracle.Option{ | ||
oracle.WithDataAggregator(aggregator), | ||
} | ||
|
||
// Additionally, dYdX requires a custom market map provider that fetches market params from the chain. | ||
customOrchestratorOps := []orchestrator.Option{ | ||
orchestrator.WithMarketMapperFactory(oraclefactory.DefaultDYDXMarketMapProvider), | ||
orchestrator.WithAggregator(aggregator), | ||
} | ||
if updateLocalConfig { | ||
customOrchestratorOps = append(customOrchestratorOps, orchestrator.WithWriteTo(marketCfgPath)) | ||
} | ||
|
||
return customOrchestratorOps, customOracleOpts, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we checking that the default value isn't used if no chain is given here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable is only used if dydx chain is specified otherwise it's ignored at the moment.