350 lines
11 KiB
C++
350 lines
11 KiB
C++
#include <nano/boost/process/child.hpp>
|
|
#include <nano/crypto_lib/random_pool.hpp>
|
|
#include <nano/lib/cli.hpp>
|
|
#include <nano/lib/errors.hpp>
|
|
#include <nano/lib/files.hpp>
|
|
#include <nano/lib/logging.hpp>
|
|
#include <nano/lib/rpcconfig.hpp>
|
|
#include <nano/lib/thread_runner.hpp>
|
|
#include <nano/lib/tomlconfig.hpp>
|
|
#include <nano/lib/utility.hpp>
|
|
#include <nano/lib/walletconfig.hpp>
|
|
#include <nano/nano_wallet/icon.hpp>
|
|
#include <nano/node/cli.hpp>
|
|
#include <nano/node/daemonconfig.hpp>
|
|
#include <nano/node/ipc/ipc_server.hpp>
|
|
#include <nano/node/json_handler.hpp>
|
|
#include <nano/node/node_rpc_config.hpp>
|
|
#include <nano/qt/qt.hpp>
|
|
#include <nano/rpc/rpc.hpp>
|
|
#include <nano/secure/working.hpp>
|
|
|
|
#include <boost/format.hpp>
|
|
#include <boost/make_shared.hpp>
|
|
#include <boost/program_options.hpp>
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
namespace nano
|
|
{
|
|
class wallet_daemon final
|
|
{
|
|
nano::logger logger{ "wallet_daemon" };
|
|
|
|
public:
|
|
void show_error (std::string const & message_a)
|
|
{
|
|
logger.critical (nano::log::type::daemon, "{}", message_a);
|
|
|
|
QMessageBox message (QMessageBox::Critical, "Error starting Nano", message_a.c_str ());
|
|
message.setModal (true);
|
|
message.show ();
|
|
message.exec ();
|
|
}
|
|
|
|
void show_help (std::string const & message_a)
|
|
{
|
|
QMessageBox message (QMessageBox::NoIcon, "Help", "see <a href=\"https://docs.nano.org/commands/command-line-interface/#launch-options\">launch options</a> ");
|
|
message.setStyleSheet ("QLabel {min-width: 450px}");
|
|
message.setDetailedText (message_a.c_str ());
|
|
message.show ();
|
|
message.exec ();
|
|
}
|
|
|
|
nano::error write_wallet_config (nano::wallet_config & config_a, std::filesystem::path const & data_path_a)
|
|
{
|
|
nano::tomlconfig wallet_config_toml;
|
|
auto wallet_path (nano::get_qtwallet_toml_config_path (data_path_a));
|
|
config_a.serialize_toml (wallet_config_toml);
|
|
|
|
// Write wallet config. If missing, the file is created and permissions are set.
|
|
wallet_config_toml.write (wallet_path);
|
|
return wallet_config_toml.get_error ();
|
|
}
|
|
|
|
nano::error read_wallet_config (nano::wallet_config & config_a, std::filesystem::path const & data_path_a)
|
|
{
|
|
nano::tomlconfig wallet_config_toml;
|
|
auto wallet_path (nano::get_qtwallet_toml_config_path (data_path_a));
|
|
if (!std::filesystem::exists (wallet_path))
|
|
{
|
|
write_wallet_config (config_a, data_path_a);
|
|
}
|
|
wallet_config_toml.read (wallet_path);
|
|
config_a.deserialize_toml (wallet_config_toml);
|
|
return wallet_config_toml.get_error ();
|
|
}
|
|
|
|
int run_wallet (QApplication & application, int argc, char * const * argv, std::filesystem::path const & data_path, nano::node_flags const & flags)
|
|
{
|
|
nano::logger::initialize (nano::log_config::daemon_default (), data_path, flags.config_overrides);
|
|
|
|
logger.info (nano::log::type::daemon_wallet, "Daemon started (wallet)");
|
|
|
|
int result (0);
|
|
nano_qt::eventloop_processor processor;
|
|
boost::system::error_code error_chmod;
|
|
std::filesystem::create_directories (data_path);
|
|
nano::set_secure_perm_directory (data_path, error_chmod);
|
|
QPixmap pixmap (":/logo.png");
|
|
auto * splash = new QSplashScreen (pixmap);
|
|
splash->show ();
|
|
QApplication::processEvents ();
|
|
splash->showMessage (QSplashScreen::tr ("Remember - Back Up Your Wallet Seed"), Qt::AlignBottom | Qt::AlignHCenter, Qt::darkGray);
|
|
QApplication::processEvents ();
|
|
|
|
nano::network_params network_params{ nano::network_constants::active_network };
|
|
nano::daemon_config config{ data_path, network_params };
|
|
nano::wallet_config wallet_config;
|
|
|
|
auto error = nano::read_node_config_toml (data_path, config, flags.config_overrides);
|
|
if (!error)
|
|
{
|
|
error = read_wallet_config (wallet_config, data_path);
|
|
}
|
|
|
|
if (!error)
|
|
{
|
|
error = nano::flags_config_conflicts (flags, config.node);
|
|
}
|
|
|
|
if (!error)
|
|
{
|
|
nano::set_use_memory_pools (config.node.use_memory_pools);
|
|
|
|
std::shared_ptr<boost::asio::io_context> io_ctx = std::make_shared<boost::asio::io_context> ();
|
|
|
|
nano::thread_runner runner (io_ctx, logger, config.node.io_threads, nano::thread_role::name::io_daemon);
|
|
|
|
std::shared_ptr<nano::node> node;
|
|
std::shared_ptr<nano_qt::wallet> gui;
|
|
nano::set_application_icon (application);
|
|
auto opencl = nano::opencl_work::create (config.opencl_enable, config.opencl, logger, config.node.network_params.work);
|
|
nano::opencl_work_func_t opencl_work_func;
|
|
if (opencl)
|
|
{
|
|
opencl_work_func = [&opencl] (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> &) {
|
|
return opencl->generate_work (version_a, root_a, difficulty_a);
|
|
};
|
|
}
|
|
nano::work_pool work{ config.node.network_params.network, config.node.work_threads, config.node.pow_sleep_interval, opencl_work_func };
|
|
node = std::make_shared<nano::node> (io_ctx, data_path, config.node, work, flags);
|
|
if (!node->init_error ())
|
|
{
|
|
auto wallet (node->wallets.open (wallet_config.wallet));
|
|
if (wallet == nullptr)
|
|
{
|
|
auto existing (node->wallets.items.begin ());
|
|
if (existing != node->wallets.items.end ())
|
|
{
|
|
wallet = existing->second;
|
|
wallet_config.wallet = existing->first;
|
|
}
|
|
else
|
|
{
|
|
wallet = node->wallets.create (wallet_config.wallet);
|
|
}
|
|
}
|
|
if (wallet_config.account.is_zero () || !wallet->exists (wallet_config.account))
|
|
{
|
|
auto transaction (wallet->wallets.tx_begin_write ());
|
|
auto existing (wallet->store.begin (transaction));
|
|
if (existing != wallet->store.end (transaction))
|
|
{
|
|
wallet_config.account = existing->first;
|
|
}
|
|
else
|
|
{
|
|
wallet_config.account = wallet->deterministic_insert (transaction);
|
|
}
|
|
}
|
|
|
|
debug_assert (wallet->exists (wallet_config.account));
|
|
write_wallet_config (wallet_config, data_path);
|
|
node->start ();
|
|
nano::ipc::ipc_server ipc (*node, config.rpc);
|
|
|
|
std::unique_ptr<boost::process::child> rpc_process;
|
|
std::shared_ptr<nano::rpc> rpc;
|
|
std::unique_ptr<nano::rpc_handler_interface> rpc_handler;
|
|
if (config.rpc_enable)
|
|
{
|
|
if (!config.rpc.child_process.enable)
|
|
{
|
|
// Launch rpc in-process
|
|
nano::rpc_config rpc_config{ config.node.network_params.network };
|
|
error = nano::read_rpc_config_toml (data_path, rpc_config, flags.rpc_config_overrides);
|
|
if (error)
|
|
{
|
|
splash->hide ();
|
|
show_error (error.get_message ());
|
|
std::exit (1);
|
|
}
|
|
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, ipc, config.rpc);
|
|
rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler);
|
|
rpc->start ();
|
|
}
|
|
else
|
|
{
|
|
// Spawn a child rpc process
|
|
if (!std::filesystem::exists (config.rpc.child_process.rpc_path))
|
|
{
|
|
throw std::runtime_error (std::string ("RPC is configured to spawn a new process however the file cannot be found at: ") + config.rpc.child_process.rpc_path);
|
|
}
|
|
|
|
auto network = node->network_params.network.get_current_network_as_string ();
|
|
rpc_process = std::make_unique<boost::process::child> (config.rpc.child_process.rpc_path, "--daemon", "--data_path", data_path.string (), "--network", network);
|
|
}
|
|
}
|
|
QObject::connect (&application, &QApplication::aboutToQuit, [&] () {
|
|
ipc.stop ();
|
|
node->stop ();
|
|
if (rpc)
|
|
{
|
|
rpc->stop ();
|
|
}
|
|
#if USE_BOOST_PROCESS
|
|
if (rpc_process)
|
|
{
|
|
rpc_process->terminate ();
|
|
}
|
|
#endif
|
|
runner.abort ();
|
|
});
|
|
QApplication::postEvent (&processor, new nano_qt::eventloop_event ([&] () {
|
|
gui = std::make_shared<nano_qt::wallet> (application, processor, *node, wallet, wallet_config.account);
|
|
splash->close ();
|
|
gui->start ();
|
|
gui->client_window->show ();
|
|
}));
|
|
result = QApplication::exec ();
|
|
runner.join ();
|
|
}
|
|
else
|
|
{
|
|
splash->hide ();
|
|
show_error ("Error initializing node");
|
|
}
|
|
write_wallet_config (wallet_config, data_path);
|
|
}
|
|
else
|
|
{
|
|
splash->hide ();
|
|
show_error ("Error deserializing config: " + error.get_message ());
|
|
}
|
|
|
|
logger.info (nano::log::type::daemon_wallet, "Daemon exiting (wallet)");
|
|
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
int main (int argc, char * const * argv)
|
|
{
|
|
nano::set_umask (); // Make sure the process umask is set before any files are created
|
|
nano::initialize_file_descriptor_limit ();
|
|
nano::logger::initialize (nano::log_config::cli_default ());
|
|
|
|
nano::node_singleton_memory_pool_purge_guard memory_pool_cleanup_guard;
|
|
|
|
QApplication application (argc, const_cast<char **> (argv));
|
|
|
|
nano::wallet_daemon daemon;
|
|
|
|
try
|
|
{
|
|
boost::program_options::options_description description ("Command line options");
|
|
// clang-format off
|
|
description.add_options()
|
|
("help", "Print out options")
|
|
("config", boost::program_options::value<std::vector<nano::config_key_value_pair>>()->multitoken(), "Pass configuration values. This takes precedence over any values in the node configuration file. This option can be repeated multiple times.")
|
|
("rpcconfig", boost::program_options::value<std::vector<nano::config_key_value_pair>>()->multitoken(), "Pass RPC configuration values. This takes precedence over any values in the RPC configuration file. This option can be repeated multiple times.");
|
|
nano::add_node_flag_options (description);
|
|
nano::add_node_options (description);
|
|
// clang-format on
|
|
boost::program_options::variables_map vm;
|
|
try
|
|
{
|
|
boost::program_options::store (boost::program_options::parse_command_line (argc, argv, description), vm);
|
|
}
|
|
catch (boost::program_options::error const & err)
|
|
{
|
|
daemon.show_error (err.what ());
|
|
return 1;
|
|
}
|
|
boost::program_options::notify (vm);
|
|
int result (0);
|
|
auto network (vm.find ("network"));
|
|
if (network != vm.end ())
|
|
{
|
|
auto err (nano::network_constants::set_active_network (network->second.as<std::string> ()));
|
|
if (err)
|
|
{
|
|
daemon.show_error (nano::network_constants::active_network_err_msg);
|
|
std::exit (1);
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> config_overrides;
|
|
const auto configItr = vm.find ("config");
|
|
if (configItr != vm.cend ())
|
|
{
|
|
config_overrides = nano::config_overrides (configItr->second.as<std::vector<nano::config_key_value_pair>> ());
|
|
}
|
|
|
|
auto ec = nano::handle_node_options (vm);
|
|
if (ec == nano::error_cli::unknown_command)
|
|
{
|
|
if (vm.count ("help") != 0)
|
|
{
|
|
std::ostringstream outstream;
|
|
description.print (outstream);
|
|
std::string helpstring = outstream.str ();
|
|
daemon.show_help (helpstring);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
std::filesystem::path data_path;
|
|
if (vm.count ("data_path"))
|
|
{
|
|
auto name (vm["data_path"].as<std::string> ());
|
|
data_path = std::filesystem::path (name);
|
|
}
|
|
else
|
|
{
|
|
data_path = nano::working_path ();
|
|
}
|
|
nano::node_flags flags;
|
|
auto flags_ec = nano::update_flags (flags, vm);
|
|
if (flags_ec)
|
|
{
|
|
throw std::runtime_error (flags_ec.message ());
|
|
}
|
|
result = daemon.run_wallet (application, argc, argv, data_path, flags);
|
|
}
|
|
catch (std::exception const & e)
|
|
{
|
|
daemon.show_error (boost::str (boost::format ("Exception while running wallet: %1%") % e.what ()));
|
|
}
|
|
catch (...)
|
|
{
|
|
daemon.show_error ("Unknown exception while running wallet");
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
catch (std::exception const & e)
|
|
{
|
|
daemon.show_error (boost::str (boost::format ("Exception while initializing %1%") % e.what ()));
|
|
}
|
|
catch (...)
|
|
{
|
|
daemon.show_error (boost::str (boost::format ("Unknown exception while initializing")));
|
|
}
|
|
return 1;
|
|
}
|