|
|
@ -1,182 +1,114 @@ |
|
|
|
package config |
|
|
|
|
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"flag" |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"net" |
|
|
|
"os" |
|
|
|
"path/filepath" |
|
|
|
"strings" |
|
|
|
|
|
|
|
. "github.com/tendermint/tendermint/common" |
|
|
|
"github.com/tendermint/confer" |
|
|
|
flag "github.com/spf13/pflag" |
|
|
|
) |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------j
|
|
|
|
// Configuration types
|
|
|
|
|
|
|
|
type ConfigType struct { |
|
|
|
Network string |
|
|
|
LAddr string |
|
|
|
SeedNode string |
|
|
|
DB DBConfig |
|
|
|
Alert AlertConfig |
|
|
|
SMTP SMTPConfig |
|
|
|
RPC RPCConfig |
|
|
|
} |
|
|
|
var rootDir string |
|
|
|
var App *confer.Config |
|
|
|
var defaultConfig = ` |
|
|
|
# This is a TOML config file. |
|
|
|
# For more information, see https://github.com/toml-lang/toml
|
|
|
|
|
|
|
|
type DBConfig struct { |
|
|
|
Backend string |
|
|
|
Dir string |
|
|
|
} |
|
|
|
Network = "tendermint_testnet0" |
|
|
|
ListenAddr = "0.0.0.0:0" |
|
|
|
# First node to connect to. Command-line overridable. |
|
|
|
# SeedNode = "a.b.c.d:pppp" |
|
|
|
|
|
|
|
type AlertConfig struct { |
|
|
|
MinInterval int |
|
|
|
[DB] |
|
|
|
# The only other available backend is "memdb" |
|
|
|
Backend = "leveldb" |
|
|
|
# The leveldb data directory. |
|
|
|
# Dir = "<YOUR_HOME_DIRECTORY>/.tendermint/data" |
|
|
|
|
|
|
|
TwilioSid string |
|
|
|
TwilioToken string |
|
|
|
TwilioFrom string |
|
|
|
TwilioTo string |
|
|
|
[RPC] |
|
|
|
# For the RPC API HTTP server. Port required. |
|
|
|
HTTP.ListenAddr = "0.0.0.0:8080" |
|
|
|
|
|
|
|
EmailRecipients []string |
|
|
|
} |
|
|
|
[Alert] |
|
|
|
# TODO: Document options |
|
|
|
|
|
|
|
type SMTPConfig struct { |
|
|
|
User string |
|
|
|
Password string |
|
|
|
Host string |
|
|
|
Port uint |
|
|
|
} |
|
|
|
|
|
|
|
type RPCConfig struct { |
|
|
|
HTTPLAddr string |
|
|
|
} |
|
|
|
|
|
|
|
func (cfg *ConfigType) validate() error { |
|
|
|
if cfg.Network == "" { |
|
|
|
cfg.Network = defaultConfig.Network |
|
|
|
} |
|
|
|
if cfg.LAddr == "" { |
|
|
|
cfg.LAddr = defaultConfig.LAddr |
|
|
|
} |
|
|
|
if cfg.SeedNode == "" { |
|
|
|
cfg.SeedNode = defaultConfig.SeedNode |
|
|
|
} |
|
|
|
if cfg.DB.Backend == "" { |
|
|
|
return errors.New("DB.Backend must be set") |
|
|
|
} |
|
|
|
if cfg.RPC.HTTPLAddr == "" { |
|
|
|
fmt.Println("Set RPC.HTTPLAddr to \"0.0.0.0:8888\" in your config.json to enable the RPC API server.") |
|
|
|
} else { |
|
|
|
_, port, err := net.SplitHostPort(cfg.RPC.HTTPLAddr) |
|
|
|
if err != nil { |
|
|
|
return errors.New(Fmt("RPC.HTTPLAddr is invalid. %v", err)) |
|
|
|
} |
|
|
|
if port == "" || port == "0" { |
|
|
|
return errors.New("RPC.HTTPLAddr is invalid. Port number must be defined") |
|
|
|
} |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (cfg *ConfigType) bytes() []byte { |
|
|
|
configBytes, err := json.MarshalIndent(cfg, "", "\t") |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
return configBytes |
|
|
|
} |
|
|
|
|
|
|
|
func (cfg *ConfigType) write(configFile string) { |
|
|
|
if strings.Index(configFile, "/") != -1 { |
|
|
|
err := os.MkdirAll(filepath.Dir(configFile), 0700) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
} |
|
|
|
err := ioutil.WriteFile(configFile, cfg.bytes(), 0600) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
var rootDir string |
|
|
|
var defaultConfig ConfigType |
|
|
|
[SMTP] |
|
|
|
# TODO: Document options |
|
|
|
` |
|
|
|
|
|
|
|
func init() { |
|
|
|
|
|
|
|
// Get RootDir
|
|
|
|
rootDir = os.Getenv("TMROOT") |
|
|
|
if rootDir == "" { |
|
|
|
rootDir = os.Getenv("HOME") + "/.tendermint" |
|
|
|
} |
|
|
|
configFile := rootDir + "/config.toml" |
|
|
|
|
|
|
|
// Write default config file if missing.
|
|
|
|
if _, err := os.Stat(configFile); os.IsNotExist(err) { |
|
|
|
if strings.Index(configFile, "/") != -1 { |
|
|
|
err := os.MkdirAll(filepath.Dir(configFile), 0700) |
|
|
|
if err != nil { |
|
|
|
fmt.Printf("Could not create directory: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
} |
|
|
|
err := ioutil.WriteFile(configFile, []byte(defaultConfig), 0600) |
|
|
|
if err != nil { |
|
|
|
fmt.Printf("Could not write config file: %v", err) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
fmt.Printf("Config file written to %v. Please edit & run again\n", configFile) |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
|
|
|
|
// Compute defaultConfig
|
|
|
|
defaultConfig = ConfigType{ |
|
|
|
Network: "tendermint_testnet0", |
|
|
|
LAddr: "0.0.0.0:0", |
|
|
|
SeedNode: "", |
|
|
|
DB: DBConfig{ |
|
|
|
Backend: "leveldb", |
|
|
|
Dir: DataDir(), |
|
|
|
}, |
|
|
|
Alert: AlertConfig{}, |
|
|
|
SMTP: SMTPConfig{}, |
|
|
|
RPC: RPCConfig{ |
|
|
|
HTTPLAddr: "0.0.0.0:0", |
|
|
|
}, |
|
|
|
// Initialize Config
|
|
|
|
App = confer.NewConfig() |
|
|
|
initDefaults() |
|
|
|
paths := []string{configFile} |
|
|
|
if err := App.ReadPaths(paths...); err != nil { |
|
|
|
log.Warn("Error reading configuration", "paths", paths, "error", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func ConfigFile() string { return rootDir + "/config.json" } |
|
|
|
func GenesisFile() string { return rootDir + "/genesis.json" } |
|
|
|
func AddrBookFile() string { return rootDir + "/addrbook.json" } |
|
|
|
func PrivValidatorFile() string { return rootDir + "/priv_validator.json" } |
|
|
|
func DataDir() string { return rootDir + "/data" } |
|
|
|
|
|
|
|
// The actual global config singleton object.
|
|
|
|
var Config ConfigType |
|
|
|
|
|
|
|
func parseFlags(flags *flag.FlagSet, args []string) (printHelp bool) { |
|
|
|
flags.BoolVar(&printHelp, "help", false, "Print this help message.") |
|
|
|
flags.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)") |
|
|
|
flags.StringVar(&Config.SeedNode, "seed", Config.SeedNode, "Address of seed node") |
|
|
|
flags.StringVar(&Config.RPC.HTTPLAddr, "rpc_http_laddr", Config.RPC.HTTPLAddr, "RPC listen address. (0.0.0.0:0 means any interface, any port)") |
|
|
|
flags.Parse(args) |
|
|
|
return |
|
|
|
func initDefaults() { |
|
|
|
App.SetDefault("Network", "tendermint_testnet0") |
|
|
|
App.SetDefault("ListenAddr", "0.0.0.0:0") |
|
|
|
App.SetDefault("DB.Backend", "leveldb") |
|
|
|
App.SetDefault("DB.Dir", rootDir+"/data") |
|
|
|
App.SetDefault("Log.Level", "debug") |
|
|
|
App.SetDefault("Log.Dir", rootDir+"/log") |
|
|
|
App.SetDefault("RPC.HTTP.ListenAddr", "0.0.0.0:8080") |
|
|
|
|
|
|
|
App.SetDefault("GenesisFile", rootDir+"/genesis.json") |
|
|
|
App.SetDefault("AddrbookFile", rootDir+"/addrbook.json") |
|
|
|
App.SetDefault("PrivValidatorfile", rootDir+"/priv_valdiator.json") |
|
|
|
} |
|
|
|
|
|
|
|
func ParseFlags(args []string) { |
|
|
|
configFile := ConfigFile() |
|
|
|
|
|
|
|
// try to read configuration from file. if missing, write default
|
|
|
|
configBytes, err := ioutil.ReadFile(configFile) |
|
|
|
if err != nil { |
|
|
|
defaultConfig.write(configFile) |
|
|
|
fmt.Println("Config file written to config.json. Please edit & run again") |
|
|
|
os.Exit(1) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// try to parse configuration. on error, die
|
|
|
|
Config = ConfigType{} |
|
|
|
err = json.Unmarshal(configBytes, &Config) |
|
|
|
if err != nil { |
|
|
|
Exit(Fmt("Invalid configuration file %s:\n%v\n", configFile, err)) |
|
|
|
} |
|
|
|
err = Config.validate() |
|
|
|
if err != nil { |
|
|
|
Exit(Fmt("Invalid configuration file %s:\n%v\n", configFile, err)) |
|
|
|
} |
|
|
|
var flags = flag.NewFlagSet("main", flag.ExitOnError) |
|
|
|
var printHelp = false |
|
|
|
|
|
|
|
// try to parse arg flags, which can override file configuration.
|
|
|
|
flags := flag.NewFlagSet("main", flag.ExitOnError) |
|
|
|
printHelp := parseFlags(flags, args) |
|
|
|
// Declare flags
|
|
|
|
flags.BoolVar(&printHelp, "help", false, "Print this help message.") |
|
|
|
flags.String("listen_addr", App.GetString("ListenAddr"), "Listen address. (0.0.0.0:0 means any interface, any port)") |
|
|
|
flags.String("seed_node", App.GetString("SeedNode"), "Address of seed node") |
|
|
|
flags.String("rpc_http_listen_addr", App.GetString("RPC.HTTP.ListenAddr"), "RPC listen address. Port required") |
|
|
|
flags.Parse(args) |
|
|
|
if printHelp { |
|
|
|
flags.PrintDefaults() |
|
|
|
os.Exit(0) |
|
|
|
} |
|
|
|
|
|
|
|
// Merge parsed flag values onto App.
|
|
|
|
App.BindPFlag("ListenAddr", flags.Lookup("listen_addr")) |
|
|
|
App.BindPFlag("SeedNode", flags.Lookup("seed_node")) |
|
|
|
App.BindPFlag("RPC.HTTP.ListenAddr", flags.Lookup("rpc_http_listen_addr")) |
|
|
|
|
|
|
|
// Confused?
|
|
|
|
//App.Debug()
|
|
|
|
} |