github.com/MetalBlockchain/metalgo@v1.11.9/config/config.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package config
     5  
     6  import (
     7  	"crypto/tls"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io/fs"
    13  	"math"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/spf13/viper"
    20  
    21  	"github.com/MetalBlockchain/metalgo/api/server"
    22  	"github.com/MetalBlockchain/metalgo/chains"
    23  	"github.com/MetalBlockchain/metalgo/genesis"
    24  	"github.com/MetalBlockchain/metalgo/ids"
    25  	"github.com/MetalBlockchain/metalgo/network"
    26  	"github.com/MetalBlockchain/metalgo/network/dialer"
    27  	"github.com/MetalBlockchain/metalgo/network/throttling"
    28  	"github.com/MetalBlockchain/metalgo/node"
    29  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowball"
    30  	"github.com/MetalBlockchain/metalgo/snow/networking/benchlist"
    31  	"github.com/MetalBlockchain/metalgo/snow/networking/router"
    32  	"github.com/MetalBlockchain/metalgo/snow/networking/tracker"
    33  	"github.com/MetalBlockchain/metalgo/staking"
    34  	"github.com/MetalBlockchain/metalgo/subnets"
    35  	"github.com/MetalBlockchain/metalgo/trace"
    36  	"github.com/MetalBlockchain/metalgo/utils/compression"
    37  	"github.com/MetalBlockchain/metalgo/utils/constants"
    38  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    39  	"github.com/MetalBlockchain/metalgo/utils/ips"
    40  	"github.com/MetalBlockchain/metalgo/utils/logging"
    41  	"github.com/MetalBlockchain/metalgo/utils/perms"
    42  	"github.com/MetalBlockchain/metalgo/utils/profiler"
    43  	"github.com/MetalBlockchain/metalgo/utils/set"
    44  	"github.com/MetalBlockchain/metalgo/utils/storage"
    45  	"github.com/MetalBlockchain/metalgo/utils/timer"
    46  	"github.com/MetalBlockchain/metalgo/version"
    47  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    48  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/fee"
    49  	"github.com/MetalBlockchain/metalgo/vms/proposervm"
    50  )
    51  
    52  const (
    53  	chainConfigFileName  = "config"
    54  	chainUpgradeFileName = "upgrade"
    55  	subnetConfigFileExt  = ".json"
    56  
    57  	keystoreDeprecationMsg = "keystore API is deprecated"
    58  )
    59  
    60  var (
    61  	// Deprecated key --> deprecation message (i.e. which key replaces it)
    62  	// TODO: deprecate "BootstrapIDsKey" and "BootstrapIPsKey"
    63  	deprecatedKeys = map[string]string{
    64  		KeystoreAPIEnabledKey: keystoreDeprecationMsg,
    65  	}
    66  
    67  	errConflictingACPOpinion                  = errors.New("supporting and objecting to the same ACP")
    68  	errConflictingImplicitACPOpinion          = errors.New("objecting to enabled ACP")
    69  	errSybilProtectionDisabledStakerWeights   = errors.New("sybil protection disabled weights must be positive")
    70  	errSybilProtectionDisabledOnPublicNetwork = errors.New("sybil protection disabled on public network")
    71  	errInvalidUptimeRequirement               = errors.New("uptime requirement must be in the range [0, 1]")
    72  	errMinValidatorStakeAboveMax              = errors.New("minimum validator stake can't be greater than maximum validator stake")
    73  	errInvalidDelegationFee                   = errors.New("delegation fee must be in the range [0, 1,000,000]")
    74  	errInvalidMinStakeDuration                = errors.New("min stake duration must be > 0")
    75  	errMinStakeDurationAboveMax               = errors.New("max stake duration can't be less than min stake duration")
    76  	errStakeMaxConsumptionTooLarge            = fmt.Errorf("max stake consumption must be less than or equal to %d", reward.PercentDenominator)
    77  	errStakeMaxConsumptionBelowMin            = errors.New("stake max consumption can't be less than min stake consumption")
    78  	errStakeMintingPeriodBelowMin             = errors.New("stake minting period can't be less than max stake duration")
    79  	errCannotTrackPrimaryNetwork              = errors.New("cannot track primary network")
    80  	errStakingKeyContentUnset                 = fmt.Errorf("%s key not set but %s set", StakingTLSKeyContentKey, StakingCertContentKey)
    81  	errStakingCertContentUnset                = fmt.Errorf("%s key set but %s not set", StakingTLSKeyContentKey, StakingCertContentKey)
    82  	errMissingStakingSigningKeyFile           = errors.New("missing staking signing key file")
    83  	errTracingEndpointEmpty                   = fmt.Errorf("%s cannot be empty", TracingEndpointKey)
    84  	errPluginDirNotADirectory                 = errors.New("plugin dir is not a directory")
    85  	errCannotReadDirectory                    = errors.New("cannot read directory")
    86  	errUnmarshalling                          = errors.New("unmarshalling failed")
    87  	errFileDoesNotExist                       = errors.New("file does not exist")
    88  )
    89  
    90  func getConsensusConfig(v *viper.Viper) snowball.Parameters {
    91  	p := snowball.Parameters{
    92  		K:                     v.GetInt(SnowSampleSizeKey),
    93  		AlphaPreference:       v.GetInt(SnowPreferenceQuorumSizeKey),
    94  		AlphaConfidence:       v.GetInt(SnowConfidenceQuorumSizeKey),
    95  		Beta:                  v.GetInt(SnowCommitThresholdKey),
    96  		ConcurrentRepolls:     v.GetInt(SnowConcurrentRepollsKey),
    97  		OptimalProcessing:     v.GetInt(SnowOptimalProcessingKey),
    98  		MaxOutstandingItems:   v.GetInt(SnowMaxProcessingKey),
    99  		MaxItemProcessingTime: v.GetDuration(SnowMaxTimeProcessingKey),
   100  	}
   101  	if v.IsSet(SnowQuorumSizeKey) {
   102  		p.AlphaPreference = v.GetInt(SnowQuorumSizeKey)
   103  		p.AlphaConfidence = p.AlphaPreference
   104  	}
   105  	return p
   106  }
   107  
   108  func getLoggingConfig(v *viper.Viper) (logging.Config, error) {
   109  	loggingConfig := logging.Config{}
   110  	loggingConfig.Directory = GetExpandedArg(v, LogsDirKey)
   111  	var err error
   112  	loggingConfig.LogLevel, err = logging.ToLevel(v.GetString(LogLevelKey))
   113  	if err != nil {
   114  		return loggingConfig, err
   115  	}
   116  	logDisplayLevel := v.GetString(LogLevelKey)
   117  	if v.IsSet(LogDisplayLevelKey) {
   118  		logDisplayLevel = v.GetString(LogDisplayLevelKey)
   119  	}
   120  	loggingConfig.DisplayLevel, err = logging.ToLevel(logDisplayLevel)
   121  	if err != nil {
   122  		return loggingConfig, err
   123  	}
   124  	loggingConfig.LogFormat, err = logging.ToFormat(v.GetString(LogFormatKey), os.Stdout.Fd())
   125  	loggingConfig.DisableWriterDisplaying = v.GetBool(LogDisableDisplayPluginLogsKey)
   126  	loggingConfig.MaxSize = int(v.GetUint(LogRotaterMaxSizeKey))
   127  	loggingConfig.MaxFiles = int(v.GetUint(LogRotaterMaxFilesKey))
   128  	loggingConfig.MaxAge = int(v.GetUint(LogRotaterMaxAgeKey))
   129  	loggingConfig.Compress = v.GetBool(LogRotaterCompressEnabledKey)
   130  
   131  	return loggingConfig, err
   132  }
   133  
   134  func getHTTPConfig(v *viper.Viper) (node.HTTPConfig, error) {
   135  	var (
   136  		httpsKey  []byte
   137  		httpsCert []byte
   138  		err       error
   139  	)
   140  	switch {
   141  	case v.IsSet(HTTPSKeyContentKey):
   142  		rawContent := v.GetString(HTTPSKeyContentKey)
   143  		httpsKey, err = base64.StdEncoding.DecodeString(rawContent)
   144  		if err != nil {
   145  			return node.HTTPConfig{}, fmt.Errorf("unable to decode base64 content: %w", err)
   146  		}
   147  	case v.IsSet(HTTPSKeyFileKey):
   148  		httpsKeyFilepath := GetExpandedArg(v, HTTPSKeyFileKey)
   149  		httpsKey, err = os.ReadFile(filepath.Clean(httpsKeyFilepath))
   150  		if err != nil {
   151  			return node.HTTPConfig{}, err
   152  		}
   153  	}
   154  
   155  	switch {
   156  	case v.IsSet(HTTPSCertContentKey):
   157  		rawContent := v.GetString(HTTPSCertContentKey)
   158  		httpsCert, err = base64.StdEncoding.DecodeString(rawContent)
   159  		if err != nil {
   160  			return node.HTTPConfig{}, fmt.Errorf("unable to decode base64 content: %w", err)
   161  		}
   162  	case v.IsSet(HTTPSCertFileKey):
   163  		httpsCertFilepath := GetExpandedArg(v, HTTPSCertFileKey)
   164  		httpsCert, err = os.ReadFile(filepath.Clean(httpsCertFilepath))
   165  		if err != nil {
   166  			return node.HTTPConfig{}, err
   167  		}
   168  	}
   169  
   170  	return node.HTTPConfig{
   171  		HTTPConfig: server.HTTPConfig{
   172  			ReadTimeout:       v.GetDuration(HTTPReadTimeoutKey),
   173  			ReadHeaderTimeout: v.GetDuration(HTTPReadHeaderTimeoutKey),
   174  			WriteTimeout:      v.GetDuration(HTTPWriteTimeoutKey),
   175  			IdleTimeout:       v.GetDuration(HTTPIdleTimeoutKey),
   176  		},
   177  		APIConfig: node.APIConfig{
   178  			APIIndexerConfig: node.APIIndexerConfig{
   179  				IndexAPIEnabled:      v.GetBool(IndexEnabledKey),
   180  				IndexAllowIncomplete: v.GetBool(IndexAllowIncompleteKey),
   181  			},
   182  			AdminAPIEnabled:    v.GetBool(AdminAPIEnabledKey),
   183  			InfoAPIEnabled:     v.GetBool(InfoAPIEnabledKey),
   184  			KeystoreAPIEnabled: v.GetBool(KeystoreAPIEnabledKey),
   185  			MetricsAPIEnabled:  v.GetBool(MetricsAPIEnabledKey),
   186  			HealthAPIEnabled:   v.GetBool(HealthAPIEnabledKey),
   187  		},
   188  		HTTPHost:           v.GetString(HTTPHostKey),
   189  		HTTPPort:           uint16(v.GetUint(HTTPPortKey)),
   190  		HTTPSEnabled:       v.GetBool(HTTPSEnabledKey),
   191  		HTTPSKey:           httpsKey,
   192  		HTTPSCert:          httpsCert,
   193  		HTTPAllowedOrigins: v.GetStringSlice(HTTPAllowedOrigins),
   194  		HTTPAllowedHosts:   v.GetStringSlice(HTTPAllowedHostsKey),
   195  		ShutdownTimeout:    v.GetDuration(HTTPShutdownTimeoutKey),
   196  		ShutdownWait:       v.GetDuration(HTTPShutdownWaitKey),
   197  	}, nil
   198  }
   199  
   200  func getRouterHealthConfig(v *viper.Viper, halflife time.Duration) (router.HealthConfig, error) {
   201  	config := router.HealthConfig{
   202  		MaxDropRate:            v.GetFloat64(RouterHealthMaxDropRateKey),
   203  		MaxOutstandingRequests: int(v.GetUint(RouterHealthMaxOutstandingRequestsKey)),
   204  		MaxOutstandingDuration: v.GetDuration(NetworkHealthMaxOutstandingDurationKey),
   205  		MaxRunTimeRequests:     v.GetDuration(NetworkMaximumTimeoutKey),
   206  		MaxDropRateHalflife:    halflife,
   207  	}
   208  	switch {
   209  	case config.MaxDropRate < 0 || config.MaxDropRate > 1:
   210  		return router.HealthConfig{}, fmt.Errorf("%q must be in [0,1]", RouterHealthMaxDropRateKey)
   211  	case config.MaxOutstandingDuration <= 0:
   212  		return router.HealthConfig{}, fmt.Errorf("%q must be positive", NetworkHealthMaxOutstandingDurationKey)
   213  	case config.MaxRunTimeRequests <= 0:
   214  		return router.HealthConfig{}, fmt.Errorf("%q must be positive", NetworkMaximumTimeoutKey)
   215  	}
   216  	return config, nil
   217  }
   218  
   219  func getAdaptiveTimeoutConfig(v *viper.Viper) (timer.AdaptiveTimeoutConfig, error) {
   220  	config := timer.AdaptiveTimeoutConfig{
   221  		InitialTimeout:     v.GetDuration(NetworkInitialTimeoutKey),
   222  		MinimumTimeout:     v.GetDuration(NetworkMinimumTimeoutKey),
   223  		MaximumTimeout:     v.GetDuration(NetworkMaximumTimeoutKey),
   224  		TimeoutHalflife:    v.GetDuration(NetworkTimeoutHalflifeKey),
   225  		TimeoutCoefficient: v.GetFloat64(NetworkTimeoutCoefficientKey),
   226  	}
   227  	switch {
   228  	case config.MinimumTimeout < 1:
   229  		return timer.AdaptiveTimeoutConfig{}, fmt.Errorf("%q must be positive", NetworkMinimumTimeoutKey)
   230  	case config.MinimumTimeout > config.MaximumTimeout:
   231  		return timer.AdaptiveTimeoutConfig{}, fmt.Errorf("%q must be >= %q", NetworkMaximumTimeoutKey, NetworkMinimumTimeoutKey)
   232  	case config.InitialTimeout < config.MinimumTimeout || config.InitialTimeout > config.MaximumTimeout:
   233  		return timer.AdaptiveTimeoutConfig{}, fmt.Errorf("%q must be in [%q, %q]", NetworkInitialTimeoutKey, NetworkMinimumTimeoutKey, NetworkMaximumTimeoutKey)
   234  	case config.TimeoutHalflife <= 0:
   235  		return timer.AdaptiveTimeoutConfig{}, fmt.Errorf("%q must > 0", NetworkTimeoutHalflifeKey)
   236  	case config.TimeoutCoefficient < 1:
   237  		return timer.AdaptiveTimeoutConfig{}, fmt.Errorf("%q must be >= 1", NetworkTimeoutCoefficientKey)
   238  	}
   239  
   240  	return config, nil
   241  }
   242  
   243  func getNetworkConfig(
   244  	v *viper.Viper,
   245  	networkID uint32,
   246  	sybilProtectionEnabled bool,
   247  	halflife time.Duration,
   248  ) (network.Config, error) {
   249  	// Set the max number of recent inbound connections upgraded to be
   250  	// equal to the max number of inbound connections per second.
   251  	maxInboundConnsPerSec := v.GetFloat64(NetworkInboundThrottlerMaxConnsPerSecKey)
   252  	upgradeCooldown := v.GetDuration(NetworkInboundConnUpgradeThrottlerCooldownKey)
   253  	upgradeCooldownInSeconds := upgradeCooldown.Seconds()
   254  	maxRecentConnsUpgraded := int(math.Ceil(maxInboundConnsPerSec * upgradeCooldownInSeconds))
   255  
   256  	compressionType, err := compression.TypeFromString(v.GetString(NetworkCompressionTypeKey))
   257  	if err != nil {
   258  		return network.Config{}, err
   259  	}
   260  
   261  	allowPrivateIPs := !constants.ProductionNetworkIDs.Contains(networkID)
   262  	if v.IsSet(NetworkAllowPrivateIPsKey) {
   263  		allowPrivateIPs = v.GetBool(NetworkAllowPrivateIPsKey)
   264  	}
   265  
   266  	var supportedACPs set.Set[uint32]
   267  	for _, acp := range v.GetIntSlice(ACPSupportKey) {
   268  		if acp < 0 || acp > math.MaxInt32 {
   269  			return network.Config{}, fmt.Errorf("invalid ACP: %d", acp)
   270  		}
   271  		supportedACPs.Add(uint32(acp))
   272  	}
   273  
   274  	var objectedACPs set.Set[uint32]
   275  	for _, acp := range v.GetIntSlice(ACPObjectKey) {
   276  		if acp < 0 || acp > math.MaxInt32 {
   277  			return network.Config{}, fmt.Errorf("invalid ACP: %d", acp)
   278  		}
   279  		objectedACPs.Add(uint32(acp))
   280  	}
   281  	if supportedACPs.Overlaps(objectedACPs) {
   282  		return network.Config{}, errConflictingACPOpinion
   283  	}
   284  	if constants.ScheduledACPs.Overlaps(objectedACPs) {
   285  		return network.Config{}, errConflictingImplicitACPOpinion
   286  	}
   287  
   288  	// Because this node version has scheduled these ACPs, we should notify
   289  	// peers that we support these upgrades.
   290  	supportedACPs.Union(constants.ScheduledACPs)
   291  
   292  	// To decrease unnecessary network traffic, peers will not be notified of
   293  	// objection or support of activated ACPs.
   294  	supportedACPs.Difference(constants.ActivatedACPs)
   295  	objectedACPs.Difference(constants.ActivatedACPs)
   296  
   297  	config := network.Config{
   298  		ThrottlerConfig: network.ThrottlerConfig{
   299  			MaxInboundConnsPerSec: maxInboundConnsPerSec,
   300  			InboundConnUpgradeThrottlerConfig: throttling.InboundConnUpgradeThrottlerConfig{
   301  				UpgradeCooldown:        upgradeCooldown,
   302  				MaxRecentConnsUpgraded: maxRecentConnsUpgraded,
   303  			},
   304  
   305  			InboundMsgThrottlerConfig: throttling.InboundMsgThrottlerConfig{
   306  				MsgByteThrottlerConfig: throttling.MsgByteThrottlerConfig{
   307  					AtLargeAllocSize:    v.GetUint64(InboundThrottlerAtLargeAllocSizeKey),
   308  					VdrAllocSize:        v.GetUint64(InboundThrottlerVdrAllocSizeKey),
   309  					NodeMaxAtLargeBytes: v.GetUint64(InboundThrottlerNodeMaxAtLargeBytesKey),
   310  				},
   311  				BandwidthThrottlerConfig: throttling.BandwidthThrottlerConfig{
   312  					RefillRate:   v.GetUint64(InboundThrottlerBandwidthRefillRateKey),
   313  					MaxBurstSize: v.GetUint64(InboundThrottlerBandwidthMaxBurstSizeKey),
   314  				},
   315  				MaxProcessingMsgsPerNode: v.GetUint64(InboundThrottlerMaxProcessingMsgsPerNodeKey),
   316  				CPUThrottlerConfig: throttling.SystemThrottlerConfig{
   317  					MaxRecheckDelay: v.GetDuration(InboundThrottlerCPUMaxRecheckDelayKey),
   318  				},
   319  				DiskThrottlerConfig: throttling.SystemThrottlerConfig{
   320  					MaxRecheckDelay: v.GetDuration(InboundThrottlerDiskMaxRecheckDelayKey),
   321  				},
   322  			},
   323  
   324  			OutboundMsgThrottlerConfig: throttling.MsgByteThrottlerConfig{
   325  				AtLargeAllocSize:    v.GetUint64(OutboundThrottlerAtLargeAllocSizeKey),
   326  				VdrAllocSize:        v.GetUint64(OutboundThrottlerVdrAllocSizeKey),
   327  				NodeMaxAtLargeBytes: v.GetUint64(OutboundThrottlerNodeMaxAtLargeBytesKey),
   328  			},
   329  		},
   330  
   331  		HealthConfig: network.HealthConfig{
   332  			Enabled:                      sybilProtectionEnabled,
   333  			MaxTimeSinceMsgSent:          v.GetDuration(NetworkHealthMaxTimeSinceMsgSentKey),
   334  			MaxTimeSinceMsgReceived:      v.GetDuration(NetworkHealthMaxTimeSinceMsgReceivedKey),
   335  			MaxPortionSendQueueBytesFull: v.GetFloat64(NetworkHealthMaxPortionSendQueueFillKey),
   336  			MinConnectedPeers:            v.GetUint(NetworkHealthMinPeersKey),
   337  			MaxSendFailRate:              v.GetFloat64(NetworkHealthMaxSendFailRateKey),
   338  			SendFailRateHalflife:         halflife,
   339  		},
   340  
   341  		ProxyEnabled:           v.GetBool(NetworkTCPProxyEnabledKey),
   342  		ProxyReadHeaderTimeout: v.GetDuration(NetworkTCPProxyReadTimeoutKey),
   343  
   344  		DialerConfig: dialer.Config{
   345  			ThrottleRps:       v.GetUint32(NetworkOutboundConnectionThrottlingRpsKey),
   346  			ConnectionTimeout: v.GetDuration(NetworkOutboundConnectionTimeoutKey),
   347  		},
   348  
   349  		TLSKeyLogFile: v.GetString(NetworkTLSKeyLogFileKey),
   350  
   351  		TimeoutConfig: network.TimeoutConfig{
   352  			PingPongTimeout:      v.GetDuration(NetworkPingTimeoutKey),
   353  			ReadHandshakeTimeout: v.GetDuration(NetworkReadHandshakeTimeoutKey),
   354  		},
   355  
   356  		PeerListGossipConfig: network.PeerListGossipConfig{
   357  			PeerListNumValidatorIPs: v.GetUint32(NetworkPeerListNumValidatorIPsKey),
   358  			PeerListPullGossipFreq:  v.GetDuration(NetworkPeerListPullGossipFreqKey),
   359  			PeerListBloomResetFreq:  v.GetDuration(NetworkPeerListBloomResetFreqKey),
   360  		},
   361  
   362  		DelayConfig: network.DelayConfig{
   363  			MaxReconnectDelay:     v.GetDuration(NetworkMaxReconnectDelayKey),
   364  			InitialReconnectDelay: v.GetDuration(NetworkInitialReconnectDelayKey),
   365  		},
   366  
   367  		MaxClockDifference:           v.GetDuration(NetworkMaxClockDifferenceKey),
   368  		CompressionType:              compressionType,
   369  		PingFrequency:                v.GetDuration(NetworkPingFrequencyKey),
   370  		AllowPrivateIPs:              allowPrivateIPs,
   371  		UptimeMetricFreq:             v.GetDuration(UptimeMetricFreqKey),
   372  		MaximumInboundMessageTimeout: v.GetDuration(NetworkMaximumInboundTimeoutKey),
   373  
   374  		SupportedACPs: supportedACPs,
   375  		ObjectedACPs:  objectedACPs,
   376  
   377  		RequireValidatorToConnect: v.GetBool(NetworkRequireValidatorToConnectKey),
   378  		PeerReadBufferSize:        int(v.GetUint(NetworkPeerReadBufferSizeKey)),
   379  		PeerWriteBufferSize:       int(v.GetUint(NetworkPeerWriteBufferSizeKey)),
   380  	}
   381  
   382  	switch {
   383  	case config.HealthConfig.MaxTimeSinceMsgSent < 0:
   384  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkHealthMaxTimeSinceMsgSentKey)
   385  	case config.HealthConfig.MaxTimeSinceMsgReceived < 0:
   386  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkHealthMaxTimeSinceMsgReceivedKey)
   387  	case config.HealthConfig.MaxSendFailRate < 0 || config.HealthConfig.MaxSendFailRate > 1:
   388  		return network.Config{}, fmt.Errorf("%s must be in [0,1]", NetworkHealthMaxSendFailRateKey)
   389  	case config.HealthConfig.MaxPortionSendQueueBytesFull < 0 || config.HealthConfig.MaxPortionSendQueueBytesFull > 1:
   390  		return network.Config{}, fmt.Errorf("%s must be in [0,1]", NetworkHealthMaxPortionSendQueueFillKey)
   391  	case config.DialerConfig.ConnectionTimeout < 0:
   392  		return network.Config{}, fmt.Errorf("%q must be >= 0", NetworkOutboundConnectionTimeoutKey)
   393  	case config.PeerListPullGossipFreq < 0:
   394  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkPeerListPullGossipFreqKey)
   395  	case config.PeerListBloomResetFreq < 0:
   396  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkPeerListBloomResetFreqKey)
   397  	case config.ThrottlerConfig.InboundMsgThrottlerConfig.CPUThrottlerConfig.MaxRecheckDelay < constants.MinInboundThrottlerMaxRecheckDelay:
   398  		return network.Config{}, fmt.Errorf("%s must be >= %d", InboundThrottlerCPUMaxRecheckDelayKey, constants.MinInboundThrottlerMaxRecheckDelay)
   399  	case config.ThrottlerConfig.InboundMsgThrottlerConfig.DiskThrottlerConfig.MaxRecheckDelay < constants.MinInboundThrottlerMaxRecheckDelay:
   400  		return network.Config{}, fmt.Errorf("%s must be >= %d", InboundThrottlerDiskMaxRecheckDelayKey, constants.MinInboundThrottlerMaxRecheckDelay)
   401  	case config.MaxReconnectDelay < 0:
   402  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkMaxReconnectDelayKey)
   403  	case config.InitialReconnectDelay < 0:
   404  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkInitialReconnectDelayKey)
   405  	case config.MaxReconnectDelay < config.InitialReconnectDelay:
   406  		return network.Config{}, fmt.Errorf("%s must be >= %s", NetworkMaxReconnectDelayKey, NetworkInitialReconnectDelayKey)
   407  	case config.PingPongTimeout < 0:
   408  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkPingTimeoutKey)
   409  	case config.PingFrequency < 0:
   410  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkPingFrequencyKey)
   411  	case config.PingPongTimeout <= config.PingFrequency:
   412  		return network.Config{}, fmt.Errorf("%s must be > %s", NetworkPingTimeoutKey, NetworkPingFrequencyKey)
   413  	case config.ReadHandshakeTimeout < 0:
   414  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkReadHandshakeTimeoutKey)
   415  	case config.MaxClockDifference < 0:
   416  		return network.Config{}, fmt.Errorf("%s must be >= 0", NetworkMaxClockDifferenceKey)
   417  	}
   418  	return config, nil
   419  }
   420  
   421  func getBenchlistConfig(v *viper.Viper, consensusParameters snowball.Parameters) (benchlist.Config, error) {
   422  	// AlphaConfidence is used here to ensure that benching can't cause a
   423  	// liveness failure. If AlphaPreference were used, the benchlist may grow to
   424  	// a point that committing would be extremely unlikely to happen.
   425  	alpha := consensusParameters.AlphaConfidence
   426  	k := consensusParameters.K
   427  	config := benchlist.Config{
   428  		Threshold:              v.GetInt(BenchlistFailThresholdKey),
   429  		Duration:               v.GetDuration(BenchlistDurationKey),
   430  		MinimumFailingDuration: v.GetDuration(BenchlistMinFailingDurationKey),
   431  		MaxPortion:             (1.0 - (float64(alpha) / float64(k))) / 3.0,
   432  	}
   433  	switch {
   434  	case config.Duration < 0:
   435  		return benchlist.Config{}, fmt.Errorf("%q must be >= 0", BenchlistDurationKey)
   436  	case config.MinimumFailingDuration < 0:
   437  		return benchlist.Config{}, fmt.Errorf("%q must be >= 0", BenchlistMinFailingDurationKey)
   438  	}
   439  	return config, nil
   440  }
   441  
   442  func getStateSyncConfig(v *viper.Viper) (node.StateSyncConfig, error) {
   443  	var (
   444  		config       = node.StateSyncConfig{}
   445  		stateSyncIPs = strings.Split(v.GetString(StateSyncIPsKey), ",")
   446  		stateSyncIDs = strings.Split(v.GetString(StateSyncIDsKey), ",")
   447  	)
   448  
   449  	for _, ip := range stateSyncIPs {
   450  		if ip == "" {
   451  			continue
   452  		}
   453  		addr, err := ips.ParseAddrPort(ip)
   454  		if err != nil {
   455  			return node.StateSyncConfig{}, fmt.Errorf("couldn't parse state sync ip %s: %w", ip, err)
   456  		}
   457  		config.StateSyncIPs = append(config.StateSyncIPs, addr)
   458  	}
   459  
   460  	for _, id := range stateSyncIDs {
   461  		if id == "" {
   462  			continue
   463  		}
   464  		nodeID, err := ids.NodeIDFromString(id)
   465  		if err != nil {
   466  			return node.StateSyncConfig{}, fmt.Errorf("couldn't parse state sync peer id %s: %w", id, err)
   467  		}
   468  		config.StateSyncIDs = append(config.StateSyncIDs, nodeID)
   469  	}
   470  
   471  	lenIPs := len(config.StateSyncIPs)
   472  	lenIDs := len(config.StateSyncIDs)
   473  	if lenIPs != lenIDs {
   474  		return node.StateSyncConfig{}, fmt.Errorf("expected the number of stateSyncIPs (%d) to match the number of stateSyncIDs (%d)", lenIPs, lenIDs)
   475  	}
   476  
   477  	return config, nil
   478  }
   479  
   480  func getBootstrapConfig(v *viper.Viper, networkID uint32) (node.BootstrapConfig, error) {
   481  	config := node.BootstrapConfig{
   482  		BootstrapBeaconConnectionTimeout:        v.GetDuration(BootstrapBeaconConnectionTimeoutKey),
   483  		BootstrapMaxTimeGetAncestors:            v.GetDuration(BootstrapMaxTimeGetAncestorsKey),
   484  		BootstrapAncestorsMaxContainersSent:     int(v.GetUint(BootstrapAncestorsMaxContainersSentKey)),
   485  		BootstrapAncestorsMaxContainersReceived: int(v.GetUint(BootstrapAncestorsMaxContainersReceivedKey)),
   486  	}
   487  
   488  	// TODO: Add a "BootstrappersKey" flag to more clearly enforce ID and IP
   489  	// length equality.
   490  	ipsSet := v.IsSet(BootstrapIPsKey)
   491  	idsSet := v.IsSet(BootstrapIDsKey)
   492  	if ipsSet && !idsSet {
   493  		return node.BootstrapConfig{}, fmt.Errorf("set %q but didn't set %q", BootstrapIPsKey, BootstrapIDsKey)
   494  	}
   495  	if !ipsSet && idsSet {
   496  		return node.BootstrapConfig{}, fmt.Errorf("set %q but didn't set %q", BootstrapIDsKey, BootstrapIPsKey)
   497  	}
   498  	if !ipsSet && !idsSet {
   499  		config.Bootstrappers = genesis.SampleBootstrappers(networkID, 5)
   500  		return config, nil
   501  	}
   502  
   503  	bootstrapIPs := strings.Split(v.GetString(BootstrapIPsKey), ",")
   504  	config.Bootstrappers = make([]genesis.Bootstrapper, 0, len(bootstrapIPs))
   505  	for _, bootstrapIP := range bootstrapIPs {
   506  		ip := strings.TrimSpace(bootstrapIP)
   507  		if ip == "" {
   508  			continue
   509  		}
   510  		addr, err := ips.ParseAddrPort(ip)
   511  		if err != nil {
   512  			return node.BootstrapConfig{}, fmt.Errorf("couldn't parse bootstrap ip %s: %w", ip, err)
   513  		}
   514  		config.Bootstrappers = append(config.Bootstrappers, genesis.Bootstrapper{
   515  			// ID is populated below
   516  			IP: addr,
   517  		})
   518  	}
   519  
   520  	bootstrapIDs := strings.Split(v.GetString(BootstrapIDsKey), ",")
   521  	bootstrapNodeIDs := make([]ids.NodeID, 0, len(bootstrapIDs))
   522  	for _, bootstrapID := range bootstrapIDs {
   523  		id := strings.TrimSpace(bootstrapID)
   524  		if id == "" {
   525  			continue
   526  		}
   527  		nodeID, err := ids.NodeIDFromString(id)
   528  		if err != nil {
   529  			return node.BootstrapConfig{}, fmt.Errorf("couldn't parse bootstrap peer id %s: %w", id, err)
   530  		}
   531  		bootstrapNodeIDs = append(bootstrapNodeIDs, nodeID)
   532  	}
   533  
   534  	if len(config.Bootstrappers) != len(bootstrapNodeIDs) {
   535  		return node.BootstrapConfig{}, fmt.Errorf("expected the number of bootstrapIPs (%d) to match the number of bootstrapIDs (%d)", len(config.Bootstrappers), len(bootstrapNodeIDs))
   536  	}
   537  	for i, nodeID := range bootstrapNodeIDs {
   538  		config.Bootstrappers[i].ID = nodeID
   539  	}
   540  
   541  	return config, nil
   542  }
   543  
   544  func getIPConfig(v *viper.Viper) (node.IPConfig, error) {
   545  	ipConfig := node.IPConfig{
   546  		PublicIP:                  v.GetString(PublicIPKey),
   547  		PublicIPResolutionService: v.GetString(PublicIPResolutionServiceKey),
   548  		PublicIPResolutionFreq:    v.GetDuration(PublicIPResolutionFreqKey),
   549  		ListenHost:                v.GetString(StakingHostKey),
   550  		ListenPort:                uint16(v.GetUint(StakingPortKey)),
   551  	}
   552  	if ipConfig.PublicIPResolutionFreq <= 0 {
   553  		return node.IPConfig{}, fmt.Errorf("%q must be > 0", PublicIPResolutionFreqKey)
   554  	}
   555  	if ipConfig.PublicIP != "" && ipConfig.PublicIPResolutionService != "" {
   556  		return node.IPConfig{}, fmt.Errorf("only one of --%s and --%s can be given", PublicIPKey, PublicIPResolutionServiceKey)
   557  	}
   558  	return ipConfig, nil
   559  }
   560  
   561  func getProfilerConfig(v *viper.Viper) (profiler.Config, error) {
   562  	config := profiler.Config{
   563  		Dir:         GetExpandedArg(v, ProfileDirKey),
   564  		Enabled:     v.GetBool(ProfileContinuousEnabledKey),
   565  		Freq:        v.GetDuration(ProfileContinuousFreqKey),
   566  		MaxNumFiles: v.GetInt(ProfileContinuousMaxFilesKey),
   567  	}
   568  	if config.Freq < 0 {
   569  		return profiler.Config{}, fmt.Errorf("%s must be >= 0", ProfileContinuousFreqKey)
   570  	}
   571  	return config, nil
   572  }
   573  
   574  func getStakingTLSCertFromFlag(v *viper.Viper) (tls.Certificate, error) {
   575  	stakingKeyRawContent := v.GetString(StakingTLSKeyContentKey)
   576  	stakingKeyContent, err := base64.StdEncoding.DecodeString(stakingKeyRawContent)
   577  	if err != nil {
   578  		return tls.Certificate{}, fmt.Errorf("unable to decode base64 content: %w", err)
   579  	}
   580  
   581  	stakingCertRawContent := v.GetString(StakingCertContentKey)
   582  	stakingCertContent, err := base64.StdEncoding.DecodeString(stakingCertRawContent)
   583  	if err != nil {
   584  		return tls.Certificate{}, fmt.Errorf("unable to decode base64 content: %w", err)
   585  	}
   586  
   587  	cert, err := staking.LoadTLSCertFromBytes(stakingKeyContent, stakingCertContent)
   588  	if err != nil {
   589  		return tls.Certificate{}, fmt.Errorf("failed creating cert: %w", err)
   590  	}
   591  
   592  	return *cert, nil
   593  }
   594  
   595  func getStakingTLSCertFromFile(v *viper.Viper) (tls.Certificate, error) {
   596  	// Parse the staking key/cert paths and expand environment variables
   597  	stakingKeyPath := GetExpandedArg(v, StakingTLSKeyPathKey)
   598  	stakingCertPath := GetExpandedArg(v, StakingCertPathKey)
   599  
   600  	// If staking key/cert locations are specified but not found, error
   601  	if v.IsSet(StakingTLSKeyPathKey) || v.IsSet(StakingCertPathKey) {
   602  		if _, err := os.Stat(stakingKeyPath); os.IsNotExist(err) {
   603  			return tls.Certificate{}, fmt.Errorf("couldn't find staking key at %s", stakingKeyPath)
   604  		} else if _, err := os.Stat(stakingCertPath); os.IsNotExist(err) {
   605  			return tls.Certificate{}, fmt.Errorf("couldn't find staking certificate at %s", stakingCertPath)
   606  		}
   607  	} else {
   608  		// Create the staking key/cert if [stakingKeyPath] and [stakingCertPath] don't exist
   609  		if err := staking.InitNodeStakingKeyPair(stakingKeyPath, stakingCertPath); err != nil {
   610  			return tls.Certificate{}, fmt.Errorf("couldn't generate staking key/cert: %w", err)
   611  		}
   612  	}
   613  
   614  	// Load and parse the staking key/cert
   615  	cert, err := staking.LoadTLSCertFromFiles(stakingKeyPath, stakingCertPath)
   616  	if err != nil {
   617  		return tls.Certificate{}, fmt.Errorf("couldn't read staking certificate: %w", err)
   618  	}
   619  	return *cert, nil
   620  }
   621  
   622  func getStakingTLSCert(v *viper.Viper) (tls.Certificate, error) {
   623  	if v.GetBool(StakingEphemeralCertEnabledKey) {
   624  		// Use an ephemeral staking key/cert
   625  		cert, err := staking.NewTLSCert()
   626  		if err != nil {
   627  			return tls.Certificate{}, fmt.Errorf("couldn't generate ephemeral staking key/cert: %w", err)
   628  		}
   629  		return *cert, nil
   630  	}
   631  
   632  	switch {
   633  	case v.IsSet(StakingTLSKeyContentKey) && !v.IsSet(StakingCertContentKey):
   634  		return tls.Certificate{}, errStakingCertContentUnset
   635  	case !v.IsSet(StakingTLSKeyContentKey) && v.IsSet(StakingCertContentKey):
   636  		return tls.Certificate{}, errStakingKeyContentUnset
   637  	case v.IsSet(StakingTLSKeyContentKey) && v.IsSet(StakingCertContentKey):
   638  		return getStakingTLSCertFromFlag(v)
   639  	default:
   640  		return getStakingTLSCertFromFile(v)
   641  	}
   642  }
   643  
   644  func getStakingSigner(v *viper.Viper) (*bls.SecretKey, error) {
   645  	if v.GetBool(StakingEphemeralSignerEnabledKey) {
   646  		key, err := bls.NewSecretKey()
   647  		if err != nil {
   648  			return nil, fmt.Errorf("couldn't generate ephemeral signing key: %w", err)
   649  		}
   650  		return key, nil
   651  	}
   652  
   653  	if v.IsSet(StakingSignerKeyContentKey) {
   654  		signerKeyRawContent := v.GetString(StakingSignerKeyContentKey)
   655  		signerKeyContent, err := base64.StdEncoding.DecodeString(signerKeyRawContent)
   656  		if err != nil {
   657  			return nil, fmt.Errorf("unable to decode base64 content: %w", err)
   658  		}
   659  		key, err := bls.SecretKeyFromBytes(signerKeyContent)
   660  		if err != nil {
   661  			return nil, fmt.Errorf("couldn't parse signing key: %w", err)
   662  		}
   663  		return key, nil
   664  	}
   665  
   666  	signingKeyPath := GetExpandedArg(v, StakingSignerKeyPathKey)
   667  	_, err := os.Stat(signingKeyPath)
   668  	if !errors.Is(err, fs.ErrNotExist) {
   669  		signingKeyBytes, err := os.ReadFile(signingKeyPath)
   670  		if err != nil {
   671  			return nil, err
   672  		}
   673  		key, err := bls.SecretKeyFromBytes(signingKeyBytes)
   674  		if err != nil {
   675  			return nil, fmt.Errorf("couldn't parse signing key: %w", err)
   676  		}
   677  		return key, nil
   678  	}
   679  
   680  	if v.IsSet(StakingSignerKeyPathKey) {
   681  		return nil, errMissingStakingSigningKeyFile
   682  	}
   683  
   684  	key, err := bls.NewSecretKey()
   685  	if err != nil {
   686  		return nil, fmt.Errorf("couldn't generate new signing key: %w", err)
   687  	}
   688  
   689  	if err := os.MkdirAll(filepath.Dir(signingKeyPath), perms.ReadWriteExecute); err != nil {
   690  		return nil, fmt.Errorf("couldn't create path for signing key at %s: %w", signingKeyPath, err)
   691  	}
   692  
   693  	keyBytes := bls.SecretKeyToBytes(key)
   694  	if err := os.WriteFile(signingKeyPath, keyBytes, perms.ReadWrite); err != nil {
   695  		return nil, fmt.Errorf("couldn't write new signing key to %s: %w", signingKeyPath, err)
   696  	}
   697  	if err := os.Chmod(signingKeyPath, perms.ReadOnly); err != nil {
   698  		return nil, fmt.Errorf("couldn't restrict permissions on new signing key at %s: %w", signingKeyPath, err)
   699  	}
   700  	return key, nil
   701  }
   702  
   703  func getStakingConfig(v *viper.Viper, networkID uint32) (node.StakingConfig, error) {
   704  	config := node.StakingConfig{
   705  		SybilProtectionEnabled:        v.GetBool(SybilProtectionEnabledKey),
   706  		SybilProtectionDisabledWeight: v.GetUint64(SybilProtectionDisabledWeightKey),
   707  		PartialSyncPrimaryNetwork:     v.GetBool(PartialSyncPrimaryNetworkKey),
   708  		StakingKeyPath:                GetExpandedArg(v, StakingTLSKeyPathKey),
   709  		StakingCertPath:               GetExpandedArg(v, StakingCertPathKey),
   710  		StakingSignerPath:             GetExpandedArg(v, StakingSignerKeyPathKey),
   711  	}
   712  	if !config.SybilProtectionEnabled && config.SybilProtectionDisabledWeight == 0 {
   713  		return node.StakingConfig{}, errSybilProtectionDisabledStakerWeights
   714  	}
   715  
   716  	if !config.SybilProtectionEnabled && (networkID == constants.MainnetID || networkID == constants.TahoeID) {
   717  		return node.StakingConfig{}, errSybilProtectionDisabledOnPublicNetwork
   718  	}
   719  
   720  	var err error
   721  	config.StakingTLSCert, err = getStakingTLSCert(v)
   722  	if err != nil {
   723  		return node.StakingConfig{}, err
   724  	}
   725  	config.StakingSigningKey, err = getStakingSigner(v)
   726  	if err != nil {
   727  		return node.StakingConfig{}, err
   728  	}
   729  	if networkID != constants.MainnetID && networkID != constants.TahoeID {
   730  		config.UptimeRequirement = v.GetFloat64(UptimeRequirementKey)
   731  		config.MinValidatorStake = v.GetUint64(MinValidatorStakeKey)
   732  		config.MaxValidatorStake = v.GetUint64(MaxValidatorStakeKey)
   733  		config.MinDelegatorStake = v.GetUint64(MinDelegatorStakeKey)
   734  		config.MinStakeDuration = v.GetDuration(MinStakeDurationKey)
   735  		config.MaxStakeDuration = v.GetDuration(MaxStakeDurationKey)
   736  		config.RewardConfig.MaxConsumptionRate = v.GetUint64(StakeMaxConsumptionRateKey)
   737  		config.RewardConfig.MinConsumptionRate = v.GetUint64(StakeMinConsumptionRateKey)
   738  		config.RewardConfig.MintingPeriod = v.GetDuration(StakeMintingPeriodKey)
   739  		config.RewardConfig.SupplyCap = v.GetUint64(StakeSupplyCapKey)
   740  		config.MinDelegationFee = v.GetUint32(MinDelegatorFeeKey)
   741  		switch {
   742  		case config.UptimeRequirement < 0 || config.UptimeRequirement > 1:
   743  			return node.StakingConfig{}, errInvalidUptimeRequirement
   744  		case config.MinValidatorStake > config.MaxValidatorStake:
   745  			return node.StakingConfig{}, errMinValidatorStakeAboveMax
   746  		case config.MinDelegationFee > 1_000_000:
   747  			return node.StakingConfig{}, errInvalidDelegationFee
   748  		case config.MinStakeDuration <= 0:
   749  			return node.StakingConfig{}, errInvalidMinStakeDuration
   750  		case config.MaxStakeDuration < config.MinStakeDuration:
   751  			return node.StakingConfig{}, errMinStakeDurationAboveMax
   752  		case config.RewardConfig.MaxConsumptionRate > reward.PercentDenominator:
   753  			return node.StakingConfig{}, errStakeMaxConsumptionTooLarge
   754  		case config.RewardConfig.MaxConsumptionRate < config.RewardConfig.MinConsumptionRate:
   755  			return node.StakingConfig{}, errStakeMaxConsumptionBelowMin
   756  		case config.RewardConfig.MintingPeriod < config.MaxStakeDuration:
   757  			return node.StakingConfig{}, errStakeMintingPeriodBelowMin
   758  		}
   759  	} else {
   760  		config.StakingConfig = genesis.GetStakingConfig(networkID)
   761  	}
   762  	return config, nil
   763  }
   764  
   765  func getTxFeeConfig(v *viper.Viper, networkID uint32) fee.StaticConfig {
   766  	if networkID != constants.MainnetID && networkID != constants.TahoeID {
   767  		return fee.StaticConfig{
   768  			TxFee:                         v.GetUint64(TxFeeKey),
   769  			CreateAssetTxFee:              v.GetUint64(CreateAssetTxFeeKey),
   770  			CreateSubnetTxFee:             v.GetUint64(CreateSubnetTxFeeKey),
   771  			TransformSubnetTxFee:          v.GetUint64(TransformSubnetTxFeeKey),
   772  			CreateBlockchainTxFee:         v.GetUint64(CreateBlockchainTxFeeKey),
   773  			AddPrimaryNetworkValidatorFee: v.GetUint64(AddPrimaryNetworkValidatorFeeKey),
   774  			AddPrimaryNetworkDelegatorFee: v.GetUint64(AddPrimaryNetworkDelegatorFeeKey),
   775  			AddSubnetValidatorFee:         v.GetUint64(AddSubnetValidatorFeeKey),
   776  			AddSubnetDelegatorFee:         v.GetUint64(AddSubnetDelegatorFeeKey),
   777  		}
   778  	}
   779  	return genesis.GetTxFeeConfig(networkID)
   780  }
   781  
   782  func getGenesisData(v *viper.Viper, networkID uint32, stakingCfg *genesis.StakingConfig) ([]byte, ids.ID, error) {
   783  	// try first loading genesis content directly from flag/env-var
   784  	if v.IsSet(GenesisFileContentKey) {
   785  		genesisData := v.GetString(GenesisFileContentKey)
   786  		return genesis.FromFlag(networkID, genesisData, stakingCfg)
   787  	}
   788  
   789  	// if content is not specified go for the file
   790  	if v.IsSet(GenesisFileKey) {
   791  		genesisFileName := GetExpandedArg(v, GenesisFileKey)
   792  		return genesis.FromFile(networkID, genesisFileName, stakingCfg)
   793  	}
   794  
   795  	// finally if file is not specified/readable go for the predefined config
   796  	config := genesis.GetConfig(networkID)
   797  	return genesis.FromConfig(config)
   798  }
   799  
   800  func getTrackedSubnets(v *viper.Viper) (set.Set[ids.ID], error) {
   801  	trackSubnetsStr := v.GetString(TrackSubnetsKey)
   802  	trackSubnetsStrs := strings.Split(trackSubnetsStr, ",")
   803  	trackedSubnetIDs := set.NewSet[ids.ID](len(trackSubnetsStrs))
   804  	for _, subnet := range trackSubnetsStrs {
   805  		if subnet == "" {
   806  			continue
   807  		}
   808  		subnetID, err := ids.FromString(subnet)
   809  		if err != nil {
   810  			return nil, fmt.Errorf("couldn't parse subnetID %q: %w", subnet, err)
   811  		}
   812  		if subnetID == constants.PrimaryNetworkID {
   813  			return nil, errCannotTrackPrimaryNetwork
   814  		}
   815  		trackedSubnetIDs.Add(subnetID)
   816  	}
   817  	return trackedSubnetIDs, nil
   818  }
   819  
   820  func getDatabaseConfig(v *viper.Viper, networkID uint32) (node.DatabaseConfig, error) {
   821  	var (
   822  		configBytes []byte
   823  		err         error
   824  	)
   825  	if v.IsSet(DBConfigContentKey) {
   826  		dbConfigContent := v.GetString(DBConfigContentKey)
   827  		configBytes, err = base64.StdEncoding.DecodeString(dbConfigContent)
   828  		if err != nil {
   829  			return node.DatabaseConfig{}, fmt.Errorf("unable to decode base64 content: %w", err)
   830  		}
   831  	} else if v.IsSet(DBConfigFileKey) {
   832  		path := GetExpandedArg(v, DBConfigFileKey)
   833  		configBytes, err = os.ReadFile(path)
   834  		if err != nil {
   835  			return node.DatabaseConfig{}, err
   836  		}
   837  	}
   838  
   839  	return node.DatabaseConfig{
   840  		Name:     v.GetString(DBTypeKey),
   841  		ReadOnly: v.GetBool(DBReadOnlyKey),
   842  		Path: filepath.Join(
   843  			GetExpandedArg(v, DBPathKey),
   844  			constants.NetworkName(networkID),
   845  		),
   846  		Config: configBytes,
   847  	}, nil
   848  }
   849  
   850  func getAliases(v *viper.Viper, name string, contentKey string, fileKey string) (map[ids.ID][]string, error) {
   851  	var fileBytes []byte
   852  	if v.IsSet(contentKey) {
   853  		var err error
   854  		aliasFlagContent := v.GetString(contentKey)
   855  		fileBytes, err = base64.StdEncoding.DecodeString(aliasFlagContent)
   856  		if err != nil {
   857  			return nil, fmt.Errorf("unable to decode base64 content for %s: %w", name, err)
   858  		}
   859  	} else {
   860  		aliasFilePath := filepath.Clean(GetExpandedArg(v, fileKey))
   861  		exists, err := storage.FileExists(aliasFilePath)
   862  		if err != nil {
   863  			return nil, err
   864  		}
   865  
   866  		if !exists {
   867  			if v.IsSet(fileKey) {
   868  				return nil, fmt.Errorf("%w: %s", errFileDoesNotExist, aliasFilePath)
   869  			}
   870  			return nil, nil
   871  		}
   872  
   873  		fileBytes, err = os.ReadFile(aliasFilePath)
   874  		if err != nil {
   875  			return nil, err
   876  		}
   877  	}
   878  
   879  	aliasMap := make(map[ids.ID][]string)
   880  	if err := json.Unmarshal(fileBytes, &aliasMap); err != nil {
   881  		return nil, fmt.Errorf("%w on %s: %w", errUnmarshalling, name, err)
   882  	}
   883  	return aliasMap, nil
   884  }
   885  
   886  func getVMAliases(v *viper.Viper) (map[ids.ID][]string, error) {
   887  	return getAliases(v, "vm aliases", VMAliasesContentKey, VMAliasesFileKey)
   888  }
   889  
   890  func getChainAliases(v *viper.Viper) (map[ids.ID][]string, error) {
   891  	return getAliases(v, "chain aliases", ChainAliasesContentKey, ChainAliasesFileKey)
   892  }
   893  
   894  // getPathFromDirKey reads flag value from viper instance and then checks the folder existence
   895  func getPathFromDirKey(v *viper.Viper, configKey string) (string, error) {
   896  	configDir := GetExpandedArg(v, configKey)
   897  	cleanPath := filepath.Clean(configDir)
   898  	ok, err := storage.FolderExists(cleanPath)
   899  	if err != nil {
   900  		return "", err
   901  	}
   902  	if ok {
   903  		return cleanPath, nil
   904  	}
   905  	if v.IsSet(configKey) {
   906  		// user specified a config dir explicitly, but dir does not exist.
   907  		return "", fmt.Errorf("%w: %s", errCannotReadDirectory, cleanPath)
   908  	}
   909  	return "", nil
   910  }
   911  
   912  func getChainConfigsFromFlag(v *viper.Viper) (map[string]chains.ChainConfig, error) {
   913  	chainConfigContentB64 := v.GetString(ChainConfigContentKey)
   914  	chainConfigContent, err := base64.StdEncoding.DecodeString(chainConfigContentB64)
   915  	if err != nil {
   916  		return nil, fmt.Errorf("unable to decode base64 content: %w", err)
   917  	}
   918  
   919  	chainConfigs := make(map[string]chains.ChainConfig)
   920  	if err := json.Unmarshal(chainConfigContent, &chainConfigs); err != nil {
   921  		return nil, fmt.Errorf("could not unmarshal JSON: %w", err)
   922  	}
   923  	return chainConfigs, nil
   924  }
   925  
   926  func getChainConfigsFromDir(v *viper.Viper) (map[string]chains.ChainConfig, error) {
   927  	chainConfigPath, err := getPathFromDirKey(v, ChainConfigDirKey)
   928  	if err != nil {
   929  		return nil, err
   930  	}
   931  
   932  	if len(chainConfigPath) == 0 {
   933  		return make(map[string]chains.ChainConfig), nil
   934  	}
   935  
   936  	return readChainConfigPath(chainConfigPath)
   937  }
   938  
   939  // getChainConfigs reads & puts chainConfigs to node config
   940  func getChainConfigs(v *viper.Viper) (map[string]chains.ChainConfig, error) {
   941  	if v.IsSet(ChainConfigContentKey) {
   942  		return getChainConfigsFromFlag(v)
   943  	}
   944  	return getChainConfigsFromDir(v)
   945  }
   946  
   947  // readChainConfigPath reads chain config files from static directories and returns map with contents,
   948  // if successful.
   949  func readChainConfigPath(chainConfigPath string) (map[string]chains.ChainConfig, error) {
   950  	chainDirs, err := filepath.Glob(filepath.Join(chainConfigPath, "*"))
   951  	if err != nil {
   952  		return nil, err
   953  	}
   954  	chainConfigMap := make(map[string]chains.ChainConfig)
   955  	for _, chainDir := range chainDirs {
   956  		dirInfo, err := os.Stat(chainDir)
   957  		if err != nil {
   958  			return nil, err
   959  		}
   960  
   961  		if !dirInfo.IsDir() {
   962  			continue
   963  		}
   964  
   965  		// chainconfigdir/chainId/config.*
   966  		configData, err := storage.ReadFileWithName(chainDir, chainConfigFileName)
   967  		if err != nil {
   968  			return chainConfigMap, err
   969  		}
   970  
   971  		// chainconfigdir/chainId/upgrade.*
   972  		upgradeData, err := storage.ReadFileWithName(chainDir, chainUpgradeFileName)
   973  		if err != nil {
   974  			return chainConfigMap, err
   975  		}
   976  
   977  		chainConfigMap[dirInfo.Name()] = chains.ChainConfig{
   978  			Config:  configData,
   979  			Upgrade: upgradeData,
   980  		}
   981  	}
   982  	return chainConfigMap, nil
   983  }
   984  
   985  // getSubnetConfigs reads subnet configs from the correct place
   986  // (flag or file) and returns a non-nil map.
   987  func getSubnetConfigs(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]subnets.Config, error) {
   988  	if v.IsSet(SubnetConfigContentKey) {
   989  		return getSubnetConfigsFromFlags(v, subnetIDs)
   990  	}
   991  	return getSubnetConfigsFromDir(v, subnetIDs)
   992  }
   993  
   994  func getSubnetConfigsFromFlags(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]subnets.Config, error) {
   995  	subnetConfigContentB64 := v.GetString(SubnetConfigContentKey)
   996  	subnetConfigContent, err := base64.StdEncoding.DecodeString(subnetConfigContentB64)
   997  	if err != nil {
   998  		return nil, fmt.Errorf("unable to decode base64 content: %w", err)
   999  	}
  1000  
  1001  	// partially parse configs to be filled by defaults later
  1002  	subnetConfigs := make(map[ids.ID]json.RawMessage, len(subnetIDs))
  1003  	if err := json.Unmarshal(subnetConfigContent, &subnetConfigs); err != nil {
  1004  		return nil, fmt.Errorf("could not unmarshal JSON: %w", err)
  1005  	}
  1006  
  1007  	res := make(map[ids.ID]subnets.Config)
  1008  	for _, subnetID := range subnetIDs {
  1009  		if rawSubnetConfigBytes, ok := subnetConfigs[subnetID]; ok {
  1010  			config := getDefaultSubnetConfig(v)
  1011  			if err := json.Unmarshal(rawSubnetConfigBytes, &config); err != nil {
  1012  				return nil, err
  1013  			}
  1014  
  1015  			if config.ConsensusParameters.Alpha != nil {
  1016  				config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
  1017  				config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
  1018  			}
  1019  
  1020  			if err := config.Valid(); err != nil {
  1021  				return nil, err
  1022  			}
  1023  
  1024  			res[subnetID] = config
  1025  		}
  1026  	}
  1027  	return res, nil
  1028  }
  1029  
  1030  // getSubnetConfigs reads SubnetConfigs to node config map
  1031  func getSubnetConfigsFromDir(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]subnets.Config, error) {
  1032  	subnetConfigPath, err := getPathFromDirKey(v, SubnetConfigDirKey)
  1033  	if err != nil {
  1034  		return nil, err
  1035  	}
  1036  
  1037  	subnetConfigs := make(map[ids.ID]subnets.Config)
  1038  	if len(subnetConfigPath) == 0 {
  1039  		// subnet config path does not exist but not explicitly specified, so ignore it
  1040  		return subnetConfigs, nil
  1041  	}
  1042  
  1043  	// reads subnet config files from a path and given subnetIDs and returns a map.
  1044  	for _, subnetID := range subnetIDs {
  1045  		filePath := filepath.Join(subnetConfigPath, subnetID.String()+subnetConfigFileExt)
  1046  		fileInfo, err := os.Stat(filePath)
  1047  		switch {
  1048  		case errors.Is(err, os.ErrNotExist):
  1049  			// this subnet config does not exist, move to the next one
  1050  			continue
  1051  		case err != nil:
  1052  			return nil, err
  1053  		case fileInfo.IsDir():
  1054  			return nil, fmt.Errorf("%q is a directory, expected a file", fileInfo.Name())
  1055  		}
  1056  
  1057  		// subnetConfigDir/subnetID.json
  1058  		file, err := os.ReadFile(filePath)
  1059  		if err != nil {
  1060  			return nil, err
  1061  		}
  1062  
  1063  		config := getDefaultSubnetConfig(v)
  1064  		if err := json.Unmarshal(file, &config); err != nil {
  1065  			return nil, fmt.Errorf("%w: %w", errUnmarshalling, err)
  1066  		}
  1067  
  1068  		if config.ConsensusParameters.Alpha != nil {
  1069  			config.ConsensusParameters.AlphaPreference = *config.ConsensusParameters.Alpha
  1070  			config.ConsensusParameters.AlphaConfidence = config.ConsensusParameters.AlphaPreference
  1071  		}
  1072  
  1073  		if err := config.Valid(); err != nil {
  1074  			return nil, err
  1075  		}
  1076  
  1077  		subnetConfigs[subnetID] = config
  1078  	}
  1079  
  1080  	return subnetConfigs, nil
  1081  }
  1082  
  1083  func getDefaultSubnetConfig(v *viper.Viper) subnets.Config {
  1084  	return subnets.Config{
  1085  		ConsensusParameters:         getConsensusConfig(v),
  1086  		ValidatorOnly:               false,
  1087  		ProposerMinBlockDelay:       proposervm.DefaultMinBlockDelay,
  1088  		ProposerNumHistoricalBlocks: proposervm.DefaultNumHistoricalBlocks,
  1089  	}
  1090  }
  1091  
  1092  func getCPUTargeterConfig(v *viper.Viper) (tracker.TargeterConfig, error) {
  1093  	vdrAlloc := v.GetFloat64(CPUVdrAllocKey)
  1094  	maxNonVdrUsage := v.GetFloat64(CPUMaxNonVdrUsageKey)
  1095  	maxNonVdrNodeUsage := v.GetFloat64(CPUMaxNonVdrNodeUsageKey)
  1096  	switch {
  1097  	case vdrAlloc < 0:
  1098  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", CPUVdrAllocKey, vdrAlloc)
  1099  	case maxNonVdrUsage < 0:
  1100  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", CPUMaxNonVdrUsageKey, maxNonVdrUsage)
  1101  	case maxNonVdrNodeUsage < 0:
  1102  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", CPUMaxNonVdrNodeUsageKey, maxNonVdrNodeUsage)
  1103  	default:
  1104  		return tracker.TargeterConfig{
  1105  			VdrAlloc:           vdrAlloc,
  1106  			MaxNonVdrUsage:     maxNonVdrUsage,
  1107  			MaxNonVdrNodeUsage: maxNonVdrNodeUsage,
  1108  		}, nil
  1109  	}
  1110  }
  1111  
  1112  func getDiskSpaceConfig(v *viper.Viper) (requiredAvailableDiskSpace uint64, warningThresholdAvailableDiskSpace uint64, err error) {
  1113  	requiredAvailableDiskSpace = v.GetUint64(SystemTrackerRequiredAvailableDiskSpaceKey)
  1114  	warningThresholdAvailableDiskSpace = v.GetUint64(SystemTrackerWarningThresholdAvailableDiskSpaceKey)
  1115  	switch {
  1116  	case warningThresholdAvailableDiskSpace < requiredAvailableDiskSpace:
  1117  		return 0, 0, fmt.Errorf("%q (%d) < %q (%d)", SystemTrackerWarningThresholdAvailableDiskSpaceKey, warningThresholdAvailableDiskSpace, SystemTrackerRequiredAvailableDiskSpaceKey, requiredAvailableDiskSpace)
  1118  	default:
  1119  		return requiredAvailableDiskSpace, warningThresholdAvailableDiskSpace, nil
  1120  	}
  1121  }
  1122  
  1123  func getDiskTargeterConfig(v *viper.Viper) (tracker.TargeterConfig, error) {
  1124  	vdrAlloc := v.GetFloat64(DiskVdrAllocKey)
  1125  	maxNonVdrUsage := v.GetFloat64(DiskMaxNonVdrUsageKey)
  1126  	maxNonVdrNodeUsage := v.GetFloat64(DiskMaxNonVdrNodeUsageKey)
  1127  	switch {
  1128  	case vdrAlloc < 0:
  1129  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", DiskVdrAllocKey, vdrAlloc)
  1130  	case maxNonVdrUsage < 0:
  1131  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", DiskMaxNonVdrUsageKey, maxNonVdrUsage)
  1132  	case maxNonVdrNodeUsage < 0:
  1133  		return tracker.TargeterConfig{}, fmt.Errorf("%q (%f) < 0", DiskMaxNonVdrNodeUsageKey, maxNonVdrNodeUsage)
  1134  	default:
  1135  		return tracker.TargeterConfig{
  1136  			VdrAlloc:           vdrAlloc,
  1137  			MaxNonVdrUsage:     maxNonVdrUsage,
  1138  			MaxNonVdrNodeUsage: maxNonVdrNodeUsage,
  1139  		}, nil
  1140  	}
  1141  }
  1142  
  1143  func getTraceConfig(v *viper.Viper) (trace.Config, error) {
  1144  	enabled := v.GetBool(TracingEnabledKey)
  1145  	if !enabled {
  1146  		return trace.Config{
  1147  			Enabled: false,
  1148  		}, nil
  1149  	}
  1150  
  1151  	exporterTypeStr := v.GetString(TracingExporterTypeKey)
  1152  	exporterType, err := trace.ExporterTypeFromString(exporterTypeStr)
  1153  	if err != nil {
  1154  		return trace.Config{}, err
  1155  	}
  1156  
  1157  	endpoint := v.GetString(TracingEndpointKey)
  1158  	if endpoint == "" {
  1159  		return trace.Config{}, errTracingEndpointEmpty
  1160  	}
  1161  
  1162  	return trace.Config{
  1163  		ExporterConfig: trace.ExporterConfig{
  1164  			Type:     exporterType,
  1165  			Endpoint: endpoint,
  1166  			Insecure: v.GetBool(TracingInsecureKey),
  1167  			Headers:  v.GetStringMapString(TracingHeadersKey),
  1168  		},
  1169  		Enabled:         true,
  1170  		TraceSampleRate: v.GetFloat64(TracingSampleRateKey),
  1171  		AppName:         constants.AppName,
  1172  		Version:         version.Current.String(),
  1173  	}, nil
  1174  }
  1175  
  1176  // Returns the path to the directory that contains VM binaries.
  1177  func getPluginDir(v *viper.Viper) (string, error) {
  1178  	pluginDir := GetExpandedString(v, v.GetString(PluginDirKey))
  1179  
  1180  	if v.IsSet(PluginDirKey) {
  1181  		// If the flag was given, assert it exists and is a directory
  1182  		info, err := os.Stat(pluginDir)
  1183  		if err != nil {
  1184  			return "", fmt.Errorf("plugin dir %q not found: %w", pluginDir, err)
  1185  		}
  1186  		if !info.IsDir() {
  1187  			return "", fmt.Errorf("%w: %q", errPluginDirNotADirectory, pluginDir)
  1188  		}
  1189  	} else {
  1190  		// If the flag wasn't given, make sure the default location exists.
  1191  		if err := os.MkdirAll(pluginDir, perms.ReadWriteExecute); err != nil {
  1192  			return "", fmt.Errorf("failed to create plugin dir at %s: %w", pluginDir, err)
  1193  		}
  1194  	}
  1195  
  1196  	return pluginDir, nil
  1197  }
  1198  
  1199  func GetNodeConfig(v *viper.Viper) (node.Config, error) {
  1200  	var (
  1201  		nodeConfig node.Config
  1202  		err        error
  1203  	)
  1204  
  1205  	nodeConfig.PluginDir, err = getPluginDir(v)
  1206  	if err != nil {
  1207  		return node.Config{}, err
  1208  	}
  1209  
  1210  	nodeConfig.ConsensusShutdownTimeout = v.GetDuration(ConsensusShutdownTimeoutKey)
  1211  	if nodeConfig.ConsensusShutdownTimeout < 0 {
  1212  		return node.Config{}, fmt.Errorf("%q must be >= 0", ConsensusShutdownTimeoutKey)
  1213  	}
  1214  
  1215  	// Gossiping
  1216  	nodeConfig.FrontierPollFrequency = v.GetDuration(ConsensusFrontierPollFrequencyKey)
  1217  	if nodeConfig.FrontierPollFrequency < 0 {
  1218  		return node.Config{}, fmt.Errorf("%s must be >= 0", ConsensusFrontierPollFrequencyKey)
  1219  	}
  1220  
  1221  	// App handling
  1222  	nodeConfig.ConsensusAppConcurrency = int(v.GetUint(ConsensusAppConcurrencyKey))
  1223  	if nodeConfig.ConsensusAppConcurrency <= 0 {
  1224  		return node.Config{}, fmt.Errorf("%s must be > 0", ConsensusAppConcurrencyKey)
  1225  	}
  1226  
  1227  	nodeConfig.UseCurrentHeight = v.GetBool(ProposerVMUseCurrentHeightKey)
  1228  
  1229  	// Logging
  1230  	nodeConfig.LoggingConfig, err = getLoggingConfig(v)
  1231  	if err != nil {
  1232  		return node.Config{}, err
  1233  	}
  1234  
  1235  	// Network ID
  1236  	nodeConfig.NetworkID, err = constants.NetworkID(v.GetString(NetworkNameKey))
  1237  	if err != nil {
  1238  		return node.Config{}, err
  1239  	}
  1240  
  1241  	// Database
  1242  	nodeConfig.DatabaseConfig, err = getDatabaseConfig(v, nodeConfig.NetworkID)
  1243  	if err != nil {
  1244  		return node.Config{}, err
  1245  	}
  1246  
  1247  	// IP configuration
  1248  	nodeConfig.IPConfig, err = getIPConfig(v)
  1249  	if err != nil {
  1250  		return node.Config{}, err
  1251  	}
  1252  
  1253  	// Staking
  1254  	nodeConfig.StakingConfig, err = getStakingConfig(v, nodeConfig.NetworkID)
  1255  	if err != nil {
  1256  		return node.Config{}, err
  1257  	}
  1258  
  1259  	// Tracked Subnets
  1260  	nodeConfig.TrackedSubnets, err = getTrackedSubnets(v)
  1261  	if err != nil {
  1262  		return node.Config{}, err
  1263  	}
  1264  
  1265  	// HTTP APIs
  1266  	nodeConfig.HTTPConfig, err = getHTTPConfig(v)
  1267  	if err != nil {
  1268  		return node.Config{}, err
  1269  	}
  1270  
  1271  	// Health
  1272  	nodeConfig.HealthCheckFreq = v.GetDuration(HealthCheckFreqKey)
  1273  	if nodeConfig.HealthCheckFreq < 0 {
  1274  		return node.Config{}, fmt.Errorf("%s must be positive", HealthCheckFreqKey)
  1275  	}
  1276  	// Halflife of continuous averager used in health checks
  1277  	healthCheckAveragerHalflife := v.GetDuration(HealthCheckAveragerHalflifeKey)
  1278  	if healthCheckAveragerHalflife <= 0 {
  1279  		return node.Config{}, fmt.Errorf("%s must be positive", HealthCheckAveragerHalflifeKey)
  1280  	}
  1281  
  1282  	// Router
  1283  	nodeConfig.RouterHealthConfig, err = getRouterHealthConfig(v, healthCheckAveragerHalflife)
  1284  	if err != nil {
  1285  		return node.Config{}, err
  1286  	}
  1287  
  1288  	// Metrics
  1289  	nodeConfig.MeterVMEnabled = v.GetBool(MeterVMsEnabledKey)
  1290  
  1291  	// Adaptive Timeout Config
  1292  	nodeConfig.AdaptiveTimeoutConfig, err = getAdaptiveTimeoutConfig(v)
  1293  	if err != nil {
  1294  		return node.Config{}, err
  1295  	}
  1296  
  1297  	// Network Config
  1298  	nodeConfig.NetworkConfig, err = getNetworkConfig(
  1299  		v,
  1300  		nodeConfig.NetworkID,
  1301  		nodeConfig.SybilProtectionEnabled,
  1302  		healthCheckAveragerHalflife,
  1303  	)
  1304  	if err != nil {
  1305  		return node.Config{}, err
  1306  	}
  1307  
  1308  	// Subnet Configs
  1309  	subnetConfigs, err := getSubnetConfigs(v, nodeConfig.TrackedSubnets.List())
  1310  	if err != nil {
  1311  		return node.Config{}, fmt.Errorf("couldn't read subnet configs: %w", err)
  1312  	}
  1313  
  1314  	primaryNetworkConfig := getDefaultSubnetConfig(v)
  1315  	if err := primaryNetworkConfig.Valid(); err != nil {
  1316  		return node.Config{}, fmt.Errorf("invalid consensus parameters: %w", err)
  1317  	}
  1318  	subnetConfigs[constants.PrimaryNetworkID] = primaryNetworkConfig
  1319  
  1320  	nodeConfig.SubnetConfigs = subnetConfigs
  1321  
  1322  	// Benchlist
  1323  	nodeConfig.BenchlistConfig, err = getBenchlistConfig(v, primaryNetworkConfig.ConsensusParameters)
  1324  	if err != nil {
  1325  		return node.Config{}, err
  1326  	}
  1327  
  1328  	// File Descriptor Limit
  1329  	nodeConfig.FdLimit = v.GetUint64(FdLimitKey)
  1330  
  1331  	// Tx Fee
  1332  	nodeConfig.StaticConfig = getTxFeeConfig(v, nodeConfig.NetworkID)
  1333  
  1334  	// Genesis Data
  1335  	genesisStakingCfg := nodeConfig.StakingConfig.StakingConfig
  1336  	nodeConfig.GenesisBytes, nodeConfig.AvaxAssetID, err = getGenesisData(v, nodeConfig.NetworkID, &genesisStakingCfg)
  1337  	if err != nil {
  1338  		return node.Config{}, fmt.Errorf("unable to load genesis file: %w", err)
  1339  	}
  1340  
  1341  	// StateSync Configs
  1342  	nodeConfig.StateSyncConfig, err = getStateSyncConfig(v)
  1343  	if err != nil {
  1344  		return node.Config{}, err
  1345  	}
  1346  
  1347  	// Bootstrap Configs
  1348  	nodeConfig.BootstrapConfig, err = getBootstrapConfig(v, nodeConfig.NetworkID)
  1349  	if err != nil {
  1350  		return node.Config{}, err
  1351  	}
  1352  
  1353  	// Chain Configs
  1354  	nodeConfig.ChainConfigs, err = getChainConfigs(v)
  1355  	if err != nil {
  1356  		return node.Config{}, fmt.Errorf("couldn't read chain configs: %w", err)
  1357  	}
  1358  
  1359  	// Profiler
  1360  	nodeConfig.ProfilerConfig, err = getProfilerConfig(v)
  1361  	if err != nil {
  1362  		return node.Config{}, err
  1363  	}
  1364  
  1365  	// VM Aliases
  1366  	nodeConfig.VMAliases, err = getVMAliases(v)
  1367  	if err != nil {
  1368  		return node.Config{}, err
  1369  	}
  1370  	// Chain aliases
  1371  	nodeConfig.ChainAliases, err = getChainAliases(v)
  1372  	if err != nil {
  1373  		return node.Config{}, err
  1374  	}
  1375  
  1376  	nodeConfig.SystemTrackerFrequency = v.GetDuration(SystemTrackerFrequencyKey)
  1377  	nodeConfig.SystemTrackerProcessingHalflife = v.GetDuration(SystemTrackerProcessingHalflifeKey)
  1378  	nodeConfig.SystemTrackerCPUHalflife = v.GetDuration(SystemTrackerCPUHalflifeKey)
  1379  	nodeConfig.SystemTrackerDiskHalflife = v.GetDuration(SystemTrackerDiskHalflifeKey)
  1380  
  1381  	nodeConfig.RequiredAvailableDiskSpace, nodeConfig.WarningThresholdAvailableDiskSpace, err = getDiskSpaceConfig(v)
  1382  	if err != nil {
  1383  		return node.Config{}, err
  1384  	}
  1385  
  1386  	nodeConfig.CPUTargeterConfig, err = getCPUTargeterConfig(v)
  1387  	if err != nil {
  1388  		return node.Config{}, err
  1389  	}
  1390  
  1391  	nodeConfig.DiskTargeterConfig, err = getDiskTargeterConfig(v)
  1392  	if err != nil {
  1393  		return node.Config{}, err
  1394  	}
  1395  
  1396  	nodeConfig.TraceConfig, err = getTraceConfig(v)
  1397  	if err != nil {
  1398  		return node.Config{}, err
  1399  	}
  1400  
  1401  	nodeConfig.ChainDataDir = GetExpandedArg(v, ChainDataDirKey)
  1402  
  1403  	nodeConfig.ProcessContextFilePath = GetExpandedArg(v, ProcessContextFileKey)
  1404  
  1405  	nodeConfig.ProvidedFlags = providedFlags(v)
  1406  	return nodeConfig, nil
  1407  }
  1408  
  1409  func providedFlags(v *viper.Viper) map[string]interface{} {
  1410  	settings := v.AllSettings()
  1411  	customSettings := make(map[string]interface{}, len(settings))
  1412  	for key, val := range settings {
  1413  		if v.IsSet(key) {
  1414  			customSettings[key] = val
  1415  		}
  1416  	}
  1417  	return customSettings
  1418  }