package main import ( "context" "flag" "fmt" "os" "path/filepath" "time" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/tools/tm-signer-harness/internal" "github.com/tendermint/tendermint/version" ) const ( defaultAcceptRetries = 100 defaultBindAddr = "tcp://127.0.0.1:0" defaultAcceptDeadline = 1 defaultConnDeadline = 3 defaultExtractKeyOutput = "./signing.key" ) var defaultTMHome string var logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false) // Command line flags var ( flagAcceptRetries int flagBindAddr string flagTMHome string flagKeyOutputPath string ) // Command line commands var ( rootCmd *flag.FlagSet runCmd *flag.FlagSet extractKeyCmd *flag.FlagSet versionCmd *flag.FlagSet ) func init() { rootCmd = flag.NewFlagSet("root", flag.ExitOnError) rootCmd.Usage = func() { fmt.Println(`Remote signer test harness for Tendermint. Usage: tm-signer-harness [flags] Available Commands: extract_key Extracts a signing key from a local Tendermint instance help Help on the available commands run Runs the test harness version Display version information and exit Use "tm-signer-harness help " for more information about that command.`) fmt.Println("") } hd, err := os.UserHomeDir() if err != nil { fmt.Println("The UserHomeDir is not defined, setting the default TM Home PATH to \"~/.tendermint\"") defaultTMHome = "~/.tendermint" } else { defaultTMHome = fmt.Sprintf("%s/.tendermint", hd) } runCmd = flag.NewFlagSet("run", flag.ExitOnError) runCmd.IntVar(&flagAcceptRetries, "accept-retries", defaultAcceptRetries, "The number of attempts to listen for incoming connections") runCmd.StringVar(&flagBindAddr, "addr", defaultBindAddr, "Bind to this address for the testing") runCmd.StringVar(&flagTMHome, "tmhome", defaultTMHome, "Path to the Tendermint home directory") runCmd.Usage = func() { fmt.Println(`Runs the remote signer test harness for Tendermint. Usage: tm-signer-harness run [flags] Flags:`) runCmd.PrintDefaults() fmt.Println("") } extractKeyCmd = flag.NewFlagSet("extract_key", flag.ExitOnError) extractKeyCmd.StringVar(&flagKeyOutputPath, "output", defaultExtractKeyOutput, "Path to which signing key should be written") extractKeyCmd.StringVar(&flagTMHome, "tmhome", defaultTMHome, "Path to the Tendermint home directory") extractKeyCmd.Usage = func() { fmt.Println(`Extracts a signing key from a local Tendermint instance for use in the remote signer under test. Usage: tm-signer-harness extract_key [flags] Flags:`) extractKeyCmd.PrintDefaults() fmt.Println("") } versionCmd = flag.NewFlagSet("version", flag.ExitOnError) versionCmd.Usage = func() { fmt.Println(` Prints the Tendermint version for which this remote signer harness was built. Usage: tm-signer-harness version`) fmt.Println("") } } func runTestHarness(ctx context.Context, acceptRetries int, bindAddr, tmhome string) { tmhome = internal.ExpandPath(tmhome) cfg := internal.TestHarnessConfig{ BindAddr: bindAddr, KeyFile: filepath.Join(tmhome, "config", "priv_validator_key.json"), StateFile: filepath.Join(tmhome, "data", "priv_validator_state.json"), GenesisFile: filepath.Join(tmhome, "config", "genesis.json"), AcceptDeadline: time.Duration(defaultAcceptDeadline) * time.Second, AcceptRetries: acceptRetries, ConnDeadline: time.Duration(defaultConnDeadline) * time.Second, SecretConnKey: ed25519.GenPrivKey(), ExitWhenComplete: true, } harness, err := internal.NewTestHarness(ctx, logger, cfg) if err != nil { logger.Error(err.Error()) if therr, ok := err.(*internal.TestHarnessError); ok { os.Exit(therr.Code) } os.Exit(internal.ErrOther) } harness.Run() } func extractKey(tmhome, outputPath string) { keyFile := filepath.Join(internal.ExpandPath(tmhome), "config", "priv_validator_key.json") stateFile := filepath.Join(internal.ExpandPath(tmhome), "data", "priv_validator_state.json") fpv, err := privval.LoadFilePV(keyFile, stateFile) if err != nil { logger.Error("Can't load file pv", "err", err) os.Exit(1) } pkb := []byte(fpv.Key.PrivKey.(ed25519.PrivKey)) if err := os.WriteFile(internal.ExpandPath(outputPath), pkb[:32], 0600); err != nil { logger.Info("Failed to write private key", "output", outputPath, "err", err) os.Exit(1) } logger.Info("Successfully wrote private key", "output", outputPath) } func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if err := rootCmd.Parse(os.Args[1:]); err != nil { fmt.Printf("Error parsing flags: %v\n", err) os.Exit(1) } if rootCmd.NArg() == 0 || (rootCmd.NArg() == 1 && rootCmd.Arg(0) == "help") { rootCmd.Usage() os.Exit(0) } switch rootCmd.Arg(0) { case "help": switch rootCmd.Arg(1) { case "run": runCmd.Usage() case "extract_key": extractKeyCmd.Usage() case "version": versionCmd.Usage() default: fmt.Printf("Unrecognized command: %s\n", rootCmd.Arg(1)) os.Exit(1) } case "run": if err := runCmd.Parse(os.Args[2:]); err != nil { fmt.Printf("Error parsing flags: %v\n", err) os.Exit(1) } runTestHarness(ctx, flagAcceptRetries, flagBindAddr, flagTMHome) case "extract_key": if err := extractKeyCmd.Parse(os.Args[2:]); err != nil { fmt.Printf("Error parsing flags: %v\n", err) os.Exit(1) } extractKey(flagTMHome, flagKeyOutputPath) case "version": fmt.Println(version.TMVersion) default: fmt.Printf("Unrecognized command: %s\n", flag.Arg(0)) os.Exit(1) } }