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 }