diff options
Diffstat (limited to 'src/wallet/wallet_rpc_server.cpp')
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 436 |
1 files changed, 278 insertions, 158 deletions
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 2cddea25d..86b46b173 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -51,6 +51,7 @@ using namespace epee; #include "mnemonics/electrum-words.h" #include "rpc/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "daemonizer/daemonizer.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -59,7 +60,6 @@ namespace { const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"}; - const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false}; const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false}; const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; @@ -100,7 +100,7 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_restricted(false), m_vm(NULL) + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_restricted(false), m_vm(NULL) { } //------------------------------------------------------------------------------------------------------------------------------ @@ -120,7 +120,7 @@ namespace tools m_stop = false; m_net_server.add_idle_handler([this](){ try { - if (m_wallet) m_wallet->refresh(m_trusted_daemon); + if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon()); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -156,31 +156,18 @@ namespace tools return false; m_vm = vm; - tools::wallet2 *walvars; - std::unique_ptr<tools::wallet2> tmpwal; - if (m_wallet) - walvars = m_wallet; - else - { - tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter); - walvars = tmpwal.get(); - } boost::optional<epee::net_utils::http::login> http_login{}; std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port); const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login); - m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon); - if (!command_line::has_arg(*m_vm, arg_trusted_daemon)) + m_restricted = command_line::get_arg(*m_vm, arg_restricted); + if (!command_line::is_arg_defaulted(*m_vm, arg_wallet_dir)) { - if (tools::is_local_address(walvars->get_daemon_address())) + if (!command_line::is_arg_defaulted(*m_vm, wallet_args::arg_wallet_file())) { - MINFO(tr("Daemon is local, assuming trusted")); - m_trusted_daemon = true; + MERROR(arg_wallet_dir.name << " and " << wallet_args::arg_wallet_file().name << " are incompatible, use only one of them"); + return false; } - } - m_restricted = command_line::get_arg(*m_vm, arg_restricted); - if (command_line::has_arg(*m_vm, arg_wallet_dir)) - { m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); #ifdef _WIN32 #define MKDIR(path, mode) mkdir(path) @@ -773,10 +760,10 @@ namespace tools { if (get_tx_key) { - std::string s = epee::string_tools::pod_to_hex(ptx.tx_key); + epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key); for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) - s += epee::string_tools::pod_to_hex(additional_tx_key); - fill(tx_key, s); + s += epee::to_hex::wipeable_string(additional_tx_key); + fill(tx_key, std::string(s.data(), s.size())); } // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); @@ -857,7 +844,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); if (ptx_vector.empty()) { @@ -918,7 +905,7 @@ namespace tools } uint32_t priority = m_wallet->adjust_priority(req.priority); LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, @@ -1079,7 +1066,7 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1127,7 +1114,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1183,7 +1170,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, m_trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra); if (ptx_vector.empty()) { @@ -1552,7 +1539,6 @@ namespace tools rpc_transfers.spent = td.m_spent; rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); - rpc_transfers.tx_size = txBlob.size(); rpc_transfers.subaddr_index = td.m_subaddr_index.minor; rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; res.transfers.push_back(rpc_transfers); @@ -1584,11 +1570,13 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key); + res.key = std::string(key.data(), key.size()); } else if(req.key_type.compare("spend_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); + res.key = std::string(key.data(), key.size()); } else { @@ -1814,11 +1802,11 @@ namespace tools return false; } - std::ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + epee::wipeable_string s; + s += epee::to_hex::wipeable_string(tx_key); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); - res.tx_key = oss.str(); + s += epee::to_hex::wipeable_string(additional_tx_keys[i]); + res.tx_key = std::string(s.data(), s.size()); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1834,26 +1822,33 @@ namespace tools return false; } - std::string tx_key_str = req.tx_key; + epee::wipeable_string tx_key_str = req.tx_key; + if (tx_key_str.size() < 64 || tx_key_str.size() % 64) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; + er.message = "Tx key has invalid format"; + return false; + } + const char *data = tx_key_str.data(); crypto::secret_key tx_key; - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key)) + if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key)))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + size_t offset = 64; std::vector<crypto::secret_key> additional_tx_keys; - while (!tx_key_str.empty()) + while (offset < tx_key_str.size()) { additional_tx_keys.resize(additional_tx_keys.size() + 1); - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back())) + if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back())))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + offset += 64; } cryptonote::address_parse_info info; @@ -2304,7 +2299,7 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - if (!m_trusted_daemon) + if (!m_wallet->is_trusted_daemon()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "This command requires a trusted daemon."; @@ -2518,7 +2513,7 @@ namespace tools } try { - m_wallet->refresh(m_trusted_daemon, req.start_height, res.blocks_fetched, res.received_money); + m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_money); return true; } catch (const std::exception& e) @@ -2554,7 +2549,7 @@ namespace tools bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); - if (!m_trusted_daemon) + if (!m_wallet->is_trusted_daemon()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "This command requires a trusted daemon."; @@ -2688,8 +2683,20 @@ namespace tools er.message = "Failed to generate wallet"; return false; } + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } delete m_wallet; + } m_wallet = wal.release(); return true; } @@ -2746,12 +2753,74 @@ namespace tools er.message = "Failed to open wallet"; return false; } + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } delete m_wallet; + } m_wallet = wal.release(); return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + + try + { + m_wallet->store(); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + delete m_wallet; + m_wallet = NULL; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (m_wallet->verify_password(req.old_password)) + { + try + { + m_wallet->rewrite(m_wallet->get_wallet_file(), req.new_password); + m_wallet->store(); + LOG_PRINT_L0("Wallet password changed."); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + } + else + { + er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD; + er.message = "Invalid original password."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) { try { @@ -2993,7 +3062,7 @@ namespace tools return false; } - if (m_trusted_daemon) + if (m_wallet->is_trusted_daemon()) { try { @@ -3206,17 +3275,176 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ } +class t_daemon +{ +private: + const boost::program_options::variables_map& vm; + +public: + t_daemon(boost::program_options::variables_map const & _vm) + : vm(_vm) + { + } + + bool run() + { + std::unique_ptr<tools::wallet2> wal; + try + { + const bool testnet = tools::wallet2::has_testnet_option(vm); + const bool stagenet = tools::wallet2::has_stagenet_option(vm); + if (testnet && stagenet) + { + MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); + return false; + } + + const auto arg_wallet_file = wallet_args::arg_wallet_file(); + const auto arg_from_json = wallet_args::arg_generate_from_json(); + + const auto wallet_file = command_line::get_arg(vm, arg_wallet_file); + const auto from_json = command_line::get_arg(vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir); + const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password); + const auto password_prompt = prompt_for_password ? password_prompter : nullptr; + + if(!wallet_file.empty() && !from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); + return false; + } + + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + + if (wallet_file.empty() && from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); + return false; + } + + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); + if(!wallet_file.empty()) + { + wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first; + } + else + { + try + { + wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt); + } + catch (const std::exception &e) + { + MERROR("Error creating wallet: " << e.what()); + return false; + } + } + if (!wal) + { + return false; + } + + bool quit = false; + tools::signal_handler::install([&wal, &quit](int) { + assert(wal); + quit = true; + wal->stop(); + }); + + wal->refresh(wal->is_trusted_daemon()); + // if we ^C during potentially length load/refresh, there's no server loop yet + if (quit) + { + MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); + wal->store(); + MINFO(tools::wallet_rpc_server::tr("Successfully saved")); + return false; + } + MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); + return false; + } + just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&vm); + CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); + tools::signal_handler::install([&wrpc](int) { + wrpc.send_stop_signal(); + }); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); + try + { + wrpc.run(); + } + catch (const std::exception &e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); + return false; + } + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); + try + { + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); + wrpc.stop(); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); + return false; + } + return true; + } +}; + +class t_executor final +{ +public: + static std::string const NAME; + + std::string const & name() + { + return NAME; + } + + t_daemon create_daemon(boost::program_options::variables_map const & vm) + { + return t_daemon(vm); + } + + bool run_non_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } + + bool run_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } +}; + +std::string const t_executor::NAME = "Wallet RPC Daemon"; + int main(int argc, char** argv) { namespace po = boost::program_options; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_from_json = wallet_args::arg_generate_from_json(); + po::options_description hidden_options("Hidden"); + po::options_description desc_params(wallet_args::tr("Wallet options")); tools::wallet2::init_options(desc_params); command_line::add_arg(desc_params, arg_rpc_bind_port); command_line::add_arg(desc_params, arg_disable_rpc_login); - command_line::add_arg(desc_params, arg_trusted_daemon); command_line::add_arg(desc_params, arg_restricted); cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); @@ -3224,6 +3452,8 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); + daemonizer::init_options(hidden_options, desc_params); + boost::optional<po::variables_map> vm; bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( @@ -3245,115 +3475,5 @@ int main(int argc, char** argv) { return 0; } - std::unique_ptr<tools::wallet2> wal; - try - { - const bool testnet = tools::wallet2::has_testnet_option(*vm); - const bool stagenet = tools::wallet2::has_stagenet_option(*vm); - if (testnet && stagenet) - { - MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); - return 1; - } - - const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); - const auto from_json = command_line::get_arg(*vm, arg_from_json); - const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); - const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password); - const auto password_prompt = prompt_for_password ? password_prompter : nullptr; - - if(!wallet_file.empty() && !from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); - return 1; - } - - if (!wallet_dir.empty()) - { - wal = NULL; - goto just_dir; - } - - if (wallet_file.empty() && from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); - return 1; - } - - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); - if(!wallet_file.empty()) - { - wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first; - } - else - { - try - { - wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt); - } - catch (const std::exception &e) - { - MERROR("Error creating wallet: " << e.what()); - return 1; - } - } - if (!wal) - { - return 1; - } - - bool quit = false; - tools::signal_handler::install([&wal, &quit](int) { - assert(wal); - quit = true; - wal->stop(); - }); - - wal->refresh(command_line::get_arg(*vm, arg_trusted_daemon)); - // if we ^C during potentially length load/refresh, there's no server loop yet - if (quit) - { - MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); - wal->store(); - MINFO(tools::wallet_rpc_server::tr("Successfully saved")); - return 1; - } - MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); - return 1; - } -just_dir: - tools::wallet_rpc_server wrpc; - if (wal) wrpc.set_wallet(wal.release()); - bool r = wrpc.init(&(vm.get())); - CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); - tools::signal_handler::install([&wrpc](int) { - wrpc.send_stop_signal(); - }); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); - try - { - wrpc.run(); - } - catch (const std::exception &e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); - return 1; - } - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); - try - { - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); - wrpc.stop(); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); - return 1; - } - return 0; + return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1; } |