// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "version.h" #include #include #include "DaemonCommandsHandler.h" #include "Common/SignalHandler.h" #include "Common/PathTools.h" #include "crypto/hash.h" #include "CryptoNoteCore/Core.h" #include "CryptoNoteCore/CoreConfig.h" #include "CryptoNoteCore/CryptoNoteTools.h" #include "CryptoNoteCore/Currency.h" #include "CryptoNoteCore/MinerConfig.h" #include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" #include "P2p/NetNode.h" #include "P2p/NetNodeConfig.h" #include "Rpc/RpcServer.h" #include "Rpc/RpcServerConfig.h" #include "version.h" #include "Logging/ConsoleLogger.h" #include #if defined(WIN32) #include #endif using Common::JsonValue; using namespace CryptoNote; using namespace Logging; namespace po = boost::program_options; namespace { const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(CryptoNote::CRYPTONOTE_NAME) + ".conf"}; const command_line::arg_descriptor arg_os_version = {"os-version", ""}; const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; const command_line::arg_descriptor arg_log_level = {"log-level", "", 2}; // info level const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; const command_line::arg_descriptor arg_testnet_on = {"testnet", "Used to deploy test nets. Checkpoints and hardcoded seeds are ignored, " "network id is changed. Use it with --data-dir flag. The wallet must be launched with --testnet flag.", false}; const command_line::arg_descriptor arg_print_genesis_tx = { "print-genesis-tx", "Prints genesis' block tx hex to insert it to config and exits" }; } bool command_line_preprocessor(const boost::program_options::variables_map& vm, LoggerRef& logger); void print_genesis_tx_hex() { Logging::ConsoleLogger logger; CryptoNote::Transaction tx = CryptoNote::CurrencyBuilder(logger).generateGenesisTransaction(); CryptoNote::BinaryArray txb = CryptoNote::toBinaryArray(tx); std::string tx_hex = Common::toHex(txb); std::cout << "Insert this line into your coin configuration file as is: " << std::endl; std::cout << "const char GENESIS_COINBASE_TX_HEX[] = \"" << tx_hex << "\";" << std::endl; return; } JsonValue buildLoggerConfiguration(Level level, const std::string& logfile) { JsonValue loggerConfiguration(JsonValue::OBJECT); loggerConfiguration.insert("globalLevel", static_cast(level)); JsonValue& cfgLoggers = loggerConfiguration.insert("loggers", JsonValue::ARRAY); JsonValue& fileLogger = cfgLoggers.pushBack(JsonValue::OBJECT); fileLogger.insert("type", "file"); fileLogger.insert("filename", logfile); fileLogger.insert("level", static_cast(TRACE)); JsonValue& consoleLogger = cfgLoggers.pushBack(JsonValue::OBJECT); consoleLogger.insert("type", "console"); consoleLogger.insert("level", static_cast(TRACE)); consoleLogger.insert("pattern", "%T %L "); return loggerConfiguration; } int main(int argc, char* argv[]) { #ifdef WIN32 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif LoggerManager logManager; LoggerRef logger(logManager, "daemon"); try { po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); command_line::add_arg(desc_cmd_only, command_line::arg_help); command_line::add_arg(desc_cmd_only, command_line::arg_version); command_line::add_arg(desc_cmd_only, arg_os_version); // tools::get_default_data_dir() can't be called during static initialization command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); command_line::add_arg(desc_cmd_only, arg_config_file); command_line::add_arg(desc_cmd_sett, arg_log_file); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_console); command_line::add_arg(desc_cmd_sett, arg_testnet_on); command_line::add_arg(desc_cmd_sett, arg_print_genesis_tx); RpcServerConfig::initOptions(desc_cmd_sett); CoreConfig::initOptions(desc_cmd_sett); NetNodeConfig::initOptions(desc_cmd_sett); MinerConfig::initOptions(desc_cmd_sett); po::options_description desc_options("Allowed options"); desc_options.add(desc_cmd_only).add(desc_cmd_sett); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() { po::store(po::parse_command_line(argc, argv, desc_options), vm); if (command_line::get_arg(vm, command_line::arg_help)) { std::cout << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; std::cout << desc_options << std::endl; return false; } if (command_line::get_arg(vm, arg_print_genesis_tx)) { print_genesis_tx_hex(); return false; } std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); std::string config = command_line::get_arg(vm, arg_config_file); boost::filesystem::path data_dir_path(data_dir); boost::filesystem::path config_path(config); if (!config_path.has_parent_path()) { config_path = data_dir_path / config_path; } boost::system::error_code ec; if (boost::filesystem::exists(config_path, ec)) { po::store(po::parse_config_file(config_path.string().c_str(), desc_cmd_sett), vm); } po::notify(vm); return true; }); if (!r) return 1; auto modulePath = Common::NativePathToGeneric(argv[0]); auto cfgLogFile = Common::NativePathToGeneric(command_line::get_arg(vm, arg_log_file)); if (cfgLogFile.empty()) { cfgLogFile = Common::ReplaceExtenstion(modulePath, ".log"); } else { if (!Common::HasParentPath(cfgLogFile)) { cfgLogFile = Common::CombinePath(Common::GetPathDirectory(modulePath), cfgLogFile); } } Level cfgLogLevel = static_cast(static_cast(Logging::ERROR) + command_line::get_arg(vm, arg_log_level)); // configure logging logManager.configure(buildLoggerConfiguration(cfgLogLevel, cfgLogFile)); logger(INFO) << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG; if (command_line_preprocessor(vm, logger)) { return 0; } logger(INFO) << "Module folder: " << argv[0]; bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); if (testnet_mode) { logger(INFO) << "Starting in testnet mode!"; } //create objects and link them CryptoNote::CurrencyBuilder currencyBuilder(logManager); currencyBuilder.testnet(testnet_mode); try { currencyBuilder.currency(); } catch (std::exception&) { std::cout << "GENESIS_COINBASE_TX_HEX constant has an incorrect value. Please launch: " << CryptoNote::CRYPTONOTE_NAME << "d --" << arg_print_genesis_tx.name; return 1; } CryptoNote::Currency currency = currencyBuilder.currency(); CryptoNote::core ccore(currency, nullptr, logManager); CryptoNote::Checkpoints checkpoints(logManager); for (const auto& cp : CryptoNote::CHECKPOINTS) { checkpoints.add_checkpoint(cp.height, cp.blockId); } if (!testnet_mode) { ccore.set_checkpoints(std::move(checkpoints)); } CoreConfig coreConfig; coreConfig.init(vm); NetNodeConfig netNodeConfig; netNodeConfig.init(vm); netNodeConfig.setTestnet(testnet_mode); MinerConfig minerConfig; minerConfig.init(vm); RpcServerConfig rpcConfig; rpcConfig.init(vm); if (!coreConfig.configFolderDefaulted) { if (!Tools::directoryExists(coreConfig.configFolder)) { throw std::runtime_error("Directory does not exist: " + coreConfig.configFolder); } } else { if (!Tools::create_directories_if_necessary(coreConfig.configFolder)) { throw std::runtime_error("Can't create directory: " + coreConfig.configFolder); } } System::Dispatcher dispatcher; CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, logManager); CryptoNote::NodeServer p2psrv(dispatcher, cprotocol, logManager); CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv, cprotocol); cprotocol.set_p2p_endpoint(&p2psrv); ccore.set_cryptonote_protocol(&cprotocol); DaemonCommandsHandler dch(ccore, p2psrv, logManager); // initialize objects logger(INFO) << "Initializing p2p server..."; if (!p2psrv.init(netNodeConfig)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize p2p server."; return 1; } logger(INFO) << "P2p server initialized OK"; //logger(INFO) << "Initializing core rpc server..."; //if (!rpc_server.init(vm)) { // logger(ERROR, BRIGHT_RED) << "Failed to initialize core rpc server."; // return 1; //} // logger(INFO, BRIGHT_GREEN) << "Core rpc server initialized OK on port: " << rpc_server.get_binded_port(); // initialize core here logger(INFO) << "Initializing core..."; if (!ccore.init(coreConfig, minerConfig, true)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize core"; return 1; } logger(INFO) << "Core initialized OK"; // start components if (!command_line::has_arg(vm, arg_console)) { dch.start_handling(); } logger(INFO) << "Starting core rpc server on address " << rpcConfig.getBindAddress(); rpcServer.start(rpcConfig.bindIp, rpcConfig.bindPort); logger(INFO) << "Core rpc server started ok"; Tools::SignalHandler::install([&dch, &p2psrv] { dch.stop_handling(); p2psrv.sendStopSignal(); }); logger(INFO) << "Starting p2p net loop..."; p2psrv.run(); logger(INFO) << "p2p net loop stopped"; dch.stop_handling(); //stop components logger(INFO) << "Stopping core rpc server..."; rpcServer.stop(); //deinitialize components logger(INFO) << "Deinitializing core..."; ccore.deinit(); logger(INFO) << "Deinitializing p2p..."; p2psrv.deinit(); ccore.set_cryptonote_protocol(NULL); cprotocol.set_p2p_endpoint(NULL); } catch (const std::exception& e) { logger(ERROR, BRIGHT_RED) << "Exception: " << e.what(); return 1; } logger(INFO) << "Node stopped."; return 0; } bool command_line_preprocessor(const boost::program_options::variables_map &vm, LoggerRef &logger) { bool exit = false; if (command_line::get_arg(vm, command_line::arg_version)) { std::cout << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; exit = true; } if (command_line::get_arg(vm, arg_os_version)) { std::cout << "OS: " << Tools::get_os_version_string() << ENDL; exit = true; } if (exit) { return true; } return false; }