diff --git a/go.mod b/go.mod index 95fb213..8d64f9f 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,10 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/samber/mo v1.11.0 github.com/stretchr/testify v1.9.0 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 59695f2..8bf42df 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,5 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/config/config.go b/pkg/config/config.go index 9b80ed0..6dacbf3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,147 +1,405 @@ package config import ( - "fmt" - "os" - "path/filepath" - "strings" - - "gopkg.in/yaml.v2" + "github.com/chia-network/go-chia-libs/pkg/types" ) // ChiaConfig the chia config.yaml type ChiaConfig struct { - ChiaRoot string - DaemonPort uint16 `yaml:"daemon_port"` - DaemonSSL SSLConfig `yaml:"daemon_ssl"` - Farmer FarmerConfig `yaml:"farmer"` - FullNode FullNodeConfig `yaml:"full_node"` - Harvester HarvesterConfig `yaml:"harvester"` - Wallet WalletConfig `yaml:"wallet"` - Seeder SeederConfig `yaml:"seeder"` - DataLayer DataLayerConfig `yaml:"data_layer"` - Timelord TimelordConfig `yaml:"timelord"` - SelectedNetwork string `yaml:"selected_network"` + ChiaRoot string `yaml:"-"` + MinMainnetKSize uint8 `yaml:"min_mainnet_k_size"` + PingInterval uint16 `yaml:"ping_interval"` + SelfHostname string `yaml:"self_hostname"` + PreferIPv6 bool `yaml:"prefer_ipv6"` + RPCTimeout uint16 `yaml:"rpc_timeout"` + DaemonPort uint16 `yaml:"daemon_port"` + DaemonMaxMessageSize uint32 `yaml:"daemon_max_message_size"` + DaemonHeartbeat uint16 `yaml:"daemon_heartbeat"` + DaemonAllowTLS12 bool `yaml:"daemon_allow_tls_1_2"` + InboundRateLimitPercent uint8 `yaml:"inbound_rate_limit_percent"` + OutboundRateLimitPercent uint8 `yaml:"outbound_rate_limit_percent"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` // @TODO this would usually be an anchor + SelectedNetwork string `yaml:"selected_network"` + AlertsURL string `yaml:"ALERTS_URL"` + ChiaAlertsPubkey string `yaml:"CHIA_ALERTS_PUBKEY"` + PrivateSSLCA CAConfig `yaml:"private_ssl_ca"` + ChiaSSLCA CAConfig `yaml:"chia_ssl_ca"` + DaemonSSL SSLConfig `yaml:"daemon_ssl"` + Logging LoggingConfig `yaml:"logging"` // @TODO this would usually be an anchor + Seeder SeederConfig `yaml:"seeder"` + Harvester HarvesterConfig `yaml:"harvester"` + Pool PoolConfig `yaml:"pool"` + Farmer FarmerConfig `yaml:"farmer"` + TimelordLauncher TimelordLauncherConfig `yaml:"timelord_launcher"` + Timelord TimelordConfig `yaml:"timelord"` + FullNode FullNodeConfig `yaml:"full_node"` + UI UIConfig `yaml:"ui"` + Introducer IntroducerConfig `yaml:"introducer"` + Wallet WalletConfig `yaml:"wallet"` + DataLayer DataLayerConfig `yaml:"data_layer"` + Simulator SimulatorConfig `yaml:"simulator"` } -// FarmerConfig farmer configuration section -type FarmerConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` +// PortConfig common port settings found in many sections of the config +type PortConfig struct { + Port uint16 `yaml:"port,omitempty"` + RPCPort uint16 `yaml:"rpc_port"` } -// FullNodeConfig full node configuration section -type FullNodeConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` - SelectedNetwork string `yaml:"selected_network"` - DatabasePath string `yaml:"database_path"` - DNSServers []string `yaml:"dns_servers"` +// CAConfig config keys for CA +type CAConfig struct { + Crt string `yaml:"crt"` + Key string `yaml:"key"` } -// HarvesterConfig harvester configuration section -type HarvesterConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` +// SSLConfig common ssl settings found in many sections of the config +type SSLConfig struct { + PrivateCRT string `yaml:"private_crt"` + PrivateKey string `yaml:"private_key"` + PublicCRT string `yaml:"public_crt"` + PublicKey string `yaml:"public_key"` } -// WalletConfig wallet configuration section -type WalletConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` +// Peer is a host/port for a peer +type Peer struct { + Host string `yaml:"host"` + Port uint16 `yaml:"port"` + EnablePrivateNetworks bool `yaml:"enable_private_networks,omitempty"` +} + +// NetworkOverrides is all network settings +type NetworkOverrides struct { + Constants map[string]NetworkConstants `yaml:"constants"` + Config map[string]NetworkConfig `yaml:"config"` +} + +// NetworkConstants the constants for each network +type NetworkConstants struct { + AggSigMeAdditionalData string `yaml:"AGG_SIG_ME_ADDITIONAL_DATA,omitempty"` + DifficultyConstantFactor types.Uint128 `yaml:"DIFFICULTY_CONSTANT_FACTOR,omitempty"` + DifficultyStarting uint64 `yaml:"DIFFICULTY_STARTING,omitempty"` + EpochBlocks uint32 `yaml:"EPOCH_BLOCKS,omitempty"` + GenesisChallenge string `yaml:"GENESIS_CHALLENGE"` + GenesisPreFarmPoolPuzzleHash string `yaml:"GENESIS_PRE_FARM_POOL_PUZZLE_HASH"` + GenesisPreFarmFarmerPuzzleHash string `yaml:"GENESIS_PRE_FARM_FARMER_PUZZLE_HASH"` + MempoolBlockBuffer uint8 `yaml:"MEMPOOL_BLOCK_BUFFER,omitempty"` + MinPlotSize uint8 `yaml:"MIN_PLOT_SIZE,omitempty"` + NetworkType uint8 `yaml:"NETWORK_TYPE,omitempty"` + SubSlotItersStarting uint64 `yaml:"SUB_SLOT_ITERS_STARTING,omitempty"` + HardForkHeight uint32 `yaml:"HARD_FORK_HEIGHT,omitempty"` + SoftFork4Height uint32 `yaml:"SOFT_FORK4_HEIGHT,omitempty"` + SoftFork5Height uint32 `yaml:"SOFT_FORK5_HEIGHT,omitempty"` + PlotFilter128Height uint32 `yaml:"PLOT_FILTER_128_HEIGHT,omitempty"` + PlotFilter64Height uint32 `yaml:"PLOT_FILTER_64_HEIGHT,omitempty"` + PlotFilter32Height uint32 `yaml:"PLOT_FILTER_32_HEIGHT,omitempty"` +} + +// NetworkConfig specific network configuration settings +type NetworkConfig struct { + AddressPrefix string `yaml:"address_prefix"` + DefaultFullNodePort uint16 `yaml:"default_full_node_port"` +} + +// LoggingConfig configuration settings for the logger +type LoggingConfig struct { + LogStdout bool `yaml:"log_stdout"` + LogFilename string `yaml:"log_filename"` + LogLevel string `yaml:"log_level"` + LogMaxFilesRotation uint8 `yaml:"log_maxfilesrotation"` + LogMaxBytesRotation uint32 `yaml:"log_maxbytesrotation"` + LogUseGzip bool `yaml:"log_use_gzip"` + LogSyslog bool `yaml:"log_syslog"` + LogSyslogHost string `yaml:"log_syslog_host"` + LogSyslogPort uint16 `yaml:"log_syslog_port"` } // SeederConfig seeder configuration section type SeederConfig struct { - CrawlerConfig CrawlerConfig `yaml:"crawler"` + Port uint16 `yaml:"port"` + OtherPeersPort uint16 `yaml:"other_peers_port"` + DNSPort uint16 `yaml:"dns_port"` + PeerConnectTimeout uint16 `yaml:"peer_connect_timeout"` + CrawlerDBPath string `yaml:"crawler_db_path"` + BootstrapPeers []string `yaml:"bootstrap_peers"` + MinimumHeight uint32 `yaml:"minimum_height"` + MinimumVersionCount uint32 `yaml:"minimum_version_count"` + DomainName string `yaml:"domain_name"` + Nameserver string `yaml:"nameserver"` + TTL uint16 `yaml:"ttl"` + SOA SeederSOA `yaml:"soa"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + Logging LoggingConfig `yaml:"logging"` + CrawlerConfig CrawlerConfig `yaml:"crawler"` +} + +// SeederSOA dns SOA for seeder +type SeederSOA struct { + Rname string `yaml:"rname"` + SerialNumber string `yaml:"serial_number"` + Refresh uint32 `yaml:"refresh"` + Retry uint32 `yaml:"retry"` + Expire uint32 `yaml:"expire"` + Minimum uint32 `yaml:"minimum"` } // CrawlerConfig is the subsection of the seeder config specific to the crawler type CrawlerConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` + StartRPCServer bool `yaml:"start_rpc_server"` + PortConfig `yaml:",inline"` + SSL SSLConfig `yaml:"ssl"` } -// DataLayerConfig datalayer configuration section -type DataLayerConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` +// HarvesterConfig harvester configuration section +type HarvesterConfig struct { + FarmerPeers []Peer `yaml:"farmer_peers"` + StartRPCServer bool `yaml:"start_rpc_server"` + NumThreads uint8 `yaml:"num_threads"` + PlotsRefreshParameter PlotsRefreshParameter `yaml:"plots_refresh_parameter"` + ParallelRead bool `yaml:"parallel_read"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + PlotDirectories []string `yaml:"plot_directories"` + RecursivePlotScan bool `yaml:"recursive_plot_scan"` + PortConfig `yaml:",inline"` + SSL SSLConfig `yaml:"ssl"` + PrivateSSLCA CAConfig `yaml:"private_ssl_ca"` + ChiaSSLCA CAConfig `yaml:"chia_ssl_ca"` + ParallelDecompressorCount uint8 `yaml:"parallel_decompressor_count"` + DecompressorThreadCount uint8 `yaml:"decompressor_thread_count"` + DisableCPUAffinity bool `yaml:"disable_cpu_affinity"` + MaxCompressionLevelAllowed uint8 `yaml:"max_compression_level_allowed"` + UseGPUHarvesting bool `yaml:"use_gpu_harvesting"` + GPUIndex uint8 `yaml:"gpu_index"` + EnforceGPUIndex bool `yaml:"enforce_gpu_index"` + DecompressorTimeout uint16 `yaml:"decompressor_timeout"` } -// TimelordConfig timelord configuration section -type TimelordConfig struct { - PortConfig `yaml:",inline"` - SSL SSLConfig `yaml:"ssl"` +// PlotsRefreshParameter refresh params for harvester +type PlotsRefreshParameter struct { + IntervalSeconds uint16 `yaml:"interval_seconds"` + RetryInvalidSeconds uint16 `yaml:"retry_invalid_seconds"` + BatchSize uint16 `yaml:"batch_size"` + BatchSleepMilliseconds uint16 `yaml:"batch_sleep_milliseconds"` } -// PortConfig common port settings found in many sections of the config -type PortConfig struct { - Port uint16 `yaml:"port"` - RPCPort uint16 `yaml:"rpc_port"` +// PoolConfig configures pool settings +type PoolConfig struct { + XCHTargetAddress string `yaml:"xch_target_address,omitempty"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` } -// SSLConfig common ssl settings found in many sections of the config -type SSLConfig struct { - PrivateCRT string `yaml:"private_crt"` - PrivateKey string `yaml:"private_key"` - PublicCRT string `yaml:"public_crt"` - PublicKey string `yaml:"public_key"` +// FarmerConfig farmer configuration section +type FarmerConfig struct { + FullNodePeers []Peer `yaml:"full_node_peers"` + PoolPublicKeys []string `yaml:"pool_public_keys"` // @TODO test if the !!set notation parses correctly + XCHTargetAddress string `yaml:"xch_target_address,omitempty"` + StartRPCServer bool `yaml:"start_rpc_server"` + EnableProfiler bool `yaml:"enable_profiler"` + PoolShareThreshold uint32 `yaml:"pool_share_threshold"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + PortConfig `yaml:",inline"` + SSL SSLConfig `yaml:"ssl"` } -// GetChiaConfig returns a struct containing the config.yaml values -func GetChiaConfig() (*ChiaConfig, error) { - rootPath, err := GetChiaRootPath() - if err != nil { - return nil, err - } - - configPath := filepath.Join(rootPath, "config", "config.yaml") - if _, err = os.Stat(configPath); os.IsNotExist(err) { - return nil, fmt.Errorf("chia config file not found at %s. Ensure CHIA_ROOT is set to the correct chia root", configPath) - } - - configBytes, err := os.ReadFile(configPath) - if err != nil { - return nil, err - } +// TimelordLauncherConfig settings for vdf_client launcher +type TimelordLauncherConfig struct { + Host string `yaml:"host"` + Port uint16 `yaml:"port"` + ProcessCount uint8 `yaml:"process_count"` + Logging LoggingConfig `yaml:"logging"` +} - config := &ChiaConfig{} +// TimelordConfig timelord configuration section +type TimelordConfig struct { + VDFClients VDFClients `yaml:"vdf_clients"` + FullNodePeers []Peer `yaml:"full_node_peers"` + MaxConnectionTime uint16 `yaml:"max_connection_time"` + VDFServer Peer `yaml:"vdf_server"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + FastAlgorithm bool `yaml:"fast_algorithm"` + BlueboxMode bool `yaml:"bluebox_mode"` + SlowBluebox bool `yaml:"slow_bluebox"` + SlowBlueboxProcessCount uint8 `yaml:"slow_bluebox_process_count"` + MultiprocessingStartMethod string `yaml:"multiprocessing_start_method"` + StartRPCServer bool `yaml:"start_rpc_server"` + PortConfig `yaml:",inline"` + SSL SSLConfig `yaml:"ssl"` +} - err = yaml.Unmarshal(configBytes, config) - if err != nil { - return nil, err - } +// VDFClients is a list of allowlisted IPs for vdf_client +type VDFClients struct { + IP []string `yaml:"ip"` + IPSEstimate []uint32 `yaml:"ips_estimate"` +} - config.ChiaRoot = rootPath - config.fillDatabasePath() +// FullNodeConfig full node configuration section +type FullNodeConfig struct { + PortConfig `yaml:",inline"` + FullNodePeers []Peer `yaml:"full_node_peers"` + DBSync string `yaml:"db_sync"` + DBReaders uint8 `yaml:"db_readers"` + DatabasePath string `yaml:"database_path"` + PeerDBPath string `yaml:"peer_db_path"` + PeersFilePath string `yaml:"peers_file_path"` + MultiprocessingStartMethod string `yaml:"multiprocessing_start_method"` + MaxDuplicateUnfinishedBlocks uint8 `yaml:"max_duplicate_unfinished_blocks"` + StartRPCServer bool `yaml:"start_rpc_server"` + EnableUPNP bool `yaml:"enable_upnp"` + SyncBlocksBehindThreshold uint16 `yaml:"sync_blocks_behind_threshold"` + ShortSyncBlocksBehindThreshold uint16 `yaml:"short_sync_blocks_behind_threshold"` + BadPeakCacheSize uint16 `yaml:"bad_peak_cache_size"` + ReservedCores uint8 `yaml:"reserved_cores"` + SingleThreaded bool `yaml:"single_threaded"` + PeerConnectInterval uint8 `yaml:"peer_connect_interval"` + PeerConnectTimeout uint8 `yaml:"peer_connect_timeout"` + TargetPeerCount uint16 `yaml:"target_peer_count"` + TargetOutboundPeerCount uint16 `yaml:"target_outbound_peer_count"` + ExemptPeerNetworks []string `yaml:"exempt_peer_networks"` + MaxInboundWallet uint8 `yaml:"max_inbound_wallet"` + MaxInboundFarmer uint8 `yaml:"max_inbound_farmer"` + MaxInboundTimelord uint8 `yaml:"max_inbound_timelord"` + RecentPeerThreshold uint16 `yaml:"recent_peer_threshold"` + SendUncompactInterval uint16 `yaml:"send_uncompact_interval"` + TargetUncompactProofs uint16 `yaml:"target_uncompact_proofs"` + SanitizeWeightProofOnly bool `yaml:"sanitize_weight_proof_only"` + WeightProofTimeout uint16 `yaml:"weight_proof_timeout"` + MaxSyncWait uint16 `yaml:"max_sync_wait"` + EnableProfiler bool `yaml:"enable_profiler"` + ProfileBlockValidation bool `yaml:"profile_block_validation"` + EnableMemoryProfiler bool `yaml:"enable_memory_profiler"` + LogSqliteCmds bool `yaml:"log_sqlite_cmds"` + MaxSubscribeItems uint32 `yaml:"max_subscribe_items"` + MaxSubscribeResponseItems uint32 `yaml:"max_subscribe_response_items"` + TrustedMaxSubscribeItems uint32 `yaml:"trusted_max_subscribe_items"` + TrustedMaxSubscribeResponseItems uint32 `yaml:"trusted_max_subscribe_response_items"` + DNSServers []string `yaml:"dns_servers"` + IntroducerPeer Peer `yaml:"introducer_peer"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + TrustedPeers map[string]string `yaml:"trusted_peers"` + SSL SSLConfig `yaml:"ssl"` + UseChiaLoopPolicy bool `yaml:"use_chia_loop_policy"` +} - return config, nil +// UIConfig settings for the UI +type UIConfig struct { + PortConfig `yaml:",inline"` + SSHFilename string `yaml:"ssh_filename"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + DaemonHost string `yaml:"daemon_host"` + DaemonPort uint16 `yaml:"daemon_port"` + DaemonSSL SSLConfig `yaml:"daemon_ssl"` } -// GetChiaRootPath returns the root path for the chia installation -func GetChiaRootPath() (string, error) { - if root, ok := os.LookupEnv("CHIA_ROOT"); ok { - return root, nil - } +// IntroducerConfig settings for introducers +type IntroducerConfig struct { + Host string `yaml:"host"` + PortConfig `yaml:",inline"` + MaxPeersToSend uint16 `yaml:"max_peers_to_send"` + RecentPeerThreshold uint16 `yaml:"recent_peer_threshold"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + SSL SSLConfig `yaml:"ssl"` +} - home, err := os.UserHomeDir() - if err != nil { - return "", err - } +// WalletConfig wallet configuration section +type WalletConfig struct { + PortConfig `yaml:",inline"` + EnableProfiler bool `yaml:"enable_profiler"` + EnableMemoryProfiler bool `yaml:"enable_memory_profiler"` + DBSync string `yaml:"db_sync"` + DBReaders uint8 `yaml:"db_readers"` + ConnectToUnknownPeers bool `yaml:"connect_to_unknown_peers"` + InitialNumPublicKeys uint16 `yaml:"initial_num_public_keys"` + ReusePublicKeyForChange map[string]bool `yaml:"reuse_public_key_for_change"` + DNSServers []string `yaml:"dns_servers"` + FullNodePeers []Peer `yaml:"full_node_peers"` + NFTMetadataCachePath string `yaml:"nft_cache"` + NFTMetadataCacheHashLength uint8 `yaml:"nft_metadata_cache_hash_length"` + MultiprocessingStartMethod string `yaml:"multiprocessing_start_method"` + Testing bool `yaml:"testing"` + DatabasePath string `yaml:"database_path"` + WalletPeersPath string `yaml:"wallet_peers_path"` + WalletPeersFilePath string `yaml:"wallet_peers_file_path"` + LogSqliteCmds bool `yaml:"log_sqlite_cmds"` + Logging LoggingConfig `yaml:"logging"` + NetworkOverrides NetworkOverrides `yaml:"network_overrides"` + SelectedNetwork string `yaml:"selected_network"` + TargetPeerCount uint16 `yaml:"target_peer_count"` + PeerConnectInterval uint8 `yaml:"peer_connect_interval"` + RecentPeerThreshold uint16 `yaml:"recent_peer_threshold"` + IntroducerPeer Peer `yaml:"introducer_peer"` + SSL SSLConfig `yaml:"ssl"` + TrustedPeers map[string]string `yaml:"trusted_peers"` + ShortSyncBlocksBehindThreshold uint16 `yaml:"short_sync_blocks_behind_threshold"` + InboundRateLimitPercent uint8 `yaml:"inbound_rate_limit_percent"` + OutboundRateLimitPercent uint8 `yaml:"outbound_rate_limit_percent"` + WeightProofTimeout uint16 `yaml:"weight_proof_timeout"` + AutomaticallyAddUnknownCats bool `yaml:"automatically_add_unknown_cats"` + TxResendTimeoutSecs uint16 `yaml:"tx_resend_timeout_secs"` + ResetSyncForFingerprint *int `yaml:"reset_sync_for_fingerprint"` + SpamFilterAfterNTxs uint16 `yaml:"spam_filter_after_n_txs"` + XCHSpamAmount uint64 `yaml:"xch_spam_amount"` + EnableNotifications bool `yaml:"enable_notifications"` + RequiredNotificationAmount uint64 `yaml:"required_notification_amount"` + UseDeltaSync bool `yaml:"use_delta_sync"` + // PuzzleDecorators + AutoClaim AutoClaim `yaml:"auto_claim"` + AutoSignTxs bool `yaml:"auto_sign_txs"` +} - root := filepath.Join(home, ".chia", "mainnet") +// AutoClaim settings for auto claim in wallet +type AutoClaim struct { + Enabled bool `yaml:"enabled"` + TxFee uint64 `yaml:"tx_fee"` + MinAmount uint64 `yaml:"min_amount"` + BatchSize uint16 `yaml:"batch_size"` +} - return root, nil +// DataLayerConfig datalayer configuration section +type DataLayerConfig struct { + WalletPeer Peer `yaml:"wallet_peer"` + DatabasePath string `yaml:"database_path"` + ServerFilesLocation string `yaml:"server_files_location"` + ClientTimeout uint16 `yaml:"client_timeout"` + ProxyURL string `yaml:"proxy_url,omitempty"` + HostIP string `yaml:"host_ip"` + HostPort uint16 `yaml:"host_port"` + ManageDataInterval uint16 `yaml:"manage_data_interval"` + SelectedNetwork string `yaml:"selected_network"` + StartRPCServer bool `yaml:"start_rpc_server"` + RPCServerMaxRequestBodySize uint32 `yaml:"rpc_server_max_request_body_size"` + LogSqliteCmds bool `yaml:"log_sqlite_cmds"` + EnableBatchAutoinsert bool `yaml:"enable_batch_autoinsert"` + Logging LoggingConfig `yaml:"logging"` + PortConfig `yaml:",inline"` + SSL SSLConfig `yaml:"ssl"` + Plugins DataLayerPlugins `yaml:"plugins"` + MaximumFullFileCount uint16 `yaml:"maximum_full_file_count"` } -// GetFullPath returns the full path to a particular filename within CHIA_ROOT -func (c *ChiaConfig) GetFullPath(filename string) string { - if filepath.IsAbs(filename) { - return filename - } - return filepath.Join(c.ChiaRoot, filename) +// DataLayerPlugins Settings for data layer plugins +type DataLayerPlugins struct { + // @TODO } -func (c *ChiaConfig) fillDatabasePath() { - c.FullNode.DatabasePath = strings.Replace(c.FullNode.DatabasePath, "CHALLENGE", c.FullNode.SelectedNetwork, 1) +// SimulatorConfig settings for simulator +type SimulatorConfig struct { + AutoFarm bool `yaml:"auto_farm"` + KeyFingerprint int `yaml:"key_fingerprint"` + FarmingAddress string `yaml:"farming_address"` + PlotDirectory string `yaml:"plot_directory"` + UseCurrentTime bool `yaml:"use_current_time"` } diff --git a/pkg/config/env.go b/pkg/config/env.go new file mode 100644 index 0000000..d5d1c85 --- /dev/null +++ b/pkg/config/env.go @@ -0,0 +1,196 @@ +package config + +import ( + "fmt" + "math/big" + "os" + "reflect" + "strconv" + "strings" + + "github.com/chia-network/go-chia-libs/pkg/types" +) + +// FillValuesFromEnvironment reads environment variables starting with `chia.` and edits the config based on the config path +// chia.selected_network=mainnet would set the top level `selected_network: mainnet` +// chia.full_node.port=8444 would set full_node.port to 8444 +// +// # Complex data structures can be passed in as JSON strings and they will be parsed out into the datatype specified for the config prior to being inserted +// +// chia.network_overrides.constants.mainnet='{"GENESIS_CHALLENGE":"abc123","GENESIS_PRE_FARM_POOL_PUZZLE_HASH":"xyz789"}' +func (c *ChiaConfig) FillValuesFromEnvironment() error { + valuesToUpdate := getAllChiaVars() + for _, pAndV := range valuesToUpdate { + err := c.SetFieldByPath(pAndV.path, pAndV.value) + if err != nil { + return err + } + } + + return nil +} + +type pathAndValue struct { + path []string + value string +} + +func getAllChiaVars() map[string]pathAndValue { + // Most shells don't allow `.` in env names, but docker will and its easier to visualize the `.`, so support both + // `.` and `__` as valid path segment separators + // chia.full_node.port + // chia__full_node__port + separators := []string{".", "__"} + envVars := os.Environ() + finalVars := map[string]pathAndValue{} + + for _, sep := range separators { + prefix := fmt.Sprintf("chia%s", sep) + for _, env := range envVars { + if strings.HasPrefix(env, prefix) { + pair := strings.SplitN(env, "=", 2) + if len(pair) == 2 { + finalVars[pair[0][len(prefix):]] = pathAndValue{ + path: strings.Split(pair[0], sep)[1:], // This is the path in the config to the value to edit minus the "chia" prefix + value: pair[1], + } + } + } + } + } + + return finalVars +} + +// SetFieldByPath iterates through each item in path to find the corresponding `yaml` tag in the struct +// Once found, we move to the next item in path and look for that key within the first element +// If any element is not found, an error will be returned +func (c *ChiaConfig) SetFieldByPath(path []string, value any) error { + v := reflect.ValueOf(c).Elem() + return setFieldByPath(v, path, value) +} + +func setFieldByPath(v reflect.Value, path []string, value any) error { + if len(path) == 0 { + return fmt.Errorf("invalid path") + } + + for i := 0; i < v.NumField(); i++ { + field := v.Type().Field(i) + yamlTagRaw := field.Tag.Get("yaml") + yamlTag := strings.Split(yamlTagRaw, ",")[0] + + if yamlTagRaw == ",inline" && field.Anonymous { + // Check the inline struct + if err := setFieldByPath(v.Field(i), path, value); err != nil { + return err + } + } else if yamlTag == path[0] { + // We found a match for the current level of "paths" + // If we only have 1 element left in paths, then we can set the value + // Otherwise, we can recursively call setFieldByPath again, with the remaining elements of path + fieldValue := v.Field(i) + if len(path) > 1 { + if fieldValue.Kind() == reflect.Map { + mapKey := reflect.ValueOf(path[1]) + if !mapKey.Type().ConvertibleTo(fieldValue.Type().Key()) { + return fmt.Errorf("invalid map key type %s", mapKey.Type()) + } + mapValue := fieldValue.MapIndex(mapKey) + if mapValue.IsValid() { + if !mapValue.CanSet() { + // Create a new writable map and copy over the existing data + newMapValue := reflect.New(fieldValue.Type().Elem()).Elem() + newMapValue.Set(mapValue) + mapValue = newMapValue + } + err := setFieldByPath(mapValue, path[2:], value) + if err != nil { + return err + } + fieldValue.SetMapIndex(mapKey, mapValue) + return nil + } + } else { + return setFieldByPath(fieldValue, path[1:], value) + } + } + + if !fieldValue.CanSet() { + return fmt.Errorf("cannot set field %s", path[0]) + } + + // Special Cases + if fieldValue.Type() == reflect.TypeOf(types.Uint128{}) { + strValue, ok := value.(string) + if !ok { + return fmt.Errorf("expected string for Uint128 field, got %T", value) + } + bigIntValue := new(big.Int) + _, ok = bigIntValue.SetString(strValue, 10) + if !ok { + return fmt.Errorf("invalid string for big.Int: %s", strValue) + } + fieldValue.Set(reflect.ValueOf(types.Uint128FromBig(bigIntValue))) + return nil + } + + val := reflect.ValueOf(value) + + if fieldValue.Type() != val.Type() { + if val.Type().ConvertibleTo(fieldValue.Type()) { + val = val.Convert(fieldValue.Type()) + } else { + convertedVal, err := convertValue(value, fieldValue.Type()) + if err != nil { + return err + } + val = reflect.ValueOf(convertedVal) + } + } + + fieldValue.Set(val) + + return nil + } + } + + return nil +} + +func convertValue(value interface{}, targetType reflect.Type) (interface{}, error) { + switch targetType.Kind() { + case reflect.Uint8: + v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 8) + if err != nil { + return nil, err + } + return uint8(v), nil + case reflect.Uint16: + v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 16) + if err != nil { + return nil, err + } + return uint16(v), nil + case reflect.Uint32: + v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 32) + if err != nil { + return nil, err + } + return uint32(v), nil + case reflect.Uint64: + v, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 64) + if err != nil { + return nil, err + } + return v, nil + case reflect.Bool: + v, err := strconv.ParseBool(fmt.Sprintf("%v", value)) + if err != nil { + return nil, err + } + return v, nil + default: + return nil, fmt.Errorf("unsupported conversion to %s", targetType.Kind()) + } +} diff --git a/pkg/config/env_test.go b/pkg/config/env_test.go new file mode 100644 index 0000000..3ecfa06 --- /dev/null +++ b/pkg/config/env_test.go @@ -0,0 +1,69 @@ +package config_test + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/chia-network/go-chia-libs/pkg/config" + "github.com/chia-network/go-chia-libs/pkg/types" +) + +func TestChiaConfig_SetFieldByPath(t *testing.T) { + defaultConfig, err := config.LoadDefaultConfig() + assert.NoError(t, err) + // Make assertions about the default state, to ensure the assumed initial values are correct + assert.Equal(t, uint16(8444), defaultConfig.FullNode.Port) + assert.Equal(t, uint16(8555), defaultConfig.FullNode.RPCPort) + assert.NotNil(t, defaultConfig.NetworkOverrides.Constants["mainnet"]) + assert.Equal(t, defaultConfig.NetworkOverrides.Constants["mainnet"].DifficultyConstantFactor, types.Uint128{}) + assert.Equal(t, defaultConfig.SelectedNetwork, "mainnet") + assert.Equal(t, defaultConfig.Logging.LogLevel, "WARNING") + + err = defaultConfig.SetFieldByPath([]string{"full_node", "port"}, "1234") + assert.NoError(t, err) + assert.Equal(t, uint16(1234), defaultConfig.FullNode.Port) + + err = defaultConfig.SetFieldByPath([]string{"full_node", "rpc_port"}, "5678") + assert.NoError(t, err) + assert.Equal(t, uint16(5678), defaultConfig.FullNode.RPCPort) + + err = defaultConfig.SetFieldByPath([]string{"network_overrides", "constants", "mainnet", "DIFFICULTY_CONSTANT_FACTOR"}, "44445555") + assert.NoError(t, err) + assert.NotNil(t, defaultConfig.NetworkOverrides.Constants["mainnet"]) + assert.Equal(t, types.Uint128From64(44445555), defaultConfig.NetworkOverrides.Constants["mainnet"].DifficultyConstantFactor) + + err = defaultConfig.SetFieldByPath([]string{"selected_network"}, "unittestnet") + assert.NoError(t, err) + assert.Equal(t, defaultConfig.SelectedNetwork, "unittestnet") + + err = defaultConfig.SetFieldByPath([]string{"logging", "log_level"}, "INFO") + assert.NoError(t, err) + assert.Equal(t, defaultConfig.Logging.LogLevel, "INFO") +} + +func TestChiaConfig_FillValuesFromEnvironment(t *testing.T) { + defaultConfig, err := config.LoadDefaultConfig() + assert.NoError(t, err) + // Make assertions about the default state, to ensure the assumed initial values are correct + assert.Equal(t, uint16(8444), defaultConfig.FullNode.Port) + assert.Equal(t, uint16(8555), defaultConfig.FullNode.RPCPort) + assert.NotNil(t, defaultConfig.NetworkOverrides.Constants["mainnet"]) + assert.Equal(t, defaultConfig.NetworkOverrides.Constants["mainnet"].DifficultyConstantFactor, types.Uint128{}) + assert.Equal(t, defaultConfig.SelectedNetwork, "mainnet") + assert.Equal(t, defaultConfig.Logging.LogLevel, "WARNING") + + assert.NoError(t, os.Setenv("chia.full_node.port", "1234")) + assert.NoError(t, os.Setenv("chia__full_node__rpc_port", "5678")) + assert.NoError(t, os.Setenv("chia.network_overrides.constants.mainnet.DIFFICULTY_CONSTANT_FACTOR", "44445555")) + assert.NoError(t, os.Setenv("chia.selected_network", "unittestnet")) + assert.NoError(t, os.Setenv("chia__logging__log_level", "INFO")) + + assert.NoError(t, defaultConfig.FillValuesFromEnvironment()) + assert.Equal(t, uint16(1234), defaultConfig.FullNode.Port) + assert.Equal(t, uint16(5678), defaultConfig.FullNode.RPCPort) + assert.Equal(t, types.Uint128From64(44445555), defaultConfig.NetworkOverrides.Constants["mainnet"].DifficultyConstantFactor) + assert.Equal(t, defaultConfig.SelectedNetwork, "unittestnet") + assert.Equal(t, defaultConfig.Logging.LogLevel, "INFO") +} diff --git a/pkg/config/initial-config.yml b/pkg/config/initial-config.yml new file mode 100644 index 0000000..934010d --- /dev/null +++ b/pkg/config/initial-config.yml @@ -0,0 +1,736 @@ +# This is the default chia config as of 2.4.0 +# https://github.com/Chia-Network/chia-blockchain/blob/2.4.0/chia/util/initial-config.yaml +min_mainnet_k_size: 32 + +# Send a ping to all peers after ping_interval seconds +ping_interval: 120 +self_hostname: &self_hostname "localhost" +prefer_ipv6: False +rpc_timeout: 300 +daemon_port: 55400 +daemon_max_message_size: 50000000 # maximum size of RPC message in bytes +daemon_heartbeat: 300 # sets the heartbeat for ping/ping interval and timeouts +daemon_allow_tls_1_2: False # if True, allow TLS 1.2 for daemon connections +inbound_rate_limit_percent: 100 +outbound_rate_limit_percent: 30 + +network_overrides: &network_overrides + constants: + mainnet: + GENESIS_CHALLENGE: ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: "d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc" + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: "3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af" + testnet0: + MIN_PLOT_SIZE: 18 + GENESIS_CHALLENGE: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: "d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc" + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: "3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af" + testnet2: + MIN_PLOT_SIZE: 18 + GENESIS_CHALLENGE: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: "d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc" + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: "3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af" + testnet3: + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + GENESIS_CHALLENGE: ca7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015af + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc + MIN_PLOT_SIZE: 18 + MEMPOOL_BLOCK_BUFFER: 10 + testnet4: + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + GENESIS_CHALLENGE: dd7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015af + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc + MIN_PLOT_SIZE: 18 + MEMPOOL_BLOCK_BUFFER: 10 + EPOCH_BLOCKS: 768 + DIFFICULTY_STARTING: 30 + testnet5: + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + GENESIS_CHALLENGE: ee7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015af + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc + MIN_PLOT_SIZE: 18 + MEMPOOL_BLOCK_BUFFER: 10 + EPOCH_BLOCKS: 768 + DIFFICULTY_STARTING: 30 + testnet7: + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + GENESIS_CHALLENGE: 117816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015af + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc + MIN_PLOT_SIZE: 18 + MEMPOOL_BLOCK_BUFFER: 50 + EPOCH_BLOCKS: 768 + DIFFICULTY_STARTING: 30 + testnet10: + AGG_SIG_ME_ADDITIONAL_DATA: ae83525ba8d1dd3f09b277de18ca3e43fc0af20d20c4b3e92ef2a48bd291ccb2 + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + DIFFICULTY_STARTING: 30 + EPOCH_BLOCKS: 768 + GENESIS_CHALLENGE: ae83525ba8d1dd3f09b277de18ca3e43fc0af20d20c4b3e92ef2a48bd291ccb2 + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 3d8765d3a597ec1d99663f6c9816d915b9f68613ac94009884c4addaefcce6af + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc + MEMPOOL_BLOCK_BUFFER: 10 + MIN_PLOT_SIZE: 18 + SOFT_FORK2_HEIGHT: 3000000 + SOFT_FORK4_HEIGHT: 4465000 + # planned 2.0 release is July 26, height 2965036 on testnet + # 1 week later + HARD_FORK_HEIGHT: 2997292 + # November 2023 + HARD_FORK_FIX_HEIGHT: 3426000 + # another 2 weeks later + PLOT_FILTER_128_HEIGHT: 3061804 + # 3 years later + PLOT_FILTER_64_HEIGHT: 8010796 + # 3 years later + PLOT_FILTER_32_HEIGHT: 13056556 + testnet11: + AGG_SIG_ME_ADDITIONAL_DATA: 37a90eb5185a9c4439a91ddc98bbadce7b4feba060d50116a067de66bf236615 + DIFFICULTY_CONSTANT_FACTOR: 10052721566054 + DIFFICULTY_STARTING: 30 + EPOCH_BLOCKS: 768 + GENESIS_CHALLENGE: 37a90eb5185a9c4439a91ddc98bbadce7b4feba060d50116a067de66bf236615 + GENESIS_PRE_FARM_FARMER_PUZZLE_HASH: 08296fc227decd043aee855741444538e4cc9a31772c4d1a9e6242d1e777e42a + GENESIS_PRE_FARM_POOL_PUZZLE_HASH: 3ef7c233fc0785f3c0cae5992c1d35e7c955ca37a423571c1607ba392a9d12f7 + MEMPOOL_BLOCK_BUFFER: 10 + MIN_PLOT_SIZE: 18 + NETWORK_TYPE: 1 + SUB_SLOT_ITERS_STARTING: 67108864 + # Forks activated from the beginning on this network + HARD_FORK_HEIGHT: 0 + HARD_FORK_FIX_HEIGHT: 0 + SOFT_FORK4_HEIGHT: 641500 + SOFT_FORK5_HEIGHT: 1340000 + PLOT_FILTER_128_HEIGHT: 6029568 + PLOT_FILTER_64_HEIGHT: 11075328 + PLOT_FILTER_32_HEIGHT: 16121088 + config: + mainnet: + address_prefix: "xch" + default_full_node_port: 8444 + testnet0: + address_prefix: "txch" + default_full_node_port: 58444 + testnet1: + address_prefix: "txch" + testnet2: + address_prefix: "txch" + testnet3: + address_prefix: "txch" + testnet4: + address_prefix: "txch" + testnet7: + address_prefix: "txch" + default_full_node_port: 58444 + testnet10: + address_prefix: "txch" + default_full_node_port: 58444 + testnet11: + address_prefix: "txch" + default_full_node_port: 58444 + +selected_network: &selected_network "mainnet" +ALERTS_URL: https://download.chia.net/notify/mainnet_alert.txt +CHIA_ALERTS_PUBKEY: 89b7fd87cb56e926ecefb879a29aae308be01f31980569f6a75a69d2a9a69daefd71fb778d865f7c50d6c967e3025937 + +# public ssl ca is included in source code +# Private ssl ca is used for trusted connections between machines user owns +private_ssl_ca: + crt: "config/ssl/ca/private_ca.crt" + key: "config/ssl/ca/private_ca.key" + +chia_ssl_ca: + crt: "config/ssl/ca/chia_ca.crt" + key: "config/ssl/ca/chia_ca.key" + +daemon_ssl: + private_crt: "config/ssl/daemon/private_daemon.crt" + private_key: "config/ssl/daemon/private_daemon.key" + +# Controls logging of all servers (harvester, farmer, etc..). Each one can be overridden. +logging: &logging + log_stdout: False # If True, outputs to stdout instead of a file + log_filename: "log/debug.log" + log_level: "WARNING" # Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET + log_maxfilesrotation: 7 # Max files in rotation. Default value 7 if the key is not set + log_maxbytesrotation: 52428800 # Max bytes logged before rotating logs + log_use_gzip: False # Use gzip to compress rotated logs + log_syslog: False # If True, outputs to SysLog host and port specified + log_syslog_host: "localhost" # Send logging messages to a remote or local Unix syslog + log_syslog_port: 514 # UDP port of the remote or local Unix syslog + +seeder: + # The fake full node used for crawling will run on this port. + port: 8444 + # Most full nodes on the network run on this port. (i.e. 8444 for mainnet, 58444 for testnet). + other_peers_port: 8444 + # What port to run the DNS server on, (this is useful if you are already using port 53 for DNS). + dns_port: 53 + # This will override the default full_node.peer_connect_timeout for the crawler full node + peer_connect_timeout: 2 + # Path to crawler DB. Defaults to $CHIA_ROOT/crawler.db + crawler_db_path: "crawler.db" + # Peers used for the initial run. + bootstrap_peers: + - "node.chia.net" + # Only consider nodes synced at least to this height. + minimum_height: 240000 + # How many of a particular version we need to see before reporting it in the logs + minimum_version_count: 100 + domain_name: "seeder.example.com." + nameserver: "example.com." + ttl: 300 + soa: + rname: "hostmaster.example.com" # all @ symbols need to be replaced with . in dns records. + serial_number: 1619105223 + refresh: 10800 + retry: 10800 + expire: 604800 + minimum: 1800 + network_overrides: *network_overrides + selected_network: *selected_network + logging: *logging + # Crawler is its own standalone service within the seeder component + crawler: + start_rpc_server: True + rpc_port: 8561 + ssl: + private_crt: "config/ssl/crawler/private_crawler.crt" + private_key: "config/ssl/crawler/private_crawler.key" + +harvester: + farmer_peers: + - host: *self_hostname + port: 8447 + + # If True, starts an RPC server at the following port + start_rpc_server: True + rpc_port: 8560 + num_threads: 30 + plots_refresh_parameter: + interval_seconds: 120 # The interval in seconds to refresh the plot file manager + retry_invalid_seconds: 1200 # How long to wait before re-trying plots which failed to load + batch_size: 300 # How many plot files the harvester processes before it waits batch_sleep_milliseconds + batch_sleep_milliseconds: 1 # Milliseconds the harvester sleeps between batch processing + + # If True use parallel reads in chiapos + parallel_read: True + + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + # Plots are searched for in the following directories + plot_directories: [] + recursive_plot_scan: False # If True the harvester scans plots recursively in the provided directories. + + ssl: + private_crt: "config/ssl/harvester/private_harvester.crt" + private_key: "config/ssl/harvester/private_harvester.key" + + private_ssl_ca: + crt: "config/ssl/ca/private_ca.crt" + key: "config/ssl/ca/private_ca.key" + + chia_ssl_ca: + crt: "config/ssl/ca/chia_ca.crt" + key: "config/ssl/ca/chia_ca.key" + + # Compressed harvesting. + parallel_decompressor_count: 0 + # If set to 0, `decompressor_thread_count` will default to half of nproc available on the machine. + # A non-zero number overrides this default. + decompressor_thread_count: 0 + disable_cpu_affinity: False + # Ignore compressed plots with compression level greater than this. + max_compression_level_allowed: 7 + use_gpu_harvesting: False + gpu_index: 0 + enforce_gpu_index: False + # If no decompressor is available after `decompressor_timeout` seconds, abort the current operation. + decompressor_timeout: 20 + +pool: + # Replace this with a real receive address + # xch_target_address: txch102gkhhzs60grx7cfnpng5n6rjecr89r86l5s8xux2za8k820cxsq64ssdg + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + +farmer: + # The farmer server (if run) will run on this port + port: 8447 + # The farmer will attempt to connect to these full nodes + full_node_peers: + - host: *self_hostname + port: 8444 + + pool_public_keys: [] + + # Replace this with a real receive address + # xch_target_address: txch102gkhhzs60grx7cfnpng5n6rjecr89r86l5s8xux2za8k820cxsq64ssdg + + # If True, starts an RPC server at the following port + start_rpc_server: True + rpc_port: 8559 + + # when enabled, the farmer will print a pstats profile to the + # root_dir/profile-farmer directory every second. + # analyze with python -m chia.util.profiler + enable_profiler: False + + # To send a share to a pool, a proof of space must have required_iters less than this number + pool_share_threshold: 1000 + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + ssl: + private_crt: "config/ssl/farmer/private_farmer.crt" + private_key: "config/ssl/farmer/private_farmer.key" + public_crt: "config/ssl/farmer/public_farmer.crt" + public_key: "config/ssl/farmer/public_farmer.key" + +# Don't run this unless you want to run VDF clients on the local machine. +timelord_launcher: + # The server where the VDF clients will connect to. + host: *self_hostname + port: 8000 + # Number of VDF client processes to keep alive in the local machine. + process_count: 3 + logging: *logging + +timelord: + # Provides a list of VDF clients expected to connect to this timelord. + # For each client, an IP is provided, together with the estimated iterations per second. + vdf_clients: + ip: + - *self_hostname + - localhost + - 127.0.0.1 + ips_estimate: + - 150000 + full_node_peers: + - host: *self_hostname + port: 8444 + # Maximum number of seconds allowed for a client to reconnect to the server. + max_connection_time: 60 + # The ip and port where the TCP clients will connect. + vdf_server: + host: *self_hostname + port: 8000 + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + # fast_algorithm is a faster proof generation algorithm. This speed increase + # requires much less memory usage and a does not have the risk of OOM that + # the normal timelord has but requires significantly more cores doing + # parallel proof generation and creates a larger and slower to verify + # resulting proof. + # An Intel Core i9-10900K can run 2 normal vdf_clients at ~221,000 ips + # without slowing down but running more than 1 with fast_algorithm will + # run each vdf_client slower. + fast_algorithm: False + # Bluebox (sanitizing timelord): + # If set 'True', the timelord will create compact proofs of time, instead of + # extending the chain. The attribute 'fast_algorithm' won't apply if timelord + # is running in bluebox_mode. + # You must set 'send_uncompact_interval' in 'full_node' > 0 in the full_node + # section below to have full_node send existing time proofs to be sanitized. + bluebox_mode: False + # This runs a less CPU intensive bluebox. Runs for windows. Settings apply as for `bluebox_mode`. + # Optionally set `process_count` in `timelord_launcher` to 0, since timelord launcher won't be used in this case. + # IMPORTANT! `bluebox_mode` must also be set to True for this option to work. + slow_bluebox: False + # If `slow_bluebox` is True, launches `slow_bluebox_process_count` processes. + slow_bluebox_process_count: 1 + + multiprocessing_start_method: default + + start_rpc_server: True + rpc_port: 8557 + + ssl: + private_crt: "config/ssl/timelord/private_timelord.crt" + private_key: "config/ssl/timelord/private_timelord.key" + public_crt: "config/ssl/timelord/public_timelord.crt" + public_key: "config/ssl/timelord/public_timelord.key" + +full_node: + # The full node server (if run) will run on this port + port: 8444 + # The full node will attempt to connect to these full nodes + full_node_peers: [] + + # controls the sync-to-disk behavior of the database connection. Can be one of: + # "on" enables syncing to disk, minimizes risk of corrupting the DB in + # power-loss, disk failure or kernel panics + # "full" enables extra syncing to disk for additional safety from + # power-failures and kernel panics + # "off" disables syncing, lightens disk load and improves performance. + # increases risk of corrupting DB in power-loss, disk failure or + # kernel panics + # "auto" on/off is decided based on a heuristic of how likely a failure is on + # the particular system we're running on. Defaults to "full". + db_sync: "auto" + + # the number of threads used to read from the blockchain database + # concurrently. There's always only 1 writer, but the number of readers is + # configurable + db_readers: 4 + + # Run multiple nodes with different databases by changing the database_path + database_path: db/blockchain_v2_CHALLENGE.sqlite + # peer_db_path is deprecated and has been replaced by peers_file_path + peer_db_path: db/peer_table_node.sqlite + peers_file_path: db/peers.dat + + multiprocessing_start_method: default + + # The maximum number of UnfinishedBlocks we accept (and forward) with the + # same reward hash (but different foliage hashes). Traditionally this was + # effectively 1, meaning whichever UnfinishedBlock we saw first was the only + # one we forwarded. In 2.2.0 we relaxed the protocol to allow some + # duplicates be forwarded, in order to allow the timelords to, + # deterministically, pick which one to infuse + max_duplicate_unfinished_blocks: 3 + + # If True, starts an RPC server at the following port + start_rpc_server: True + rpc_port: 8555 + + # Use UPnP to attempt to allow other full nodes to reach your node behind a gateway + enable_upnp: True + + # If node is more than these blocks behind, will do a sync (long sync) + sync_blocks_behind_threshold: 300 + + # If node is more than these blocks behind, will do a short batch-sync, if it's less, will do a backtrack sync + short_sync_blocks_behind_threshold: 20 + + bad_peak_cache_size: 100 + + # When creating process pools the process count will generally be the CPU count minus + # this reserved core count. + reserved_cores: 0 + + # set this to true to not offload heavy lifting into separate child processes. + # this option is mostly useful when profiling, since only the main process is + # profiled. + single_threaded: False + + # How often to initiate outbound connections to other full nodes. + peer_connect_interval: 30 + # How long to wait for a peer connection + peer_connect_timeout: 30 + # Accept peers until this number of connections + target_peer_count: 40 + # Initiate outbound connections until this number is hit. + target_outbound_peer_count: 8 + # IPv4/IPv6 network addresses and CIDR blocks allowed to connect even when target_peer_count has been hit. + # exempt_peer_networks: ["192.168.0.3", "192.168.1.0/24", "fe80::/10", "2606:4700:4700::64/128"] + exempt_peer_networks: [] + # Accept at most # of inbound connections for different node types. + max_inbound_wallet: 20 + max_inbound_farmer: 10 + max_inbound_timelord: 5 + # Only connect to peers who we have heard about in the last recent_peer_threshold seconds + recent_peer_threshold: 6000 + + # Send to a Bluebox (sanitizing timelord) uncompact blocks once every + # 'send_uncompact_interval' seconds. Set to 0 if you don't use this feature. + send_uncompact_interval: 0 + # At every 'send_uncompact_interval' seconds, send blueboxes 'target_uncompact_proofs' proofs to be normalized. + target_uncompact_proofs: 100 + # Setting this flag as True, blueboxes will sanitize only data needed in weight proof calculation, as opposed to whole blocks. + # Default is set to False, as the network needs only one or two blueboxes like this. + sanitize_weight_proof_only: False + # timeout for weight proof request + weight_proof_timeout: &weight_proof_timeout 360 + + # when the full node enters sync-mode, we wait until we have collected peaks + # from at least 3 peers, or until we've waitied this many seconds + max_sync_wait: 30 + + # when enabled, the full node will print a pstats profile to the + # root_dir/profile-node directory every second. + # analyze with python -m chia.util.profiler + enable_profiler: False + + # when enabled, each time a block is validated, the python profiler is + # engaged. If the validation takes more than 2 seconds, the profile is saved + # to disk, in the chia root/block-validation-profile + profile_block_validation: False + + enable_memory_profiler: False + + # this is a debug and profiling facility that logs all SQLite commands to a + # separate log file (under logging/sql.log). + log_sqlite_cmds: False + + # Number of coin_ids | puzzle hashes that node will let wallets subscribe to + max_subscribe_items: 200000 + + # the maximum number of CoinStates will be returned by a RegisterForPhUpdates + # request, for untrusted peers + max_subscribe_response_items: 100000 + + # Number of coin_ids | puzzle hashes that node will let local wallets subscribe to + trusted_max_subscribe_items: 2000000 + + # the maximum number of CoinStates will be returned by a RegisterForPhUpdates + # request, for trusted peers + trusted_max_subscribe_response_items: 500000 + + # List of trusted DNS seeders to bootstrap from. + # If you modify this, please change the hardcode as well from FullNode.set_server() + dns_servers: &dns_servers + - "dns-introducer.chia.net" + - "chia.ctrlaltdel.ch" + - "seeder.dexie.space" + - "chia-seeder.h9.com" + - "chia.hoffmang.com" + - "seeder.xchpool.org" + introducer_peer: + host: introducer.chia.net # Chia AWS introducer IPv4/IPv6 + port: 8444 + enable_private_networks: False + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + # Node IDs of trusted wallet peers, allows using more permissive limits on sync + trusted_peers: + 0ThisisanexampleNodeID7ff9d60f1c3fa270c213c0ad0cb89c01274634a7c3cb7: Does_not_matter + + ssl: + private_crt: "config/ssl/full_node/private_full_node.crt" + private_key: "config/ssl/full_node/private_full_node.key" + public_crt: "config/ssl/full_node/public_full_node.crt" + public_key: "config/ssl/full_node/public_full_node.key" + use_chia_loop_policy: True + +ui: + # Which port to use to communicate with the full node + rpc_port: 8555 + + # This SSH key is for the ui SSH server + ssh_filename: config/ssh_host_key + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + # this is where the electron UI will find its daemon + # defaults to the one running locally with its private keys + daemon_host: *self_hostname + daemon_port: 55400 + daemon_ssl: + private_crt: config/ssl/daemon/private_daemon.crt + private_key: config/ssl/daemon/private_daemon.key + +introducer: + host: *self_hostname + port: 8445 + max_peers_to_send: 20 + # The introducer will only return peers it has seen in the last + # recent_peer_threshold seconds + recent_peer_threshold: 6000 + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + ssl: + public_crt: "config/ssl/full_node/public_full_node.crt" + public_key: "config/ssl/full_node/public_full_node.key" + +wallet: + rpc_port: 9256 + + # when enabled, the wallet will print a pstats profile to the + # root_dir/profile-wallet directory every second. + # analyze with python -m chia.util.profiler + enable_profiler: False + + enable_memory_profiler: False + + # see description for full_node.db_sync + db_sync: auto + + # the number of threads used to read from the wallet database + # concurrently. There's always only 1 writer, but the number of readers is + # configurable + db_readers: 2 + + connect_to_unknown_peers: True + + initial_num_public_keys: 425 + reuse_public_key_for_change: + #Add your wallet fingerprint here, this is an example. + "2999502625": False + + dns_servers: *dns_servers + + full_node_peers: + - host: *self_hostname + port: 8444 + # The path of NFT off-chain metadata cache + nft_metadata_cache_path: "nft_cache" + # The length of NFT ID prefix will be used as hash index + nft_metadata_cache_hash_length: 3 + multiprocessing_start_method: default + + testing: False + # v2 used by the light wallet sync protocol + database_path: wallet/db/blockchain_wallet_v2_CHALLENGE_KEY.sqlite + # wallet_peers_path is deprecated and has been replaced by wallet_peers_file_path + wallet_peers_path: wallet/db/wallet_peers.sqlite + wallet_peers_file_path: wallet/db/wallet_peers.dat + + # this is a debug and profiling facility that logs all SQLite commands to a + # separate log file (under logging/wallet_sql.log). + log_sqlite_cmds: False + + logging: *logging + network_overrides: *network_overrides + selected_network: *selected_network + + target_peer_count: 3 + peer_connect_interval: 60 + # The introducer will only return peers who it has seen in the last + # recent_peer_threshold seconds + recent_peer_threshold: 6000 + + introducer_peer: + host: introducer.chia.net # Chia AWS introducer IPv4/IPv6 + port: 8444 + enable_private_networks: False + + ssl: + private_crt: "config/ssl/wallet/private_wallet.crt" + private_key: "config/ssl/wallet/private_wallet.key" + public_crt: "config/ssl/wallet/public_wallet.crt" + public_key: "config/ssl/wallet/public_wallet.key" + + # Node IDs of trusted full node peers, for performing a fast trusted wallet sync + trusted_peers: + 0ThisisanexampleNodeID7ff9d60f1c3fa270c213c0ad0cb89c01274634a7c3cb9: Does_not_matter + + short_sync_blocks_behind_threshold: 20 + + # wallet overrides for limits + inbound_rate_limit_percent: 100 + outbound_rate_limit_percent: 60 + + # timeout for weight proof request + weight_proof_timeout: *weight_proof_timeout + + # if an unknown CAT belonging to us is seen, a wallet will be automatically created + # the user accepts the risk/responsibility of verifying the authenticity and origin of unknown CATs + automatically_add_unknown_cats: False + + # Interval to resend unconfirmed transactions, even if previously accepted into Mempool + tx_resend_timeout_secs: 1800 + + # Reset wallet sync data on start for given fingerprint + reset_sync_for_fingerprint: null + + # After n received unspent transactions, the spam filter will be enabled, which will filter out received + # coins with very small value. Any standard TX under xch_spam_amount is filtered + spam_filter_after_n_txs: 200 + xch_spam_amount: 1000000 + # Enable notifications from parties on chain + enable_notifications: True + # The amount someone has to pay you in mojos for you to see their notification + required_notification_amount: 10000000 + + # Enabling the delta sync can under certain circumstances lead to missing coin states during re-orgs + use_delta_sync: False + + ################################# + # Inner puzzle decorators # + ################################# + # Default puzzle decorator settings, this will apply to all asset wallets under the same key + # Here is an example + # puzzle_decorators: + # 2999502625: + # - decorator: "CLAWBACK" + # clawback_timelock: 3600 + # - decorator: "DECORATOR_TYPE" #If you want to add more decorators + # parameter1: 0 + + auto_claim: + enabled: False + tx_fee: 0 + min_amount: 0 + batch_size: 50 + + auto_sign_txs: True + +data_layer: + # TODO: consider name + # TODO: organize consistently with other sections + wallet_peer: + host: localhost + port: 9256 + + database_path: "data_layer/db/data_layer_CHALLENGE.sqlite" + # The location where the server files will be stored. + server_files_location: "data_layer/db/server_files_location_CHALLENGE" + # The timeout for the client to download a file from a server + client_timeout: 15 + # If you need use a proxy for download data you can use this setting sample + # proxy_url: http://localhost:8888 + + # Data for running a data layer server. + host_ip: 0.0.0.0 + host_port: 8575 + # Data for running a data layer client. + manage_data_interval: 60 + selected_network: *selected_network + # If True, starts an RPC server at the following port + start_rpc_server: True + # TODO: what considerations are there in choosing this? + rpc_port: 8562 + rpc_server_max_request_body_size: 26214400 + + # this is a debug and profiling facility that logs all SQLite commands to a + # separate log file (under logging/data_sql.log). + log_sqlite_cmds: False + + # Speeds up autoinserts. Disable to perform inserts one by one instead of in a batch. + enable_batch_autoinsert: True + + logging: *logging + + ssl: + private_crt: "config/ssl/data_layer/private_data_layer.crt" + private_key: "config/ssl/data_layer/private_data_layer.key" + public_crt: "config/ssl/data_layer/public_data_layer.crt" + public_key: "config/ssl/data_layer/public_data_layer.key" + + plugins: + uploaders: [] + downloaders: [] + maximum_full_file_count: 1 + +simulator: + # Should the simulator farm a block whenever a transaction is in mempool + auto_farm: True + + # The key used by the simulator to farm blocks and send transactions + key_fingerprint: + + # The target address for any blocks that are farmed + farming_address: + + # the directory used to save plots + # for the plot directory below the final directory will be: ~/.chia/simulator/plots + plot_directory: simulator/plots + + # Should we use real time in the simulated chain? + # most tests don't need this, however it is pretty important when writing ChiaLisp + use_current_time: True diff --git a/pkg/config/load.go b/pkg/config/load.go new file mode 100644 index 0000000..8998291 --- /dev/null +++ b/pkg/config/load.go @@ -0,0 +1,86 @@ +package config + +import ( + // Need to embed the default config into the library + _ "embed" + "fmt" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" +) + +//go:embed initial-config.yml +var initialConfig []byte + +// GetChiaConfig returns a struct containing the config.yaml values +func GetChiaConfig() (*ChiaConfig, error) { + rootPath, err := GetChiaRootPath() + if err != nil { + return nil, err + } + + configPath := filepath.Join(rootPath, "config", "config.yaml") + if _, err = os.Stat(configPath); os.IsNotExist(err) { + return nil, fmt.Errorf("chia config file not found at %s. Ensure CHIA_ROOT is set to the correct chia root", configPath) + } + + configBytes, err := os.ReadFile(configPath) + if err != nil { + return nil, err + } + + return commonLoad(configBytes, rootPath) +} + +// LoadDefaultConfig loads the initial-config bundled in go-chia-libs +func LoadDefaultConfig() (*ChiaConfig, error) { + rootPath, err := GetChiaRootPath() + if err != nil { + return nil, err + } + return commonLoad(initialConfig, rootPath) +} + +func commonLoad(configBytes []byte, rootPath string) (*ChiaConfig, error) { + config := &ChiaConfig{} + + err := yaml.Unmarshal(configBytes, config) + if err != nil { + return nil, err + } + + config.ChiaRoot = rootPath + config.fillDatabasePath() + + return config, nil +} + +// GetChiaRootPath returns the root path for the chia installation +func GetChiaRootPath() (string, error) { + if root, ok := os.LookupEnv("CHIA_ROOT"); ok { + return root, nil + } + + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + + root := filepath.Join(home, ".chia", "mainnet") + + return root, nil +} + +// GetFullPath returns the full path to a particular filename within CHIA_ROOT +func (c *ChiaConfig) GetFullPath(filename string) string { + if filepath.IsAbs(filename) { + return filename + } + return filepath.Join(c.ChiaRoot, filename) +} + +func (c *ChiaConfig) fillDatabasePath() { + c.FullNode.DatabasePath = strings.Replace(c.FullNode.DatabasePath, "CHALLENGE", c.FullNode.SelectedNetwork, 1) +} diff --git a/pkg/types/uint128.go b/pkg/types/uint128.go index 2f7fd73..dccc334 100644 --- a/pkg/types/uint128.go +++ b/pkg/types/uint128.go @@ -462,11 +462,41 @@ func (u *Uint128) UnmarshalJSON(data []byte) error { return nil } +// UnmarshalYAML Uint128 from yaml +func (u *Uint128) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + err := unmarshal(&s) + if err != nil { + return err + } + + if s == "null" || s == "" { + s = "0" + } + + var z big.Int + _, ok := z.SetString(s, 10) + if !ok { + return fmt.Errorf("not a valid big integer: %s", s) + } + + ui := Uint128FromBig(&z) + u.Lo = ui.Lo + u.Hi = ui.Hi + + return nil +} + // MarshalJSON marshals the uint128 value to json func (u Uint128) MarshalJSON() ([]byte, error) { return []byte(u.String()), nil } +// MarshalYAML marshals the uint128 value to yaml +func (u Uint128) MarshalYAML() (interface{}, error) { + return u.String(), nil +} + // FitsInUint64 returns true if the value of the Uint128 will fit in Uint64 func (u Uint128) FitsInUint64() bool { return u.Hi == 0 diff --git a/pkg/types/uint128_test.go b/pkg/types/uint128_test.go index b589d0a..ed66e69 100644 --- a/pkg/types/uint128_test.go +++ b/pkg/types/uint128_test.go @@ -33,6 +33,8 @@ import ( "math/big" "testing" + "gopkg.in/yaml.v3" + "github.com/chia-network/go-chia-libs/pkg/types" ) @@ -513,3 +515,31 @@ func TestUint128_UnmarshalJSON_Null(t *testing.T) { t.Error("`null` did not unmarshal as zero") } } + +func TestUint128_UnmarshalYAML(t *testing.T) { + yamldata := []byte(`int128: 18446744073709551616`) + structDef := &struct { + Int128 types.Uint128 `yaml:"int128"` + }{} + err := yaml.Unmarshal(yamldata, structDef) + if err != nil { + t.Error(err) + } + if structDef.Int128.String() != "18446744073709551616" { + t.Error("value did not parse correctly") + } +} + +func TestUint128_UnmarshalYAML_Null(t *testing.T) { + yamldata := []byte(`int128: null`) + structDef := &struct { + Int128 types.Uint128 `yaml:"int128"` + }{} + err := yaml.Unmarshal(yamldata, structDef) + if err != nil { + t.Error(err) + } + if structDef.Int128.String() != "0" { + t.Error("`null` did not unmarshal as zero") + } +}