aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt2
-rw-r--r--src/wallet/wallet2.cpp51
-rw-r--r--src/wallet/wallet2.h5
-rw-r--r--src/wallet/wallet_rpc_server.cpp275
4 files changed, 220 insertions, 113 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index a16f4fe19..be10b9f62 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server
cncrypto
common
version
+ daemonizer
+ ${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 38509ed5a..6ec03ced2 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -161,6 +161,7 @@ struct options {
}
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
+ const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -211,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
+ auto device_name = command_line::get_arg(vm, opts.hw_device);
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -265,6 +267,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
+ wallet->device_name(device_name);
return wallet;
}
@@ -814,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
return command_line::get_arg(vm, options().stagenet);
}
+std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
+{
+ return command_line::get_arg(vm, options().hw_device);
+}
+
void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
@@ -829,6 +837,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
+ command_line::add_arg(desc_params, opts.hw_device);
}
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -983,6 +992,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
return true;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::reconnect_device()
+{
+ bool r = true;
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
+ r = hwdev.init();
+ if (!r){
+ LOG_PRINT_L2("Could not init device");
+ return false;
+ }
+
+ r = hwdev.connect();
+ if (!r){
+ LOG_PRINT_L2("Could not connect to the device");
+ return false;
+ }
+
+ m_account.set_device(hwdev);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -2979,6 +3009,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
+ value.SetString(m_device_name.c_str(), m_device_name.size());
+ json.AddMember("device_name", value, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -3087,6 +3120,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
+ m_device_name = "";
m_key_on_device = false;
encrypted_secret_keys = false;
}
@@ -3218,8 +3252,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
+
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
encrypted_secret_keys = field_encrypted_secret_keys;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
+ if (m_device_name.empty() && field_device_name_found)
+ {
+ m_device_name = field_device_name;
+ }
}
else
{
@@ -3230,7 +3271,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
if (r && m_key_on_device) {
LOG_PRINT_L0("Account on device. Initing device...");
- hw::device &hwdev = hw::get_device("Ledger");
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
m_account.set_device(hwdev);
@@ -3672,13 +3714,18 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_key_on_device = true;
- m_account.create_from_device(device_name);
+
+ auto &hwdev = hw::get_device(device_name);
+ hwdev.set_name(device_name);
+
+ m_account.create_from_device(hwdev);
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
setup_keys(password);
+ m_device_name = device_name;
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index fe39014ba..c30ca1d85 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -172,6 +172,7 @@ namespace tools
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
+ static std::string device_name_option(const boost::program_options::variables_map& vm);
static void init_options(boost::program_options::options_description& desc_params);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
@@ -709,6 +710,7 @@ namespace tools
bool has_unknown_key_images() const;
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
+ bool reconnect_device();
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
@@ -938,6 +940,8 @@ namespace tools
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
+ const std::string & device_name() const { return m_device_name; }
+ void device_name(const std::string & device_name) { m_device_name = device_name; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
@@ -1319,6 +1323,7 @@ namespace tools
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
+ std::string m_device_name;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index ee86587bc..121c67549 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"
@@ -3265,12 +3266,172 @@ 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);
@@ -3282,6 +3443,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(
@@ -3303,115 +3466,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(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 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;
}