// Load the most recent state from "state" db, // or create a new one (and save) from genesis. func getState(config cfg.Config, stateDB dbm.DB) *sm.State { state := sm.LoadState(stateDB) if state == nil { state = sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file")) state.Save() } return state }
// Convenience function func SendEmail(config cfg.Config, subject, body string, tos []string) error { email := Compose(subject, body) email.From = config.GetString("smtp_user") email.ContentType = "text/html; charset=utf-8" email.AddRecipients(tos...) err := email.Send(config) return err }
// Send sends the email, returning any error encountered. func (e *Email) Send(config cfg.Config) error { if e.From == "" { return errors.New("Error: No sender specified. Please set the Email.From field.") } if e.To == nil || len(e.To) == 0 { return errors.New("Error: No recipient specified. Please set the Email.To field.") } auth := smtp.PlainAuth( "", config.GetString("smtp_user"), config.GetString("smtp_password"), config.GetString("smtp_host"), ) conn, err := smtp.Dial(fmt.Sprintf("%v:%v", config.GetString("smtp_host"), config.GetString("smtp_port"))) if err != nil { return err } err = conn.StartTLS(&tls.Config{}) if err != nil { return err } err = conn.Auth(auth) if err != nil { return err } err = conn.Mail(e.From) if err != nil { if strings.Contains(err.Error(), "530 5.5.1") { return errors.New("Error: Authentication failure. Your username or password is incorrect.") } return err } for _, recipient := range e.To { err = conn.Rcpt(recipient) if err != nil { return err } } wc, err := conn.Data() if err != nil { return err } defer wc.Close() _, err = wc.Write(e.Bytes()) if err != nil { return err } return nil }
func RunReplayConsole(config cfg.Config) { walFile := config.GetString("cswal") if walFile == "" { Exit("cswal file name not set in tendermint config") } consensusState := newConsensusState(config) if err := consensusState.ReplayConsole(walFile); err != nil { Exit(Fmt("Error during consensus replay: %v", err)) } }
func RunReplay(config cfg.Config) { walFile := config.GetString("cswal") if walFile == "" { Exit("cswal file name not set in tendermint config") } consensusState := newConsensusState(config) if err := consensusState.ReplayMessages(walFile); err != nil { Exit(Fmt("Error during consensus replay: %v", err)) } log.Notice("Replay run successfully") }
func parseFlags(config cfg.Config, args []string) { var ( printHelp bool moniker string nodeLaddr string seeds string fastSync bool skipUPNP bool rpcLaddr string logLevel string proxyApp string ) // Declare flags var flags = flag.NewFlagSet("main", flag.ExitOnError) flags.BoolVar(&printHelp, "help", false, "Print this help message.") flags.StringVar(&moniker, "moniker", config.GetString("moniker"), "Node Name") flags.StringVar(&nodeLaddr, "node_laddr", config.GetString("node_laddr"), "Node listen address. (0.0.0.0:0 means any interface, any port)") flags.StringVar(&seeds, "seeds", config.GetString("seeds"), "Comma delimited host:port seed nodes") flags.BoolVar(&fastSync, "fast_sync", config.GetBool("fast_sync"), "Fast blockchain syncing") flags.BoolVar(&skipUPNP, "skip_upnp", config.GetBool("skip_upnp"), "Skip UPNP configuration") flags.StringVar(&rpcLaddr, "rpc_laddr", config.GetString("rpc_laddr"), "RPC listen address. Port required") flags.StringVar(&logLevel, "log_level", config.GetString("log_level"), "Log level") flags.StringVar(&proxyApp, "proxy_app", config.GetString("proxy_app"), "Proxy app address, or 'nilapp' or 'dummy' for local testing.") flags.Parse(args) if printHelp { flags.PrintDefaults() os.Exit(0) } // Merge parsed flag values onto app. config.Set("moniker", moniker) config.Set("node_laddr", nodeLaddr) config.Set("seeds", seeds) config.Set("fast_sync", fastSync) config.Set("skip_upnp", skipUPNP) config.Set("rpc_laddr", rpcLaddr) config.Set("log_level", logLevel) config.Set("proxy_app", proxyApp) }
func sendTwilio(config cfg.Config, message string) { defer func() { if err := recover(); err != nil { log.Error("sendTwilio error", "error", err) } }() if len(message) > 50 { message = message[:50] } twilio := gotwilio.NewTwilioClient(config.GetString("alert_twilio_sid"), config.GetString("alert_twilio_token")) res, exp, err := twilio.SendSMS(config.GetString("alert_twilio_from"), config.GetString("alert_twilio_to"), message, "", "") if exp != nil || err != nil { log.Error("sendTwilio error", "res", res, "exp", exp, "error", err) } }
func makeNodeInfo(config cfg.Config, sw *p2p.Switch, privKey crypto.PrivKeyEd25519) *p2p.NodeInfo { nodeInfo := &p2p.NodeInfo{ PubKey: privKey.PubKey().(crypto.PubKeyEd25519), Moniker: config.GetString("moniker"), Network: config.GetString("chain_id"), Version: version.Version, Other: []string{ Fmt("wire_version=%v", wire.Version), Fmt("p2p_version=%v", p2p.Version), Fmt("consensus_version=%v", consensus.Version), Fmt("rpc_version=%v/%v", rpc.Version, rpccore.Version), }, } // include git hash in the nodeInfo if available if rev, err := ReadFile(config.GetString("revision_file")); err == nil { nodeInfo.Other = append(nodeInfo.Other, Fmt("revision=%v", string(rev))) } if !sw.IsListening() { return nodeInfo } p2pListener := sw.Listeners()[0] p2pHost := p2pListener.ExternalAddress().IP.String() p2pPort := p2pListener.ExternalAddress().Port rpcListenAddr := config.GetString("rpc_laddr") // We assume that the rpcListener has the same ExternalAddress. // This is probably true because both P2P and RPC listeners use UPnP, // except of course if the rpc is only bound to localhost nodeInfo.ListenAddr = Fmt("%v:%v", p2pHost, p2pPort) nodeInfo.Other = append(nodeInfo.Other, Fmt("rpc_addr=%v", rpcListenAddr)) return nodeInfo }
// Sends a critical alert message to administrators. func Alert(config cfg.Config, message string) { log.Error("<!> ALERT <!>\n" + message) now := time.Now().Unix() if now-lastAlertUnix > int64(config.GetInt("alert_min_interval")) { message = fmt.Sprintf("%v:%v", config.GetString("chain_id"), message) if alertCountSince > 0 { message = fmt.Sprintf("%v (+%v more since)", message, alertCountSince) alertCountSince = 0 } if len(config.GetString("alert_twilio_sid")) > 0 { go sendTwilio(config, message) } if len(config.GetString("alert_email_recipients")) > 0 { go sendEmail(config, message) } } else { alertCountSince++ } }
func NewNode(config cfg.Config, privValidator *types.PrivValidator, getProxyApp func(proxyAddr string, appHash []byte) proxy.AppConn) *Node { EnsureDir(config.GetString("db_dir"), 0700) // incase we use memdb, cswal still gets written here // Get BlockStore blockStoreDB := dbm.NewDB("blockstore", config.GetString("db_backend"), config.GetString("db_dir")) blockStore := bc.NewBlockStore(blockStoreDB) // Get State db stateDB := dbm.NewDB("state", config.GetString("db_backend"), config.GetString("db_dir")) // Get State state := getState(config, stateDB) // Create two proxyAppConn connections, // one for the consensus and one for the mempool. proxyAddr := config.GetString("proxy_app") proxyAppConnMempool := getProxyApp(proxyAddr, state.AppHash) proxyAppConnConsensus := getProxyApp(proxyAddr, state.AppHash) // add the chainid and number of validators to the global config config.Set("chain_id", state.ChainID) config.Set("num_vals", state.Validators.Size()) // Generate node PrivKey privKey := crypto.GenPrivKeyEd25519() // Make event switch eventSwitch := events.NewEventSwitch() _, err := eventSwitch.Start() if err != nil { Exit(Fmt("Failed to start switch: %v", err)) } // Decide whether to fast-sync or not // We don't fast-sync when the only validator is us. fastSync := config.GetBool("fast_sync") if state.Validators.Size() == 1 { addr, _ := state.Validators.GetByIndex(0) if bytes.Equal(privValidator.Address, addr) { fastSync = false } } // Make BlockchainReactor bcReactor := bc.NewBlockchainReactor(state.Copy(), proxyAppConnConsensus, blockStore, fastSync) // Make MempoolReactor mempool := mempl.NewMempool(config, proxyAppConnMempool) mempoolReactor := mempl.NewMempoolReactor(config, mempool) // Make ConsensusReactor consensusState := consensus.NewConsensusState(config, state.Copy(), proxyAppConnConsensus, blockStore, mempool) consensusReactor := consensus.NewConsensusReactor(consensusState, blockStore, fastSync) if privValidator != nil { consensusReactor.SetPrivValidator(privValidator) } // deterministic accountability err = consensusState.OpenWAL(config.GetString("cswal")) if err != nil { log.Error("Failed to open cswal", "error", err.Error()) } // Make p2p network switch sw := p2p.NewSwitch(config) sw.AddReactor("MEMPOOL", mempoolReactor) sw.AddReactor("BLOCKCHAIN", bcReactor) sw.AddReactor("CONSENSUS", consensusReactor) // add the event switch to all services // they should all satisfy events.Eventable SetEventSwitch(eventSwitch, bcReactor, mempoolReactor, consensusReactor) // run the profile server profileHost := config.GetString("prof_laddr") if profileHost != "" { go func() { log.Warn("Profile server", "error", http.ListenAndServe(profileHost, nil)) }() } return &Node{ config: config, sw: sw, evsw: eventSwitch, blockStore: blockStore, bcReactor: bcReactor, mempoolReactor: mempoolReactor, consensusState: consensusState, consensusReactor: consensusReactor, privValidator: privValidator, genesisDoc: state.GenesisDoc, privKey: privKey, } }
// convenience for replay mode func newConsensusState(config cfg.Config) *consensus.ConsensusState { // Get BlockStore blockStoreDB := dbm.NewDB("blockstore", config.GetString("db_backend"), config.GetString("db_dir")) blockStore := bc.NewBlockStore(blockStoreDB) // Get State stateDB := dbm.NewDB("state", config.GetString("db_backend"), config.GetString("db_dir")) state := sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file")) // Create two proxyAppConn connections, // one for the consensus and one for the mempool. proxyAddr := config.GetString("proxy_app") proxyAppConnMempool := GetProxyApp(proxyAddr, state.AppHash) proxyAppConnConsensus := GetProxyApp(proxyAddr, state.AppHash) // add the chainid to the global config config.Set("chain_id", state.ChainID) // Make event switch eventSwitch := events.NewEventSwitch() _, err := eventSwitch.Start() if err != nil { Exit(Fmt("Failed to start event switch: %v", err)) } mempool := mempl.NewMempool(config, proxyAppConnMempool) consensusState := consensus.NewConsensusState(config, state.Copy(), proxyAppConnConsensus, blockStore, mempool) consensusState.SetEventSwitch(eventSwitch) return consensusState }
// Users wishing to use an external signer for their validators // should fork tendermint/tendermint and implement RunNode to // load their custom priv validator and call NewNode(privVal, getProxyFunc) func RunNode(config cfg.Config) { // Wait until the genesis doc becomes available genDocFile := config.GetString("genesis_file") if !FileExists(genDocFile) { log.Notice(Fmt("Waiting for genesis file %v...", genDocFile)) for { time.Sleep(time.Second) if !FileExists(genDocFile) { continue } jsonBlob, err := ioutil.ReadFile(genDocFile) if err != nil { Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) } genDoc := types.GenesisDocFromJSON(jsonBlob) if genDoc.ChainID == "" { PanicSanity(Fmt("Genesis doc %v must include non-empty chain_id", genDocFile)) } config.Set("chain_id", genDoc.ChainID) } } // Get PrivValidator privValidatorFile := config.GetString("priv_validator_file") privValidator := types.LoadOrGenPrivValidator(privValidatorFile) // Create & start node n := NewNode(config, privValidator, GetProxyApp) l := p2p.NewDefaultListener("tcp", config.GetString("node_laddr"), config.GetBool("skip_upnp")) n.AddListener(l) err := n.Start() if err != nil { Exit(Fmt("Failed to start node: %v", err)) } log.Notice("Started node", "nodeInfo", n.sw.NodeInfo()) // If seedNode is provided by config, dial out. if config.GetString("seeds") != "" { seeds := strings.Split(config.GetString("seeds"), ",") n.sw.DialSeeds(seeds) } // Run the RPC server. if config.GetString("rpc_laddr") != "" { _, err := n.StartRPC() if err != nil { PanicCrisis(err) } } // Sleep forever and then... TrapSignal(func() { n.Stop() }) }