diff --git a/cmd/config/addtrustedpeer.go b/cmd/config/addtrustedpeer.go new file mode 100644 index 0000000..eee780d --- /dev/null +++ b/cmd/config/addtrustedpeer.go @@ -0,0 +1,124 @@ +package config + +import ( + "encoding/hex" + "net" + "path" + "strconv" + + "github.com/chia-network/go-chia-libs/pkg/config" + "github.com/chia-network/go-chia-libs/pkg/peerprotocol" + "github.com/chia-network/go-modules/pkg/slogs" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/chia-network/chia-tools/internal/utils" +) + +var ( + skipConfirm bool +) + +// addTrustedPeerCmd Adds a trusted peer to the config +var addTrustedPeerCmd = &cobra.Command{ + Use: "add-trusted-peer", + Short: "Adds a trusted peer to the config file", + Example: `chia-tools config add-trusted-peer 1.2.3.4 + +# The following version will also override the port to use when connecting to this peer +chia-tools config add-trusted-peer 1.2.3.4 18444`, + Run: func(cmd *cobra.Command, args []string) { + chiaRoot, err := config.GetChiaRootPath() + if err != nil { + slogs.Logr.Fatal("Unable to determine CHIA_ROOT", "error", err) + } + + // 1: Peer IP + // 2: Optional, port + if len(args) < 1 || len(args) > 2 { + slogs.Logr.Fatal("Unexpected number of arguments provided") + } + + cfgPath := viper.GetString("config") + if cfgPath == "" { + // Use default chia root + cfgPath = path.Join(chiaRoot, "config", "config.yaml") + } + + cfg, err := config.LoadConfigAtRoot(cfgPath, chiaRoot) + if err != nil { + slogs.Logr.Fatal("error loading chia config", "error", err) + } + + peer := args[0] + port := cfg.FullNode.Port + if len(args) > 1 { + port64, err := strconv.ParseUint(args[1], 10, 16) + if err != nil { + slogs.Logr.Fatal("Invalid port provided") + } + port = uint16(port64) + } + + ip := net.ParseIP(peer) + if ip == nil { + slogs.Logr.Fatal("Invalid IP address", "id", peer) + } + slogs.Logr.Info("Attempting to get peer id", "peer", peer, "port", port) + + keypair, err := cfg.FullNode.SSL.LoadPublicKeyPair(chiaRoot) + if err != nil { + slogs.Logr.Fatal("Error loading certs from CHIA_ROOT", "CHIA_ROOT", chiaRoot, "error", err) + } + if keypair == nil { + slogs.Logr.Fatal("Error loading certs from CHIA_ROOT", "CHIA_ROOT", chiaRoot, "error", "keypair was nil") + } + conn, err := peerprotocol.NewConnection( + &ip, + peerprotocol.WithPeerPort(port), + peerprotocol.WithNetworkID(*cfg.SelectedNetwork), + peerprotocol.WithPeerKeyPair(*keypair), + ) + if err != nil { + slogs.Logr.Fatal("Error creating connection", "error", err) + } + peerID, err := conn.PeerID() + if err != nil { + slogs.Logr.Fatal("Error getting peer id", "error", err) + } + peerIDStr := hex.EncodeToString(peerID[:]) + slogs.Logr.Info("peer id received", "peer", peerIDStr) + if !utils.ConfirmAction("Would you like trust this peer? (y/N)", skipConfirm) { + slogs.Logr.Error("Cancelled") + } + cfg.Wallet.TrustedPeers[peerIDStr] = "Does_not_matter" + + peerToAdd := config.Peer{ + Host: ip.String(), + Port: port, + } + + foundPeer := false + for idx, peer := range cfg.Wallet.FullNodePeers { + if peer.Host == ip.String() { + foundPeer = true + cfg.Wallet.FullNodePeers[idx] = peerToAdd + } + } + if !foundPeer { + cfg.Wallet.FullNodePeers = append(cfg.Wallet.FullNodePeers, peerToAdd) + } + + err = cfg.Save() + if err != nil { + slogs.Logr.Fatal("error saving config", "error", err) + } + + slogs.Logr.Info("Added trusted peer. Restart your chia services for the configuration to take effect") + }, +} + +func init() { + addTrustedPeerCmd.Flags().BoolVarP(&skipConfirm, "yes", "y", false, "Skip confirmation") + configCmd.AddCommand(addTrustedPeerCmd) +} diff --git a/cmd/config/config.go b/cmd/config/config.go index 3927df5..b7b1b9e 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -2,6 +2,7 @@ package config import ( "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/chia-network/chia-tools/cmd" ) @@ -13,5 +14,8 @@ var configCmd = &cobra.Command{ } func init() { + configCmd.PersistentFlags().String("config", "", "existing config file to use (default is to look in $CHIA_ROOT)") + cobra.CheckErr(viper.BindPFlag("config", configCmd.PersistentFlags().Lookup("config"))) + cmd.RootCmd.AddCommand(configCmd) } diff --git a/cmd/config/edit.go b/cmd/config/edit.go index 8cd1cf5..404bfe3 100644 --- a/cmd/config/edit.go +++ b/cmd/config/edit.go @@ -13,24 +13,22 @@ import ( var editCmd = &cobra.Command{ Use: "edit", Short: "Edit an existing chia configuration file", - Example: `chia-tools config edit ~/.chia/mainnet/config/config.yaml --set full_node.port=58444 --set full_node.target_peer_count=10 + Example: `chia-tools config edit --config ~/.chia/mainnet/config/config.yaml --set full_node.port=58444 --set full_node.target_peer_count=10 # The following version will discover the config file by inspecting CHIA_ROOT or using the default CHIA_ROOT chia-tools config edit --set full_node.port=58444 --set full_node.target_peer_count=10`, Run: func(cmd *cobra.Command, args []string) { - var cfgPath string - chiaRoot, err := config.GetChiaRootPath() if err != nil { slogs.Logr.Fatal("Unable to determine CHIA_ROOT", "error", err) } - if len(args) > 1 { + if len(args) > 0 { slogs.Logr.Fatal("Unexpected number of arguments provided") - } else if len(args) == 1 { - // Use the provided config path - cfgPath = args[0] - } else { + } + + cfgPath := viper.GetString("config") + if cfgPath == "" { // Use default chia root cfgPath = path.Join(chiaRoot, "config", "config.yaml") } diff --git a/cmd/network/switch.go b/cmd/network/switch.go index 8f3d35c..1cc7557 100644 --- a/cmd/network/switch.go +++ b/cmd/network/switch.go @@ -43,10 +43,11 @@ func init() { // retainedSettings are the settings we want to keep track of when switching networks so we can swap back to them in the future type retainedSettings struct { - DNSServers []string `json:"dns_servers"` - BootstrapPeers []string `json:"bootstrap_peers"` - StaticPeers []string `json:"static_peers"` - FullNodePeers []config.Peer `json:"full_node_peers"` + DNSServers []string `json:"dns_servers"` + BootstrapPeers []string `json:"bootstrap_peers"` + StaticPeers []string `json:"static_peers"` + FullNodePeers []config.Peer `json:"full_node_peers"` + WalletFullNodePeers []config.Peer `json:"wallet_full_node_peers"` } // SwitchNetwork implements the logic to swap networks @@ -101,10 +102,11 @@ func SwitchNetwork(networkName string, checkForRunningNode bool) { } previousSettings := retainedSettings{ - DNSServers: cfg.FullNode.DNSServers, - BootstrapPeers: cfg.Seeder.BootstrapPeers, - StaticPeers: cfg.Seeder.StaticPeers, - FullNodePeers: cfg.FullNode.FullNodePeers, + DNSServers: cfg.FullNode.DNSServers, + BootstrapPeers: cfg.Seeder.BootstrapPeers, + StaticPeers: cfg.Seeder.StaticPeers, + FullNodePeers: cfg.FullNode.FullNodePeers, + WalletFullNodePeers: cfg.Wallet.FullNodePeers, } marshalled, err := json.Marshal(previousSettings) if err != nil { @@ -174,6 +176,7 @@ func SwitchNetwork(networkName string, checkForRunningNode bool) { dnsIntroducerHosts := []string{"dns-introducer.chia.net"} fullNodePort := uint16(8444) var fullnodePeers []config.Peer + var walletFullNodePeers []config.Peer peersFilePath := "db/peers.dat" walletPeersFilePath := "wallet/db/wallet_peers.dat" bootstrapPeers := []string{"node.chia.net"} @@ -202,6 +205,9 @@ func SwitchNetwork(networkName string, checkForRunningNode bool) { if len(settingsToRestore.FullNodePeers) > 0 { fullnodePeers = settingsToRestore.FullNodePeers } + if len(settingsToRestore.WalletFullNodePeers) > 0 { + walletFullNodePeers = settingsToRestore.WalletFullNodePeers + } } if introFlag := viper.GetString("switch-introducer"); introFlag != "" { @@ -247,13 +253,8 @@ func SwitchNetwork(networkName string, checkForRunningNode bool) { Port: fullNodePort, }, }, - "wallet.dns_servers": dnsIntroducerHosts, - "wallet.full_node_peers": []config.Peer{ - { - Host: "localhost", - Port: fullNodePort, - }, - }, + "wallet.dns_servers": dnsIntroducerHosts, + "wallet.full_node_peers": ensureAtLeastLocalPeer(walletFullNodePeers, fullNodePort), "wallet.introducer_peer.host": introducerHost, "wallet.introducer_peer.port": fullNodePort, "wallet.wallet_peers_file_path": walletPeersFilePath, @@ -286,6 +287,17 @@ func SwitchNetwork(networkName string, checkForRunningNode bool) { slogs.Logr.Info("Complete") } +func ensureAtLeastLocalPeer(peers []config.Peer, port uint16) []config.Peer { + if len(peers) == 0 { + peers = append(peers, config.Peer{ + Host: "localhost", + Port: port, + }) + } + + return peers +} + func isConnectionRefused(err error) bool { var netErr *net.OpError if errors.As(err, &netErr) { diff --git a/cmd/root.go b/cmd/root.go index b1ae054..d455cf8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,7 +11,6 @@ import ( "github.com/chia-network/go-modules/pkg/slogs" ) -var cfgFile string var ( gitVersion string buildTime string @@ -37,26 +36,20 @@ func init() { cobra.OnInitialize(initConfig) cobra.OnInitialize(InitLogs) - RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.chia-tools.yaml)") RootCmd.PersistentFlags().String("log-level", "info", "The log-level for the application, can be one of info, warn, error, debug.") cobra.CheckErr(viper.BindPFlag("log-level", RootCmd.PersistentFlags().Lookup("log-level"))) } // initConfig reads in config file and ENV variables if set. func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - } else { - // Find home directory. - home, err := os.UserHomeDir() - cobra.CheckErr(err) + // Find home directory. + home, err := os.UserHomeDir() + cobra.CheckErr(err) - // Search config in home directory with name ".chia-tools" (without extension). - viper.AddConfigPath(home) - viper.SetConfigType("yaml") - viper.SetConfigName(".chia-tools") - } + // Search config in home directory with name ".chia-tools" (without extension). + viper.AddConfigPath(home) + viper.SetConfigType("yaml") + viper.SetConfigName(".chia-tools") viper.SetEnvPrefix("CHIA_TOOLS") viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) diff --git a/internal/utils/confirm.go b/internal/utils/confirm.go new file mode 100644 index 0000000..27a2354 --- /dev/null +++ b/internal/utils/confirm.go @@ -0,0 +1,21 @@ +package utils + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +// ConfirmAction waits for the user to confirm with "yes" or "y" +func ConfirmAction(prompt string, skipConfirm bool) bool { + // Easy support for -y type flags to skip confirmation + if skipConfirm { + return true + } + fmt.Printf("%s ", prompt) + reader := bufio.NewReader(os.Stdin) + response, _ := reader.ReadString('\n') + response = strings.TrimSpace(strings.ToLower(response)) + return response == "yes" || response == "y" +}