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

chore: validate configuration #88

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Changes from all 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
130 changes: 124 additions & 6 deletions cmd/appstate.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package cmd

import (
"fmt"
"os"

"github.com/rs/zerolog"

"cosmossdk.io/log"

"github.com/strangelove-ventures/noble-cctp-relayer/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/noble"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)

const nobleChainName = "noble"

// appState is the modifiable state of the application.
type AppState struct {
Config *types.Config
Expand Down Expand Up @@ -70,18 +75,131 @@ func (a *AppState) loadConfigFile() {
}
a.Logger.Info("Successfully parsed config file", "location", a.ConfigPath)
a.Config = config
a.validateConfig()

err = a.validateConfig()
if err != nil {
a.Logger.Error("Invalid config", "err", err)
os.Exit(1)
}
}

// validateConfig checks the AppState Config for any invalid settings.
func (a *AppState) validateConfig() {
func (a *AppState) validateConfig() error {
// validate chains
for name, cfg := range a.Config.Chains {
// check if chain is noble
if name == nobleChainName {
// validate noble chain
cc := cfg.(*noble.ChainConfig)
err := a.validateChain(
name,
cc.ChainID,
"",
cc.RPC,
"",
cc.BroadcastRetries,
cc.BroadcastRetryInterval,
cc.MinMintAmount,
)
if err != nil {
return err
}
} else {
// validate eth based chains
cc := cfg.(*ethereum.ChainConfig)
err := a.validateChain(
name,
fmt.Sprintf("%d", cc.ChainID),
fmt.Sprintf("%d", cc.Domain),
cc.RPC,
cc.WS,
cc.BroadcastRetries,
cc.BroadcastRetryInterval,
cc.MinMintAmount,
)
if err != nil {
return err
}
}
}

// ensure at least 1 enabled route
if len(a.Config.EnabledRoutes) == 0 {
return fmt.Errorf("at least one route must be enabled in the config")
}

// validate circle api config
err := a.validateCircleConfig()
if err != nil {
return err
}

// validate processor worker count
if a.Config.ProcessorWorkerCount == 0 {
return fmt.Errorf("ProcessorWorkerCount must be greater than zero in the config")
}

return nil
}

// validateChain ensures the chain is configured correctly
func (a *AppState) validateChain(
name string,
chainID string,
domain string,
rpcURL string,
wsURL string,
broadcastRetries int,
broadcastRetryInterval int,
minMintAmount uint64,
) error {
if name == "" {
return fmt.Errorf("chain name must be set in the config")
}

if chainID == "" {
return fmt.Errorf("chainID must be set in the config (chain: %s) (chainID: %s)", name, chainID)
}

// domain is hardcoded to 4 for noble chain
if domain == "" && name != nobleChainName {
return fmt.Errorf("domain must be set in the config (chain: %s) (domain: %s)", name, domain)
}

if rpcURL == "" {
return fmt.Errorf("rpcURL must be set in the config (chain: %s) (rpcURL: %s)", name, rpcURL)
}

// we do not use a websocket for noble
if wsURL == "" && name != nobleChainName {
return fmt.Errorf("wsURL must be set in the config (chain: %s) (wsURL: %s)", name, wsURL)
}

if broadcastRetries <= 0 {
return fmt.Errorf("broadcastRetries must be greater than zero in the config (chain: %s) (broadcastRetries: %d)", name, broadcastRetries)
}

if broadcastRetryInterval <= 0 {
return fmt.Errorf("broadcastRetryInterval must be greater than zero in the config (chain: %s) (broadcastRetryInterval: %d)", name, broadcastRetryInterval)
}

// noble has free minting
if minMintAmount == 0 && name != nobleChainName {
return fmt.Errorf("ETH-based chains must have a minMintAmount greater than zero in the config (chain: %s) (minMintAmount: %d)", name, minMintAmount)
}

return nil
}

// validateCircleConfig ensures the circle api is configured correctly
func (a *AppState) validateCircleConfig() error {
if a.Config.Circle.AttestationBaseURL == "" {
a.Logger.Error("AttestationBaseUrl is required in the config")
os.Exit(1)
return fmt.Errorf("AttestationBaseUrl is required in the config")
}

if a.Config.Circle.FetchRetryInterval == 0 {
a.Logger.Error("FetchRetryInterval must be greater than zero in the config")
os.Exit(1)
return fmt.Errorf("FetchRetryInterval must be greater than zero in the config")
}

return nil
}
Loading