Skip to content
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

[CORE-813] - Add pricefeed config tomls back into the repository #863

Merged
merged 5 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion protocol/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"github.com/dydxprotocol/v4-chain/protocol/daemons/configs"
"io"
"math/big"
"net/http"
Expand Down Expand Up @@ -627,7 +628,7 @@ func New(

// Non-validating full-nodes have no need to run the price daemon.
if !appFlags.NonValidatingFullNode && daemonFlags.Price.Enabled {
exchangeQueryConfig := constants.StaticExchangeQueryConfig
exchangeQueryConfig := configs.ReadExchangeQueryConfigFile(homePath)
clemire marked this conversation as resolved.
Show resolved Hide resolved
// Start pricefeed client for sending prices for the pricefeed server to consume. These prices
// are retrieved via third-party APIs like Binance and then are encoded in-memory and
// periodically sent via gRPC to a shared socket with the server.
clemire marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
28 changes: 28 additions & 0 deletions protocol/cmd/dydxprotocold/cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cmd

import (
"os"

"github.com/cosmos/cosmos-sdk/client"
"github.com/dydxprotocol/v4-chain/protocol/daemons/configs"
"github.com/spf13/cobra"
)

// AddInitCmdPostRunE adds a PostRunE to the `init` subcommand.
func AddInitCmdPostRunE(rootCmd *cobra.Command) {
// Fetch init subcommand.
initCmd, _, err := rootCmd.Find([]string{"init"})
if err != nil {
os.Exit(1)
clemire marked this conversation as resolved.
Show resolved Hide resolved
}

// Add PostRun to configure required setups after `init`.
initCmd.PostRunE = func(cmd *cobra.Command, args []string) error {
// Get home directory.
clientCtx := client.GetClientContextFromCmd(cmd)

// Add default pricefeed exchange config toml file if it does not exist.
configs.WriteDefaultPricefeedExchangeToml(clientCtx.HomeDir)
return nil
}
}
1 change: 1 addition & 0 deletions protocol/cmd/dydxprotocold/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func main() {
rootCmd := cmd.NewRootCmd(option)

cmd.AddTendermintSubcommands(rootCmd)
cmd.AddInitCmdPostRunE(rootCmd)

if err := svrcmd.Execute(rootCmd, app.AppDaemonName, app.DefaultNodeHome); err != nil {
os.Exit(1)
clemire marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
124 changes: 124 additions & 0 deletions protocol/daemons/configs/default_pricefeed_exchange_config.go
Copy link
Contributor Author

@clemire clemire Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following two files were previously removed, and were added back in wholesale with the following caveat: the old ExchangeStartupConfig type was renamed to ExchangeQueryConfig, so I replaced Startup with Query everywhere in this file and the corresponding test file.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package configs

import (
"bytes"
"fmt"
"os"
"path/filepath"
"text/template"

tmos "github.com/cometbft/cometbft/libs/os"
daemonconstants "github.com/dydxprotocol/v4-chain/protocol/daemons/constants"
"github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/client/constants"
"github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/client/types"
"github.com/pelletier/go-toml"
)

// Note: any changes to the comments/variables/mapstructure must be reflected in the appropriate
// struct in daemons/pricefeed/client/static_exchange_startup_config.go.
const (
defaultTomlTemplate = `# This is a TOML config file.
# StaticExchangeStartupConfig represents the mapping of exchanges to the parameters for
# querying from them.
#
# ExchangeId - Unique string identifying an exchange.
#
# IntervalMs - Delays between sending API requests to get exchange market prices - cannot be 0.
#
# TimeoutMs - Max time to wait on an API call to an exchange - cannot be 0.
#
# MaxQueries - Max api calls to get market prices for an exchange to make in a task-loop -
# cannot be 0. For multi-market API exchanges, the behavior will default to 1.{{ range $exchangeId, $element := .}}
[[exchanges]]
ExchangeId = "{{$element.ExchangeId}}"
IntervalMs = {{$element.IntervalMs}}
TimeoutMs = {{$element.TimeoutMs}}
MaxQueries = {{$element.MaxQueries}}{{end}}
`
)

// GenerateDefaultExchangeTomlString creates the toml file string containing the default configs
// for querying each exchange.
func GenerateDefaultExchangeTomlString() bytes.Buffer {
// Create the template for turning each `parsableExchangeStartupConfig` into a toml map config in
// a stringified toml file.
template, err := template.New("").Parse(defaultTomlTemplate)
// Panic if failure occurs when parsing the template.
if err != nil {
panic(err)
}

// Encode toml string into `defaultExchangeToml` and return if successful. Otherwise, panic.
var defaultExchangeToml bytes.Buffer
err = template.Execute(&defaultExchangeToml, constants.StaticExchangeQueryConfig)
if err != nil {
panic(err)
}
return defaultExchangeToml
clemire marked this conversation as resolved.
Show resolved Hide resolved
}

// WriteDefaultPricefeedExchangeToml reads in the toml string for the pricefeed client and
// writes said string to the config folder as a toml file if the config file does not exist.
func WriteDefaultPricefeedExchangeToml(homeDir string) {
// Write file into config folder if file does not exist.
configFilePath := getConfigFilePath(homeDir)
if !tmos.FileExists(configFilePath) {
buffer := GenerateDefaultExchangeTomlString()
tmos.MustWriteFile(configFilePath, buffer.Bytes(), 0644)
}
clemire marked this conversation as resolved.
Show resolved Hide resolved
}

// ReadExchangeQueryConfigFile gets a mapping of `exchangeIds` to `ExchangeQueryConfigs`
// where `ExchangeQueryConfig` for querying exchanges for market prices comes from parsing a TOML
// file in the config directory.
// NOTE: if the config file is not found for the price-daemon, return the static exchange query
// config.
func ReadExchangeQueryConfigFile(homeDir string) map[types.ExchangeId]*types.ExchangeQueryConfig {
// Read file for exchange query configurations.
tomlFile, err := os.ReadFile(getConfigFilePath(homeDir))
if err != nil {
panic(err)
}

// Unmarshal `tomlFile` into `exchanges` for `exchangeStartupConfigMap`.
exchanges := map[string][]types.ExchangeQueryConfig{}
if err = toml.Unmarshal(tomlFile, &exchanges); err != nil {
panic(err)
}

// Populate configs for exchanges.
exchangeStartupConfigMap := make(map[types.ExchangeId]*types.ExchangeQueryConfig, len(exchanges))
for _, exchange := range exchanges["exchanges"] {
// Zero is an invalid configuration value for all parameters. This could also point to the
// configuration file being setup wrong with one or more exchange parameters unset.
if exchange.IntervalMs == 0 ||
exchange.TimeoutMs == 0 ||
exchange.MaxQueries == 0 {
panic(
fmt.Errorf(
"One or more query config values are unset or are set to zero for exchange with id: '%v'",
exchange.ExchangeId,
),
)
}

// Insert Key-Value pair into `exchangeStartupConfigMap`.
exchangeStartupConfigMap[exchange.ExchangeId] = &types.ExchangeQueryConfig{
ExchangeId: exchange.ExchangeId,
IntervalMs: exchange.IntervalMs,
TimeoutMs: exchange.TimeoutMs,
MaxQueries: exchange.MaxQueries,
}
}

return exchangeStartupConfigMap
Comment on lines +71 to +114
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ReadExchangeQueryConfigFile function should not panic when encountering an error. Instead, it should return an error and allow the caller to handle it. This would improve error handling and make the code more robust and maintainable.

- func ReadExchangeQueryConfigFile(homeDir string) map[types.ExchangeId]*types.ExchangeQueryConfig {
+ func ReadExchangeQueryConfigFile(homeDir string) (map[types.ExchangeId]*types.ExchangeQueryConfig, error) {
  ...
-   panic(err)
+   return nil, err
  ...
-   panic(err)
+   return nil, err
  ...
-   panic(
+   return nil, fmt.Errorf(
  ...
-   )
+   )
  ...
-   return exchangeStartupConfigMap
+   return exchangeStartupConfigMap, nil
}

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
// ReadExchangeQueryConfigFile gets a mapping of `exchangeIds` to `ExchangeQueryConfigs`
// where `ExchangeQueryConfig` for querying exchanges for market prices comes from parsing a TOML
// file in the config directory.
// NOTE: if the config file is not found for the price-daemon, return the static exchange query
// config.
func ReadExchangeQueryConfigFile(homeDir string) map[types.ExchangeId]*types.ExchangeQueryConfig {
// Read file for exchange query configurations.
tomlFile, err := os.ReadFile(getConfigFilePath(homeDir))
if err != nil {
panic(err)
}
// Unmarshal `tomlFile` into `exchanges` for `exchangeStartupConfigMap`.
exchanges := map[string][]types.ExchangeQueryConfig{}
if err = toml.Unmarshal(tomlFile, &exchanges); err != nil {
panic(err)
}
// Populate configs for exchanges.
exchangeStartupConfigMap := make(map[types.ExchangeId]*types.ExchangeQueryConfig, len(exchanges))
for _, exchange := range exchanges["exchanges"] {
// Zero is an invalid configuration value for all parameters. This could also point to the
// configuration file being setup wrong with one or more exchange parameters unset.
if exchange.IntervalMs == 0 ||
exchange.TimeoutMs == 0 ||
exchange.MaxQueries == 0 {
panic(
fmt.Errorf(
"One or more query config values are unset or are set to zero for exchange with id: '%v'",
exchange.ExchangeId,
),
)
}
// Insert Key-Value pair into `exchangeStartupConfigMap`.
exchangeStartupConfigMap[exchange.ExchangeId] = &types.ExchangeQueryConfig{
ExchangeId: exchange.ExchangeId,
IntervalMs: exchange.IntervalMs,
TimeoutMs: exchange.TimeoutMs,
MaxQueries: exchange.MaxQueries,
}
}
return exchangeStartupConfigMap
// ReadExchangeQueryConfigFile gets a mapping of `exchangeIds` to `ExchangeQueryConfigs`
// where `ExchangeQueryConfig` for querying exchanges for market prices comes from parsing a TOML
// file in the config directory.
// NOTE: if the config file is not found for the price-daemon, return the static exchange query
// config.
func ReadExchangeQueryConfigFile(homeDir string) (map[types.ExchangeId]*types.ExchangeQueryConfig, error) {
// Read file for exchange query configurations.
tomlFile, err := os.ReadFile(getConfigFilePath(homeDir))
if err != nil {
return nil, err
}
// Unmarshal `tomlFile` into `exchanges` for `exchangeStartupConfigMap`.
exchanges := map[string][]types.ExchangeQueryConfig{}
if err = toml.Unmarshal(tomlFile, &exchanges); err != nil {
return nil, err
}
// Populate configs for exchanges.
exchangeStartupConfigMap := make(map[types.ExchangeId]*types.ExchangeQueryConfig, len(exchanges))
for _, exchange := range exchanges["exchanges"] {
// Zero is an invalid configuration value for all parameters. This could also point to the
// configuration file being setup wrong with one or more exchange parameters unset.
if exchange.IntervalMs == 0 ||
exchange.TimeoutMs == 0 ||
exchange.MaxQueries == 0 {
return nil, fmt.Errorf(
"One or more query config values are unset or are set to zero for exchange with id: '%v'",
exchange.ExchangeId,
)
}
// Insert Key-Value pair into `exchangeStartupConfigMap`.
exchangeStartupConfigMap[exchange.ExchangeId] = &types.ExchangeQueryConfig{
ExchangeId: exchange.ExchangeId,
IntervalMs: exchange.IntervalMs,
TimeoutMs: exchange.TimeoutMs,
MaxQueries: exchange.MaxQueries,
}
}
return exchangeStartupConfigMap, nil
}

}

// getConfigFilePath returns the path to the pricefeed exchange config file.
func getConfigFilePath(homeDir string) string {
return filepath.Join(
homeDir,
"config",
daemonconstants.PricefeedExchangeConfigFileName,
)
}
Loading