From b18f0b10513b0455723518a634595b2f36d5ea25 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 13 Apr 2019 09:19:38 +0000 Subject: [PATCH] wallet: new --offline option It will avoid connecting to a daemon (so useful for cold signing using a RPC wallet), and not perform DNS queries. --- contrib/epee/include/net/http_client.h | 12 +++ src/wallet/node_rpc_proxy.cpp | 13 ++- src/wallet/node_rpc_proxy.h | 6 +- src/wallet/wallet2.cpp | 143 ++++++++++++++++--------- src/wallet/wallet2.h | 13 ++- 5 files changed, 127 insertions(+), 60 deletions(-) diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index bb10c8efc..588d5f0e3 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -274,6 +274,7 @@ namespace net_utils reciev_machine_state m_state; chunked_state m_chunked_state; std::string m_chunked_cache; + bool m_auto_connect; critical_section m_lock; public: @@ -291,6 +292,7 @@ namespace net_utils , m_state() , m_chunked_state() , m_chunked_cache() + , m_auto_connect(true) , m_lock() {} @@ -316,6 +318,11 @@ namespace net_utils m_net_client.set_ssl(std::move(ssl_options)); } + void set_auto_connect(bool auto_connect) + { + m_auto_connect = auto_connect; + } + template void set_connector(F connector) { @@ -367,6 +374,11 @@ namespace net_utils CRITICAL_REGION_LOCAL(m_lock); if(!is_connected()) { + if (!m_auto_connect) + { + MWARNING("Auto connect attempt to " << m_host_buff << ":" << m_port << " disabled"); + return false; + } MDEBUG("Reconnecting..."); if(!connect(timeout)) { diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index f5f3c0e1b..1d5078a11 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -37,9 +37,10 @@ namespace tools static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); -NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) +NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) + , m_offline(false) { invalidate(); } @@ -61,6 +62,8 @@ void NodeRPCProxy::invalidate() boost::optional NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const { + if (m_offline) + return boost::optional("offline"); if (m_rpc_version == 0) { cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); @@ -84,6 +87,8 @@ void NodeRPCProxy::set_height(uint64_t h) boost::optional NodeRPCProxy::get_info() const { + if (m_offline) + return boost::optional("offline"); const time_t now = time(NULL); if (now >= m_get_info_time + 30) // re-cache every 30 seconds { @@ -134,6 +139,8 @@ boost::optional NodeRPCProxy::get_block_weight_limit(uint64_t &bloc boost::optional NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const { + if (m_offline) + return boost::optional("offline"); if (m_earliest_height[version] == 0) { cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t); @@ -161,6 +168,8 @@ boost::optional NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_ if (result) return result; + if (m_offline) + return boost::optional("offline"); if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks) { cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); @@ -191,6 +200,8 @@ boost::optional NodeRPCProxy::get_fee_quantization_mask(uint64_t &f if (result) return result; + if (m_offline) + return boost::optional("offline"); if (m_dynamic_base_fee_estimate_cached_height != height) { cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 3630aec08..3b75c8b94 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -39,9 +39,10 @@ namespace tools class NodeRPCProxy { public: - NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex); + NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex); void invalidate(); + void set_offline(bool offline) { m_offline = offline; } boost::optional get_rpc_version(uint32_t &version) const; boost::optional get_height(uint64_t &height) const; @@ -56,7 +57,8 @@ private: boost::optional get_info() const; epee::net_utils::http::http_simple_client &m_http_client; - boost::mutex &m_daemon_rpc_mutex; + boost::recursive_mutex &m_daemon_rpc_mutex; + bool m_offline; mutable uint64_t m_height; mutable uint64_t m_earliest_height[256]; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b288994a5..f662555b5 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -266,6 +266,7 @@ struct options { const command_line::arg_descriptor hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""}; const command_line::arg_descriptor tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" }; const command_line::arg_descriptor no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false}; + const command_line::arg_descriptor offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false}; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file) @@ -456,6 +457,9 @@ std::unique_ptr make_basic(const boost::program_options::variabl if (command_line::get_arg(vm, opts.no_dns)) wallet->enable_dns(false); + if (command_line::get_arg(vm, opts.offline)) + wallet->set_offline(); + try { if (!command_line::is_arg_defaulted(vm, opts.tx_notify)) @@ -1083,7 +1087,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_unattended(unattended), m_devices_registered(false), m_device_last_key_image_sync(0), - m_use_dns(true) + m_use_dns(true), + m_offline(false) { } @@ -1139,6 +1144,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.hw_device_derivation_path); command_line::add_arg(desc_params, opts.tx_notify); command_line::add_arg(desc_params, opts.no_dns); + command_line::add_arg(desc_params, opts.offline); } std::pair, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function(const char *, bool)> &password_prompter) @@ -1184,6 +1190,8 @@ std::unique_ptr wallet2::make_dummy(const boost::program_options::varia //---------------------------------------------------------------------------------------------------- bool wallet2::set_daemon(std::string daemon_address, boost::optional daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) { + boost::lock_guard lock(m_daemon_rpc_mutex); + if(m_http_client.is_connected()) m_http_client.disconnect(); m_daemon_address = std::move(daemon_address); @@ -2392,7 +2400,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, req.start_height = start_height; req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); @@ -2414,7 +2422,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, req.start_height = start_height; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin"); @@ -2691,7 +2699,7 @@ void wallet2::update_pool_state(bool refreshed) cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req; cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin"); @@ -2838,7 +2846,7 @@ void wallet2::update_pool_state(bool refreshed) req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); MDEBUG("Got " << r << " and " << res.status); if (r && res.status == CORE_RPC_STATUS_OK) @@ -2999,6 +3007,13 @@ std::shared_ptr, size_t>> wallet2::create //---------------------------------------------------------------------------------------------------- void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool) { + if (m_offline) + { + blocks_fetched = 0; + received_money = 0; + return; + } + if(m_light_wallet) { // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. @@ -3258,7 +3273,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector req.binary = true; req.compress = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (!r) { @@ -5088,7 +5103,14 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) { THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized); - boost::lock_guard lock(m_daemon_rpc_mutex); + if (m_offline) + { + if (version) + *version = 0; + if (ssl) + *ssl = false; + return false; + } // TODO: Add light wallet version check. if(m_light_wallet) { @@ -5099,20 +5121,23 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) return m_light_wallet_connected; } - if(!m_http_client.is_connected(ssl)) { - m_node_rpc_proxy.invalidate(); - if (!m_http_client.connect(std::chrono::milliseconds(timeout))) - return false; + boost::lock_guard lock(m_daemon_rpc_mutex); if(!m_http_client.is_connected(ssl)) - return false; + { + m_node_rpc_proxy.invalidate(); + if (!m_http_client.connect(std::chrono::milliseconds(timeout))) + return false; + if(!m_http_client.is_connected(ssl)) + return false; + } } if (version) { cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client); + bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t); if(!r) { *version = 0; return false; @@ -5126,6 +5151,18 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) return true; } //---------------------------------------------------------------------------------------------------- +void wallet2::set_offline(bool offline) +{ + m_offline = offline; + m_http_client.set_auto_connect(!offline); + if (offline) + { + boost::lock_guard lock(m_daemon_rpc_mutex); + if(m_http_client.is_connected()) + m_http_client.disconnect(); + } +} +//---------------------------------------------------------------------------------------------------- bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const { hw::device &hwdev = m_account.get_device(); @@ -5304,7 +5341,7 @@ void wallet2::trim_hashchain() cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); req.height = m_blockchain.size() - 1; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); if (r && res.status == CORE_RPC_STATUS_OK) { @@ -5660,7 +5697,7 @@ void wallet2::rescan_spent() for (size_t n = start_offset; n < start_offset + n_outputs; ++n) req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image)); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); + bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); @@ -5988,7 +6025,7 @@ void wallet2::commit_tx(pending_tx& ptx) oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); // MyMonero and OpenMonero use different status strings @@ -6002,7 +6039,7 @@ void wallet2::commit_tx(pending_tx& ptx) req.do_not_relay = false; COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, m_http_client, rpc_timeout); + bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); @@ -6955,7 +6992,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority) m_daemon_rpc_mutex.lock(); getbh_req.start_height = m_blockchain.size() - N; getbh_req.end_height = m_blockchain.size() - 1; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange"); THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange"); @@ -7126,7 +7163,7 @@ bool wallet2::unset_ring(const crypto::hash &txid) req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon"); if (res.txs.empty()) @@ -7180,8 +7217,8 @@ bool wallet2::find_and_save_rings(bool force) req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s])); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -7322,7 +7359,7 @@ void wallet2::light_wallet_get_outs(std::vector> req_t.unlocked = true; req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -7485,7 +7522,7 @@ void wallet2::get_outs(std::vector> req_t.cumulative = true; req_t.binary = true; m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution"); @@ -7884,7 +7921,7 @@ void wallet2::get_outs(std::vector> // get the keys for those req.get_txid = false; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); + bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -8596,7 +8633,7 @@ bool wallet2::light_wallet_login(bool &new_address) // Always create account if it doesn't exist. request.create_account = true; m_daemon_rpc_mutex.lock(); - bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, rpc_timeout, "POST"); + bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); // MyMonero doesn't send any status message. OpenMonero does. m_light_wallet_connected = connected && (response.status.empty() || response.status == "success"); @@ -8621,7 +8658,7 @@ bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLE oreq.address = get_account().get_public_address_str(m_nettype); oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request"); @@ -8647,7 +8684,7 @@ void wallet2::light_wallet_get_unspent_outs() m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_unspent_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs"); THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason); @@ -8792,7 +8829,7 @@ bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO: request.address = get_account().get_public_address_str(m_nettype); request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info"); // TODO: Validate result @@ -8809,7 +8846,7 @@ void wallet2::light_wallet_get_address_txs() ireq.address = get_account().get_public_address_str(m_nettype); ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, rpc_timeout, "POST"); + bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST"); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs"); //OpenMonero sends status=success, Mymonero doesn't. @@ -10113,7 +10150,7 @@ std::vector wallet2::select_available_outputs_from_histogram(uint64_t co req_t.max_count = 0; req_t.unlocked = unlocked; req_t.recent_cutoff = 0; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -10151,7 +10188,7 @@ uint64_t wallet2::get_num_rct_outputs() req_t.max_count = 0; req_t.unlocked = true; req_t.recent_cutoff = 0; - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -10276,7 +10313,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10319,8 +10356,8 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10369,8 +10406,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10431,8 +10468,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -10487,8 +10524,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_json("/gettransactions", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions"); @@ -10560,8 +10597,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); bool r; { - const boost::lock_guard lock{m_daemon_rpc_mutex}; - r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); + const boost::lock_guard lock{m_daemon_rpc_mutex}; + r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout); } THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin"); @@ -10659,7 +10696,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10708,7 +10745,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -10863,7 +10900,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account req.decode_as_json = false; req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -11155,7 +11192,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr gettx_req.decode_as_json = false; gettx_req.prune = true; m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client); + bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(), error::wallet_internal_error, "Failed to get transaction from daemon"); @@ -11166,7 +11203,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr for (size_t i = 0; i < proofs.size(); ++i) kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image)); m_daemon_rpc_mutex.lock(); - ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout); + ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(), error::wallet_internal_error, "Failed to get key image spent status from daemon"); @@ -11718,7 +11755,7 @@ uint64_t wallet2::import_key_images(const std::vector> wallet2::estimate_backlog(const std:: cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, m_http_client, rpc_timeout); + bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); @@ -12879,7 +12916,7 @@ uint64_t wallet2::get_segregation_fork_height() const if (m_segregation_height > 0) return m_segregation_height; - if (m_use_dns) + if (m_use_dns && !m_offline) { // All four MoneroPulse domains have DNSSEC on and valid static const std::vector dns_urls = { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 39380c9df..094346293 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1232,19 +1232,22 @@ namespace tools template inline bool invoke_http_json(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET") { - boost::lock_guard lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_json(uri, req, res, m_http_client, timeout, http_method); } template inline bool invoke_http_bin(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET") { - boost::lock_guard lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_bin(uri, req, res, m_http_client, timeout, http_method); } template inline bool invoke_http_json_rpc(const boost::string_ref uri, const std::string& method_name, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") { - boost::lock_guard lock(m_daemon_rpc_mutex); + if (m_offline) return false; + boost::lock_guard lock(m_daemon_rpc_mutex); return epee::net_utils::invoke_http_json_rpc(uri, method_name, req, res, m_http_client, timeout, http_method, req_id); } @@ -1291,6 +1294,7 @@ namespace tools uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const; void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash); void enable_dns(bool enable) { m_use_dns = enable; } + void set_offline(bool offline = true); private: /*! @@ -1422,7 +1426,7 @@ namespace tools std::atomic m_run; - boost::mutex m_daemon_rpc_mutex; + boost::recursive_mutex m_daemon_rpc_mutex; bool m_trusted_daemon; i_wallet2_callback* m_callback; @@ -1474,6 +1478,7 @@ namespace tools std::string m_device_derivation_path; uint64_t m_device_last_key_image_sync; bool m_use_dns; + bool m_offline; // Aux transaction data from device std::unordered_map m_tx_device;