Allow wallet2.h to run in WebAssembly

- Add abstract_http_client.h which http_client.h extends.
- Replace simple_http_client with abstract_http_client in wallet2,
message_store, message_transporter, and node_rpc_proxy.
- Import and export wallet data in wallet2.
- Use #if defined __EMSCRIPTEN__ directives to skip incompatible code.
This commit is contained in:
woodser 2020-04-15 13:22:46 -04:00
parent 7c74e1919e
commit 87d75584e8
16 changed files with 517 additions and 318 deletions

View file

@ -0,0 +1,87 @@
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <string>
#include <boost/optional/optional.hpp>
#include "http_auth.h"
#include "net/net_ssl.h"
namespace epee
{
namespace net_utils
{
inline const char* get_hex_vals()
{
static constexpr const char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
return hexVals;
}
inline const char* get_unsave_chars()
{
//static constexpr char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&";
static constexpr const char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&";
return unsave_chars;
}
bool is_unsafe(unsigned char compare_char);
std::string dec_to_hex(char num, int radix);
int get_index(const char *s, char c);
std::string hex_to_dec_2bytes(const char *s);
std::string convert(char val);
std::string conver_to_url_format(const std::string& uri);
std::string convert_from_url_format(const std::string& uri);
std::string convert_to_url_format_force_all(const std::string& uri);
namespace http
{
class abstract_http_client
{
public:
abstract_http_client() {}
virtual ~abstract_http_client() {}
bool set_server(const std::string& address, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
virtual void set_server(std::string host, std::string port, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) = 0;
virtual void set_auto_connect(bool auto_connect) = 0;
virtual bool connect(std::chrono::milliseconds timeout) = 0;
virtual bool disconnect() = 0;
virtual bool is_connected(bool *ssl = NULL) = 0;
virtual bool invoke(const boost::string_ref uri, const boost::string_ref method, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual uint64_t get_bytes_sent() const = 0;
virtual uint64_t get_bytes_received() const = 0;
};
class http_client_factory
{
public:
virtual ~http_client_factory() {}
virtual std::unique_ptr<abstract_http_client> create() = 0;
};
}
}
}

View file

@ -47,6 +47,7 @@
#include "string_tools.h" #include "string_tools.h"
#include "reg_exp_definer.h" #include "reg_exp_definer.h"
#include "abstract_http_client.h"
#include "http_base.h" #include "http_base.h"
#include "http_auth.h" #include "http_auth.h"
#include "to_nonconst_iterator.h" #include "to_nonconst_iterator.h"
@ -105,140 +106,11 @@ namespace net_utils
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static inline const char* get_hex_vals()
{
static const char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
return hexVals;
}
static inline const char* get_unsave_chars()
{
//static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&";
static const char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&";
return unsave_chars;
}
static inline bool is_unsafe(unsigned char compare_char)
{
if(compare_char <= 32 || compare_char >= 123)
return true;
const char* punsave = get_unsave_chars();
for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++)
if(compare_char == punsave[ichar_pos])
return true;
return false;
}
static inline
std::string dec_to_hex(char num, int radix)
{
int temp=0;
std::string csTmp;
int num_char;
num_char = (int) num;
if (num_char < 0)
num_char = 256 + num_char;
while (num_char >= radix)
{
temp = num_char % radix;
num_char = (int)floor((float)num_char / (float)radix);
csTmp = get_hex_vals()[temp];
}
csTmp += get_hex_vals()[num_char];
if(csTmp.size() < 2)
{
csTmp += '0';
}
std::reverse(csTmp.begin(), csTmp.end());
//_mbsrev((unsigned char*)csTmp.data());
return csTmp;
}
static inline int get_index(const char *s, char c) { const char *ptr = (const char*)memchr(s, c, 16); return ptr ? ptr-s : -1; }
static inline
std::string hex_to_dec_2bytes(const char *s)
{
const char *hex = get_hex_vals();
int i0 = get_index(hex, toupper(s[0]));
int i1 = get_index(hex, toupper(s[1]));
if (i0 < 0 || i1 < 0)
return std::string("%") + std::string(1, s[0]) + std::string(1, s[1]);
return std::string(1, i0 * 16 | i1);
}
static inline std::string convert(char val)
{
std::string csRet;
csRet += "%";
csRet += dec_to_hex(val, 16);
return csRet;
}
static inline std::string conver_to_url_format(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
if(is_unsafe(uri[i]))
result += convert(uri[i]);
else
result += uri[i];
}
return result;
}
static inline std::string convert_from_url_format(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
if(uri[i] == '%' && i + 2 < uri.size())
{
result += hex_to_dec_2bytes(uri.c_str() + i + 1);
i += 2;
}
else
result += uri[i];
}
return result;
}
static inline std::string convert_to_url_format_force_all(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
result += convert(uri[i]);
}
return result;
}
namespace http namespace http
{ {
template<typename net_client_type> template<typename net_client_type>
class http_simple_client_template: public i_target_handler class http_simple_client_template : public i_target_handler, public abstract_http_client
{ {
private: private:
enum reciev_machine_state enum reciev_machine_state
@ -279,7 +151,7 @@ namespace net_utils
public: public:
explicit http_simple_client_template() explicit http_simple_client_template()
: i_target_handler() : i_target_handler(), abstract_http_client()
, m_net_client() , m_net_client()
, m_host_buff() , m_host_buff()
, m_port() , m_port()
@ -299,26 +171,19 @@ namespace net_utils
const std::string &get_host() const { return m_host_buff; }; const std::string &get_host() const { return m_host_buff; };
const std::string &get_port() const { return m_port; }; const std::string &get_port() const { return m_port; };
bool set_server(const std::string& address, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) using abstract_http_client::set_server;
{
http::url_content parsed{};
const bool r = parse_url(address, parsed);
CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address);
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), std::move(ssl_options));
return true;
}
void set_server(std::string host, std::string port, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) void set_server(std::string host, std::string port, boost::optional<login> user, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
disconnect(); disconnect();
m_host_buff = std::move(host); m_host_buff = std::move(host);
m_port = std::move(port); m_port = std::move(port);
m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{};
m_net_client.set_ssl(std::move(ssl_options)); m_net_client.set_ssl(std::move(ssl_options));
} }
void set_auto_connect(bool auto_connect) void set_auto_connect(bool auto_connect) override
{ {
m_auto_connect = auto_connect; m_auto_connect = auto_connect;
} }
@ -330,25 +195,25 @@ namespace net_utils
m_net_client.set_connector(std::move(connector)); m_net_client.set_connector(std::move(connector));
} }
bool connect(std::chrono::milliseconds timeout) bool connect(std::chrono::milliseconds timeout) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
return m_net_client.connect(m_host_buff, m_port, timeout); return m_net_client.connect(m_host_buff, m_port, timeout);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool disconnect() bool disconnect() override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
return m_net_client.disconnect(); return m_net_client.disconnect();
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool is_connected(bool *ssl = NULL) bool is_connected(bool *ssl = NULL) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
return m_net_client.is_connected(ssl); return m_net_client.is_connected(ssl);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
virtual bool handle_target_data(std::string& piece_of_transfer) virtual bool handle_target_data(std::string& piece_of_transfer) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
m_response_info.m_body += piece_of_transfer; m_response_info.m_body += piece_of_transfer;
@ -361,15 +226,14 @@ namespace net_utils
return true; return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
inline inline bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) override
bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
return invoke(uri, "GET", body, timeout, ppresponse_info, additional_params); return invoke(uri, "GET", body, timeout, ppresponse_info, additional_params);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
inline bool invoke(const boost::string_ref uri, const boost::string_ref method, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) inline bool invoke(const boost::string_ref uri, const boost::string_ref method, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
if(!is_connected()) if(!is_connected())
@ -442,7 +306,7 @@ namespace net_utils
return false; return false;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
inline bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) inline bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) override
{ {
CRITICAL_REGION_LOCAL(m_lock); CRITICAL_REGION_LOCAL(m_lock);
return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params); return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params);
@ -456,12 +320,12 @@ namespace net_utils
return handle_reciev(timeout); return handle_reciev(timeout);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
uint64_t get_bytes_sent() const uint64_t get_bytes_sent() const override
{ {
return m_net_client.get_bytes_sent(); return m_net_client.get_bytes_sent();
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
uint64_t get_bytes_received() const uint64_t get_bytes_received() const override
{ {
return m_net_client.get_bytes_received(); return m_net_client.get_bytes_received();
} }
@ -1016,6 +880,14 @@ namespace net_utils
} }
}; };
typedef http_simple_client_template<blocked_mode_client> http_simple_client; typedef http_simple_client_template<blocked_mode_client> http_simple_client;
class http_simple_client_factory : public http_client_factory
{
public:
std::unique_ptr<abstract_http_client> create() override {
return std::unique_ptr<epee::net_utils::http::abstract_http_client>(new epee::net_utils::http::http_simple_client());
}
};
} }
} }
} }

View file

@ -38,7 +38,7 @@ namespace epee
namespace net_utils namespace net_utils
{ {
template<class t_request, class t_response, class t_transport> template<class t_request, class t_response, class t_transport>
bool invoke_http_json(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "GET") bool invoke_http_json(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "POST")
{ {
std::string req_param; std::string req_param;
if(!serialization::store_t_to_json(out_struct, req_param)) if(!serialization::store_t_to_json(out_struct, req_param))
@ -72,7 +72,7 @@ namespace epee
template<class t_request, class t_response, class t_transport> template<class t_request, class t_response, class t_transport>
bool invoke_http_bin(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "GET") bool invoke_http_bin(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "POST")
{ {
std::string req_param; std::string req_param;
if(!serialization::store_t_to_binary(out_struct, req_param)) if(!serialization::store_t_to_binary(out_struct, req_param))
@ -101,7 +101,7 @@ namespace epee
} }
template<class t_request, class t_response, class t_transport> template<class t_request, class t_response, class t_transport>
bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, epee::json_rpc::error &error_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, epee::json_rpc::error &error_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0")
{ {
epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t);
req_t.jsonrpc = "2.0"; req_t.jsonrpc = "2.0";
@ -125,14 +125,14 @@ namespace epee
} }
template<class t_request, class t_response, class t_transport> template<class t_request, class t_response, class t_transport>
bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0")
{ {
epee::json_rpc::error error_struct; epee::json_rpc::error error_struct;
return invoke_http_json_rpc(uri, method_name, out_struct, result_struct, error_struct, transport, timeout, http_method, req_id); return invoke_http_json_rpc(uri, method_name, out_struct, result_struct, error_struct, transport, timeout, http_method, req_id);
} }
template<class t_command, class t_transport> template<class t_command, class t_transport>
bool invoke_http_json_rpc(const boost::string_ref uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") bool invoke_http_json_rpc(const boost::string_ref uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0")
{ {
return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id);
} }

View file

@ -150,7 +150,7 @@ namespace epee
}; };
#define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x) #define CRITICAL_REGION_LOCAL(x) {} epee::critical_region_t<decltype(x)> critical_region_var(x)
#define CRITICAL_REGION_BEGIN(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var(x) #define CRITICAL_REGION_BEGIN(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var(x)
#define CRITICAL_REGION_LOCAL1(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var1(x) #define CRITICAL_REGION_LOCAL1(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var1(x)
#define CRITICAL_REGION_BEGIN1(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var1(x) #define CRITICAL_REGION_BEGIN1(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var1(x)

View file

@ -26,7 +26,7 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_library(epee STATIC byte_slice.cpp hex.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp add_library(epee STATIC byte_slice.cpp hex.cpp abstract_http_client.cpp http_auth.cpp mlog.cpp net_helper.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp
levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp levin_base.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp
int-util.cpp) int-util.cpp)

View file

@ -0,0 +1,142 @@
#include "net/abstract_http_client.h"
#include "net/http_base.h"
#include "net/net_parse_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.http"
namespace epee
{
namespace net_utils
{
//----------------------------------------------------------------------------------------------------
bool is_unsafe(unsigned char compare_char)
{
if(compare_char <= 32 || compare_char >= 123)
return true;
const char* punsave = get_unsave_chars();
for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++)
if(compare_char == punsave[ichar_pos])
return true;
return false;
}
//----------------------------------------------------------------------------------------------------
std::string dec_to_hex(char num, int radix)
{
int temp=0;
std::string csTmp;
int num_char;
num_char = (int) num;
if (num_char < 0)
num_char = 256 + num_char;
while (num_char >= radix)
{
temp = num_char % radix;
num_char = (int)floor((float)num_char / (float)radix);
csTmp = get_hex_vals()[temp];
}
csTmp += get_hex_vals()[num_char];
if(csTmp.size() < 2)
{
csTmp += '0';
}
std::reverse(csTmp.begin(), csTmp.end());
//_mbsrev((unsigned char*)csTmp.data());
return csTmp;
}
//----------------------------------------------------------------------------------------------------
int get_index(const char *s, char c)
{
const char *ptr = (const char*)memchr(s, c, 16);
return ptr ? ptr-s : -1;
}
//----------------------------------------------------------------------------------------------------
std::string hex_to_dec_2bytes(const char *s)
{
const char *hex = get_hex_vals();
int i0 = get_index(hex, toupper(s[0]));
int i1 = get_index(hex, toupper(s[1]));
if (i0 < 0 || i1 < 0)
return std::string("%") + std::string(1, s[0]) + std::string(1, s[1]);
return std::string(1, i0 * 16 | i1);
}
//----------------------------------------------------------------------------------------------------
std::string convert(char val)
{
std::string csRet;
csRet += "%";
csRet += dec_to_hex(val, 16);
return csRet;
}
//----------------------------------------------------------------------------------------------------
std::string conver_to_url_format(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
if(is_unsafe(uri[i]))
result += convert(uri[i]);
else
result += uri[i];
}
return result;
}
//----------------------------------------------------------------------------------------------------
std::string convert_from_url_format(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
if(uri[i] == '%' && i + 2 < uri.size())
{
result += hex_to_dec_2bytes(uri.c_str() + i + 1);
i += 2;
}
else
result += uri[i];
}
return result;
}
//----------------------------------------------------------------------------------------------------
std::string convert_to_url_format_force_all(const std::string& uri)
{
std::string result;
for(size_t i = 0; i!= uri.size(); i++)
{
result += convert(uri[i]);
}
return result;
}
namespace http
{
//----------------------------------------------------------------------------------------------------
bool epee::net_utils::http::abstract_http_client::set_server(const std::string& address, boost::optional<login> user, ssl_options_t ssl_options)
{
http::url_content parsed{};
const bool r = parse_url(address, parsed);
CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address);
set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), std::move(ssl_options));
return true;
}
}
}
}

View file

@ -48,7 +48,7 @@
namespace mms namespace mms
{ {
message_store::message_store() message_store::message_store(std::unique_ptr<epee::net_utils::http::abstract_http_client> http_client) : m_transporter(std::move(http_client))
{ {
m_active = false; m_active = false;
m_auto_send = false; m_auto_send = false;

View file

@ -43,6 +43,7 @@
#include "common/i18n.h" #include "common/i18n.h"
#include "common/command_line.h" #include "common/command_line.h"
#include "wipeable_string.h" #include "wipeable_string.h"
#include "net/abstract_http_client.h"
#include "message_transporter.h" #include "message_transporter.h"
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
@ -202,7 +203,8 @@ namespace mms
class message_store class message_store
{ {
public: public:
message_store(); message_store(std::unique_ptr<epee::net_utils::http::abstract_http_client> http_client);
// Initialize and start to use the MMS, set the first signer, this wallet itself // Initialize and start to use the MMS, set the first signer, this wallet itself
// Filename, if not null and not empty, is used to create the ".mms" file // Filename, if not null and not empty, is used to create the ".mms" file
// reset it if already used, with deletion of all signers and messages // reset it if already used, with deletion of all signers and messages

View file

@ -80,7 +80,7 @@ namespace bitmessage_rpc
} }
message_transporter::message_transporter() message_transporter::message_transporter(std::unique_ptr<epee::net_utils::http::abstract_http_client> http_client) : m_http_client(std::move(http_client))
{ {
m_run = true; m_run = true;
} }
@ -96,7 +96,7 @@ void message_transporter::set_options(const std::string &bitmessage_address, con
} }
m_bitmessage_login = bitmessage_login; m_bitmessage_login = bitmessage_login;
m_http_client.set_server(address_parts.host, std::to_string(address_parts.port), boost::none); m_http_client->set_server(address_parts.host, std::to_string(address_parts.port), boost::none);
} }
bool message_transporter::receive_messages(const std::vector<std::string> &destination_transport_addresses, bool message_transporter::receive_messages(const std::vector<std::string> &destination_transport_addresses,
@ -256,7 +256,7 @@ bool message_transporter::post_request(const std::string &request, std::string &
additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8")); additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8"));
const epee::net_utils::http::http_response_info* response = NULL; const epee::net_utils::http::http_response_info* response = NULL;
std::chrono::milliseconds timeout = std::chrono::seconds(15); std::chrono::milliseconds timeout = std::chrono::seconds(15);
bool r = m_http_client.invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params)); bool r = m_http_client->invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params));
if (r) if (r)
{ {
answer = response->m_body; answer = response->m_body;
@ -266,7 +266,7 @@ bool message_transporter::post_request(const std::string &request, std::string &
LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300)); LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300));
THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url); THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url);
} }
m_http_client.disconnect(); // see comment above m_http_client->disconnect(); // see comment above
std::string string_value = get_str_between_tags(answer, "<string>", "</string>"); std::string string_value = get_str_between_tags(answer, "<string>", "</string>");
if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0)) if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0))
{ {

View file

@ -34,9 +34,9 @@
#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_basic.h"
#include "net/http_server_impl_base.h" #include "net/http_server_impl_base.h"
#include "net/http_client.h" #include "net/http_client.h"
#include "net/abstract_http_client.h"
#include "common/util.h" #include "common/util.h"
#include "wipeable_string.h" #include "wipeable_string.h"
#include "serialization/keyvalue_serialization.h"
#include <vector> #include <vector>
namespace mms namespace mms
@ -83,7 +83,7 @@ typedef epee::misc_utils::struct_init<transport_message_t> transport_message;
class message_transporter class message_transporter
{ {
public: public:
message_transporter(); message_transporter(std::unique_ptr<epee::net_utils::http::abstract_http_client> http_client);
void set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login); void set_options(const std::string &bitmessage_address, const epee::wipeable_string &bitmessage_login);
bool send_message(const transport_message &message); bool send_message(const transport_message &message);
bool receive_messages(const std::vector<std::string> &destination_transport_addresses, bool receive_messages(const std::vector<std::string> &destination_transport_addresses,
@ -94,7 +94,7 @@ public:
bool delete_transport_address(const std::string &transport_address); bool delete_transport_address(const std::string &transport_address);
private: private:
epee::net_utils::http::http_simple_client m_http_client; const std::unique_ptr<epee::net_utils::http::abstract_http_client> m_http_client;
std::string m_bitmessage_url; std::string m_bitmessage_url;
epee::wipeable_string m_bitmessage_login; epee::wipeable_string m_bitmessage_login;
std::atomic<bool> m_run; std::atomic<bool> m_run;

View file

@ -51,7 +51,7 @@ namespace tools
static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); 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, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex) NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex)
: m_http_client(http_client) : m_http_client(http_client)
, m_rpc_payment_state(rpc_payment_state) , m_rpc_payment_state(rpc_payment_state)
, m_daemon_rpc_mutex(mutex) , m_daemon_rpc_mutex(mutex)

View file

@ -31,7 +31,7 @@
#include <string> #include <string>
#include <boost/thread/mutex.hpp> #include <boost/thread/mutex.hpp>
#include "include_base_utils.h" #include "include_base_utils.h"
#include "net/http_client.h" #include "net/abstract_http_client.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "wallet_rpc_helpers.h" #include "wallet_rpc_helpers.h"
@ -41,7 +41,7 @@ namespace tools
class NodeRPCProxy class NodeRPCProxy
{ {
public: public:
NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex); NodeRPCProxy(epee::net_utils::http::abstract_http_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex);
void set_client_secret_key(const crypto::secret_key &skey) { m_client_id_secret_key = skey; } void set_client_secret_key(const crypto::secret_key &skey) { m_client_id_secret_key = skey; }
void invalidate(); void invalidate();
@ -72,7 +72,7 @@ private:
private: private:
boost::optional<std::string> get_info(); boost::optional<std::string> get_info();
epee::net_utils::http::http_simple_client &m_http_client; epee::net_utils::http::abstract_http_client &m_http_client;
rpc_payment_state_t &m_rpc_payment_state; rpc_payment_state_t &m_rpc_payment_state;
boost::recursive_mutex &m_daemon_rpc_mutex; boost::recursive_mutex &m_daemon_rpc_mutex;
crypto::secret_key m_client_id_secret_key; crypto::secret_key m_client_id_secret_key;

View file

@ -1122,7 +1122,8 @@ void wallet_device_callback::on_progress(const hw::device_progress& event)
wallet->on_device_progress(event); wallet->on_device_progress(event);
} }
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std::unique_ptr<epee::net_utils::http::http_client_factory> http_client_factory):
m_http_client(std::move(http_client_factory->create())),
m_multisig_rescan_info(NULL), m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL), m_multisig_rescan_k(NULL),
m_upper_transaction_weight_limit(0), m_upper_transaction_weight_limit(0),
@ -1167,7 +1168,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_watch_only(false), m_watch_only(false),
m_multisig(false), m_multisig(false),
m_multisig_threshold(0), m_multisig_threshold(0),
m_node_rpc_proxy(m_http_client, m_rpc_payment_state, m_daemon_rpc_mutex), m_node_rpc_proxy(*m_http_client, m_rpc_payment_state, m_daemon_rpc_mutex),
m_account_public_address{crypto::null_pkey, crypto::null_pkey}, m_account_public_address{crypto::null_pkey, crypto::null_pkey},
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@ -1178,7 +1179,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_light_wallet_balance(0), m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0), m_light_wallet_unlocked_balance(0),
m_original_keys_available(false), m_original_keys_available(false),
m_message_store(), m_message_store(http_client_factory->create()),
m_key_device_type(hw::device::device_type::SOFTWARE), m_key_device_type(hw::device::device_type::SOFTWARE),
m_ring_history_saved(false), m_ring_history_saved(false),
m_ringdb(), m_ringdb(),
@ -1298,8 +1299,8 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
{ {
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(m_http_client.is_connected()) if(m_http_client->is_connected())
m_http_client.disconnect(); m_http_client->disconnect();
const bool changed = m_daemon_address != daemon_address; const bool changed = m_daemon_address != daemon_address;
m_daemon_address = std::move(daemon_address); m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login); m_daemon_login = std::move(daemon_login);
@ -1313,7 +1314,7 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
const std::string address = get_daemon_address(); const std::string address = get_daemon_address();
MINFO("setting daemon to " << address); MINFO("setting daemon to " << address);
bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options)); bool ret = m_http_client->set_server(address, get_daemon_login(), std::move(ssl_options));
if (ret) if (ret)
{ {
CRITICAL_REGION_LOCAL(default_daemon_address_lock); CRITICAL_REGION_LOCAL(default_daemon_address_lock);
@ -1328,7 +1329,12 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::
m_is_initialized = true; m_is_initialized = true;
m_upper_transaction_weight_limit = upper_transaction_weight_limit; m_upper_transaction_weight_limit = upper_transaction_weight_limit;
if (proxy != boost::asio::ip::tcp::endpoint{}) if (proxy != boost::asio::ip::tcp::endpoint{})
m_http_client.set_connector(net::socks::connector{std::move(proxy)}); {
epee::net_utils::http::abstract_http_client* abstract_http_client = m_http_client.get();
epee::net_utils::http::http_simple_client* http_simple_client = dynamic_cast<epee::net_utils::http::http_simple_client*>(abstract_http_client);
CHECK_AND_ASSERT_MES(http_simple_client != nullptr, false, "http_simple_client must be used to set proxy");
http_simple_client->set_connector(net::socks::connector{std::move(proxy)});
}
return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options)); return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -2593,7 +2599,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
"mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" + "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
@ -2622,7 +2628,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.client = get_client_signature(); req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status));
check_rpc_cost("/gethashes.bin", res.credits, pre_call_credits, 1 + res.m_block_ids.size() * COST_PER_BLOCK_HASH); check_rpc_cost("/gethashes.bin", res.credits, pre_call_credits, 1 + res.m_block_ids.size() * COST_PER_BLOCK_HASH);
} }
@ -2907,7 +2913,7 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_transaction_pool_hashes.bin", error::get_tx_pool_error); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_transaction_pool_hashes.bin", error::get_tx_pool_error);
check_rpc_cost("/get_transaction_pool_hashes.bin", res.credits, pre_call_credits, 1 + res.tx_hashes.size() * COST_PER_POOL_HASH); check_rpc_cost("/get_transaction_pool_hashes.bin", res.credits, pre_call_credits, 1 + res.tx_hashes.size() * COST_PER_POOL_HASH);
} }
@ -3052,7 +3058,7 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK) if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX); check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
} }
@ -3538,7 +3544,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout); r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_output_distribution.bin"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_output_distribution.bin");
check_rpc_cost("/get_output_distribution.bin", res.credits, pre_call_credits, COST_PER_OUTPUT_DISTRIBUTION_0); check_rpc_cost("/get_output_distribution.bin", res.credits, pre_call_credits, COST_PER_OUTPUT_DISTRIBUTION_0);
} }
@ -3696,6 +3702,30 @@ void wallet2::clear_soft(bool keep_key_images)
* \return Whether it was successful. * \return Whether it was successful.
*/ */
bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only) bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only)
{
boost::optional<wallet2::keys_file_data> keys_file_data = get_keys_file_data(password, watch_only);
CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data");
std::string tmp_file_name = keys_file_name + ".new";
std::string buf;
bool r = ::serialization::dump_binary(keys_file_data.get(), buf);
r = r && save_to_file(tmp_file_name, buf);
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
unlock_keys_file();
std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
lock_keys_file();
if (e) {
boost::filesystem::remove(tmp_file_name);
LOG_ERROR("failed to update wallet keys file " << keys_file_name);
return false;
}
return true;
}
//----------------------------------------------------------------------------------------------------
boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only)
{ {
std::string account_data; std::string account_data;
std::string multisig_signers; std::string multisig_signers;
@ -3717,8 +3747,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account.encrypt_keys(key); account.encrypt_keys(key);
bool r = epee::serialization::store_t_to_binary(account, account_data); bool r = epee::serialization::store_t_to_binary(account, account_data);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys"); CHECK_AND_ASSERT_MES(r, boost::none, "failed to serialize wallet keys");
wallet2::keys_file_data keys_file_data = {}; boost::optional<wallet2::keys_file_data> keys_file_data = (wallet2::keys_file_data) {};
// Create a JSON object with "key_data" and "seed_language" as keys. // Create a JSON object with "key_data" and "seed_language" as keys.
rapidjson::Document json; rapidjson::Document json;
@ -3749,12 +3779,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
if (m_multisig) if (m_multisig)
{ {
bool r = ::serialization::dump_binary(m_multisig_signers, multisig_signers); bool r = ::serialization::dump_binary(m_multisig_signers, multisig_signers);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig signers"); CHECK_AND_ASSERT_MES(r, boost::none, "failed to serialize wallet multisig signers");
value.SetString(multisig_signers.c_str(), multisig_signers.length()); value.SetString(multisig_signers.c_str(), multisig_signers.length());
json.AddMember("multisig_signers", value, json.GetAllocator()); json.AddMember("multisig_signers", value, json.GetAllocator());
r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations); r = ::serialization::dump_binary(m_multisig_derivations, multisig_derivations);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet multisig derivations"); CHECK_AND_ASSERT_MES(r, boost::none, "failed to serialize wallet multisig derivations");
value.SetString(multisig_derivations.c_str(), multisig_derivations.length()); value.SetString(multisig_derivations.c_str(), multisig_derivations.length());
json.AddMember("multisig_derivations", value, json.GetAllocator()); json.AddMember("multisig_derivations", value, json.GetAllocator());
@ -3897,27 +3927,10 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
// Encrypt the entire JSON object. // Encrypt the entire JSON object.
std::string cipher; std::string cipher;
cipher.resize(account_data.size()); cipher.resize(account_data.size());
keys_file_data.iv = crypto::rand<crypto::chacha_iv>(); keys_file_data.get().iv = crypto::rand<crypto::chacha_iv>();
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.get().iv, &cipher[0]);
keys_file_data.account_data = cipher; keys_file_data.get().account_data = cipher;
return keys_file_data;
std::string tmp_file_name = keys_file_name + ".new";
std::string buf;
r = ::serialization::dump_binary(keys_file_data, buf);
r = r && save_to_file(tmp_file_name, buf);
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
unlock_keys_file();
std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
lock_keys_file();
if (e) {
boost::filesystem::remove(tmp_file_name);
LOG_ERROR("failed to update wallet keys file " << keys_file_name);
return false;
}
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::setup_keys(const epee::wipeable_string &password) void wallet2::setup_keys(const epee::wipeable_string &password)
@ -3957,16 +3970,51 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_
*/ */
bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password) bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
{ {
rapidjson::Document json; std::string keys_file_buf;
wallet2::keys_file_data keys_file_data; bool r = load_from_file(keys_file_name, keys_file_buf);
std::string buf;
bool encrypted_secret_keys = false;
bool r = load_from_file(keys_file_name, buf);
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
// Load keys from buffer
boost::optional<crypto::chacha_key> keys_to_encrypt;
try {
r = wallet2::load_keys_buf(keys_file_buf, password, keys_to_encrypt);
} catch (const std::exception& e) {
std::size_t found = string(e.what()).find("failed to deserialize keys buffer");
THROW_WALLET_EXCEPTION_IF(found != std::string::npos, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
throw e;
}
// Rewrite with encrypted keys if unencrypted, ignore errors
if (r && keys_to_encrypt != boost::none)
{
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
encrypt_keys(keys_to_encrypt.get());
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
if (!saved_ret)
{
// just moan a bit, but not fatal
MERROR("Error saving keys file with encrypted keys, not fatal");
}
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(keys_to_encrypt.get());
m_keys_file_locker.reset();
}
return r;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password) {
boost::optional<crypto::chacha_key> keys_to_encrypt;
return wallet2::load_keys_buf(keys_buf, password, keys_to_encrypt);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional<crypto::chacha_key>& keys_to_encrypt) {
// Decrypt the contents // Decrypt the contents
r = ::serialization::parse_binary(buf, keys_file_data); rapidjson::Document json;
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); wallet2::keys_file_data keys_file_data;
bool encrypted_secret_keys = false;
bool r = ::serialization::parse_binary(keys_buf, keys_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize keys buffer");
crypto::chacha_key key; crypto::chacha_key key;
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string account_data; std::string account_data;
@ -4250,8 +4298,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
} }
else else
{ {
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password"); THROW_WALLET_EXCEPTION(error::wallet_internal_error, "invalid password");
return false; return false;
} }
r = epee::serialization::load_t_from_binary(m_account, account_data); r = epee::serialization::load_t_from_binary(m_account, account_data);
@ -4285,24 +4333,13 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
} }
else else
{ {
// rewrite with encrypted keys, ignore errors keys_to_encrypt = key;
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
encrypt_keys(key);
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
if (!saved_ret)
{
// just moan a bit, but not fatal
MERROR("Error saving keys file with encrypted keys, not fatal");
}
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
decrypt_keys(key);
m_keys_file_locker.reset();
} }
} }
const cryptonote::account_keys& keys = m_account.get_keys(); const cryptonote::account_keys& keys = m_account.get_keys();
hw::device &hwdev = m_account.get_device(); hw::device &hwdev = m_account.get_device();
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
@ -4921,7 +4958,8 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
// re-encrypt keys // re-encrypt keys
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller(); keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); if (!m_wallet_file.empty())
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
setup_new_blockchain(); setup_new_blockchain();
@ -5061,7 +5099,9 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
++m_multisig_rounds_passed; ++m_multisig_rounds_passed;
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt")); if (!m_wallet_file.empty())
create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
return extra_multisig_info; return extra_multisig_info;
} }
@ -5435,13 +5475,13 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
{ {
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(!m_http_client.is_connected(ssl)) if(!m_http_client->is_connected(ssl))
{ {
m_rpc_version = 0; m_rpc_version = 0;
m_node_rpc_proxy.invalidate(); m_node_rpc_proxy.invalidate();
if (!m_http_client.connect(std::chrono::milliseconds(timeout))) if (!m_http_client->connect(std::chrono::milliseconds(timeout)))
return false; return false;
if(!m_http_client.is_connected(ssl)) if(!m_http_client->is_connected(ssl))
return false; return false;
} }
} }
@ -5469,12 +5509,12 @@ void wallet2::set_offline(bool offline)
{ {
m_offline = offline; m_offline = offline;
m_node_rpc_proxy.set_offline(offline); m_node_rpc_proxy.set_offline(offline);
m_http_client.set_auto_connect(!offline); m_http_client->set_auto_connect(!offline);
if (offline) if (offline)
{ {
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(m_http_client.is_connected()) if(m_http_client->is_connected())
m_http_client.disconnect(); m_http_client->disconnect();
} }
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -5489,48 +5529,63 @@ void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pas
crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds); crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password) void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password, const std::string& keys_buf, const std::string& cache_buf)
{ {
clear(); clear();
prepare_file_names(wallet_); prepare_file_names(wallet_);
boost::system::error_code e; // determine if loading from file system or string buffer
bool exists = boost::filesystem::exists(m_keys_file, e); bool use_fs = !wallet_.empty();
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file); THROW_WALLET_EXCEPTION_IF((use_fs && !keys_buf.empty()) || (!use_fs && keys_buf.empty()), error::file_read_error, "must load keys either from file system or from buffer");\
lock_keys_file();
THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). boost::system::error_code e;
unlock_keys_file(); if (use_fs)
if (!load_keys(m_keys_file, password))
{ {
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file); bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
lock_keys_file();
THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
unlock_keys_file();
if (!load_keys(m_keys_file, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
}
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
lock_keys_file();
}
else if (!load_keys_buf(keys_buf, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer");
} }
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
lock_keys_file();
wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
//keys loaded ok! //keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem //try to load wallet file. but even if we failed, it is not big problem
if(!boost::filesystem::exists(m_wallet_file, e) || e) if (use_fs && (!boost::filesystem::exists(m_wallet_file, e) || e))
{ {
LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain"); LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
m_account_public_address = m_account.get_keys().m_account_address; m_account_public_address = m_account.get_keys().m_account_address;
} }
else else if (use_fs || !cache_buf.empty())
{ {
wallet2::cache_file_data cache_file_data; wallet2::cache_file_data cache_file_data;
std::string buf; std::string cache_file_buf;
bool r = load_from_file(m_wallet_file, buf, std::numeric_limits<size_t>::max()); bool r = true;
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); if (use_fs)
{
load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits<size_t>::max());
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file);
}
// try to read it as an encrypted cache // try to read it as an encrypted cache
try try
{ {
LOG_PRINT_L1("Trying to decrypt cache data"); LOG_PRINT_L1("Trying to decrypt cache data");
r = ::serialization::parse_binary(buf, cache_file_data); r = ::serialization::parse_binary(use_fs ? cache_file_buf : cache_buf, cache_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
std::string cache_data; std::string cache_data;
cache_data.resize(cache_file_data.cache_data.size()); cache_data.resize(cache_file_data.cache_data.size());
@ -5567,7 +5622,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
catch (...) catch (...)
{ {
LOG_PRINT_L0("Failed to open portable binary, trying unportable"); LOG_PRINT_L0("Failed to open portable binary, trying unportable");
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); if (use_fs) boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
std::stringstream iss; std::stringstream iss;
iss.str(""); iss.str("");
iss << cache_data; iss << cache_data;
@ -5582,17 +5637,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted"); LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
try { try {
std::stringstream iss; std::stringstream iss;
iss << buf; iss << cache_file_buf;
boost::archive::portable_binary_iarchive ar(iss); boost::archive::portable_binary_iarchive ar(iss);
ar >> *this; ar >> *this;
} }
catch (...) catch (...)
{ {
LOG_PRINT_L0("Failed to open portable binary, trying unportable"); LOG_PRINT_L0("Failed to open portable binary, trying unportable");
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); if (use_fs) boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
std::stringstream iss; std::stringstream iss;
iss.str(""); iss.str("");
iss << buf; iss << cache_file_buf;
boost::archive::binary_iarchive ar(iss); boost::archive::binary_iarchive ar(iss);
ar >> *this; ar >> *this;
} }
@ -5636,7 +5691,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
try try
{ {
m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file); if (use_fs)
m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -5664,7 +5720,7 @@ void wallet2::trim_hashchain()
req.height = m_blockchain.size() - 1; req.height = m_blockchain.size() - 1;
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, m_http_client, rpc_timeout); r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK) if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("getblockheaderbyheight", res.credits, pre_call_credits, COST_PER_BLOCK_HEADER); check_rpc_cost("getblockheaderbyheight", res.credits, pre_call_credits, COST_PER_BLOCK_HEADER);
} }
@ -5739,18 +5795,10 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
} }
} }
} }
// preparing wallet data
std::stringstream oss;
boost::archive::portable_binary_oarchive ar(oss);
ar << *this;
wallet2::cache_file_data cache_file_data = {}; // get wallet cache data
cache_file_data.cache_data = oss.str(); boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data(password);
std::string cipher; THROW_WALLET_EXCEPTION_IF(cache_file_data == boost::none, error::wallet_internal_error, "failed to generate wallet cache data");
cipher.resize(cache_file_data.cache_data.size());
cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
cache_file_data.cache_data = cipher;
const std::string new_file = same_file ? m_wallet_file + ".new" : path; const std::string new_file = same_file ? m_wallet_file + ".new" : path;
const std::string old_file = m_wallet_file; const std::string old_file = m_wallet_file;
@ -5801,7 +5849,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
// The price to pay is temporary higher memory consumption for string stream + binary archive // The price to pay is temporary higher memory consumption for string stream + binary archive
std::ostringstream oss; std::ostringstream oss;
binary_archive<true> oar(oss); binary_archive<true> oar(oss);
bool success = ::serialization::serialize(oar, cache_file_data); bool success = ::serialization::serialize(oar, cache_file_data.get());
if (success) { if (success) {
success = save_to_file(new_file, oss.str()); success = save_to_file(new_file, oss.str());
} }
@ -5810,7 +5858,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
std::ofstream ostr; std::ofstream ostr;
ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
binary_archive<true> oar(ostr); binary_archive<true> oar(ostr);
bool success = ::serialization::serialize(oar, cache_file_data); bool success = ::serialization::serialize(oar, cache_file_data.get());
ostr.close(); ostr.close();
THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file); THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
#endif #endif
@ -5826,7 +5874,30 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
// store should only exist if the MMS is really active // store should only exist if the MMS is really active
m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
} }
}
//----------------------------------------------------------------------------------------------------
boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data(const epee::wipeable_string &passwords)
{
trim_hashchain();
try
{
std::stringstream oss;
boost::archive::portable_binary_oarchive ar(oss);
ar << *this;
boost::optional<wallet2::cache_file_data> cache_file_data = (wallet2::cache_file_data) {};
cache_file_data.get().cache_data = oss.str();
std::string cipher;
cipher.resize(cache_file_data.get().cache_data.size());
cache_file_data.get().iv = crypto::rand<crypto::chacha_iv>();
crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]);
cache_file_data.get().cache_data = cipher;
return cache_file_data;
}
catch(...)
{
return boost::none;
}
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::balance(uint32_t index_major, bool strict) const uint64_t wallet2::balance(uint32_t index_major, bool strict) const
@ -6030,7 +6101,7 @@ void wallet2::rescan_spent()
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(daemon_resp.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
"daemon returned wrong response for is_key_image_spent, wrong amounts count = " + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
@ -6359,7 +6430,7 @@ void wallet2::commit_tx(pending_tx& ptx)
oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST"); bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, *m_http_client, rpc_timeout, "POST");
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx");
// MyMonero and OpenMonero use different status strings // MyMonero and OpenMonero use different status strings
THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error); THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
@ -6378,7 +6449,7 @@ void wallet2::commit_tx(pending_tx& ptx)
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
check_rpc_cost("/sendrawtransaction", daemon_send_resp.credits, pre_call_credits, COST_PER_TX_RELAY); check_rpc_cost("/sendrawtransaction", daemon_send_resp.credits, pre_call_credits, COST_PER_TX_RELAY);
} }
@ -7350,7 +7421,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
getbh_req.client = get_client_signature(); getbh_req.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status));
check_rpc_cost("/sendrawtransaction", getbh_res.credits, pre_call_credits, N * COST_PER_BLOCK_HEADER); check_rpc_cost("/sendrawtransaction", getbh_res.credits, pre_call_credits, N * COST_PER_BLOCK_HEADER);
} }
@ -7576,7 +7647,7 @@ bool wallet2::find_and_save_rings(bool force)
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
@ -7724,7 +7795,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, *m_http_client, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock(); m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
@ -7911,7 +7982,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature(); req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status));
check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size()); check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
} }
@ -7937,7 +8008,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature(); req_t.client = get_client_signature();
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 = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, *m_http_client, rpc_timeout * 1000);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status));
uint64_t expected_cost = 0; uint64_t expected_cost = 0;
for (uint64_t amount: req_t.amounts) expected_cost += (amount ? COST_PER_OUTPUT_DISTRIBUTION : COST_PER_OUTPUT_DISTRIBUTION_0); for (uint64_t amount: req_t.amounts) expected_cost += (amount ? COST_PER_OUTPUT_DISTRIBUTION : COST_PER_OUTPUT_DISTRIBUTION_0);
@ -8291,7 +8362,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(daemon_resp.status)); THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(daemon_resp.status));
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
@ -10500,7 +10571,7 @@ uint8_t wallet2::get_current_hard_fork()
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
req_t.version = 0; req_t.version = 0;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, *m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock(); m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, "hard_fork_info"); THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, "hard_fork_info");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "hard_fork_info"); THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "hard_fork_info");
@ -10595,7 +10666,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature(); req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
uint64_t cost = req_t.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size()); uint64_t cost = req_t.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, cost); check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, cost);
@ -10637,7 +10708,7 @@ uint64_t wallet2::get_num_rct_outputs()
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req_t.client = get_client_signature(); req_t.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response"); THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount"); THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
@ -10768,7 +10839,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
req.client = get_client_signature(); req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), 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"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX); check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
@ -10821,7 +10892,7 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits; pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
@ -10874,7 +10945,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits; pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
@ -10938,7 +11009,7 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits; pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
@ -10996,7 +11067,7 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits; pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong txs count = " + "daemon returned wrong response for gettransactions, wrong txs count = " +
@ -11071,7 +11142,7 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
pre_call_credits = m_rpc_payment_state.credits; pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout); r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
"daemon returned wrong response for get_outs.bin, wrong amounts count = " + "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
@ -11173,7 +11244,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), 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"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX); check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
@ -11228,7 +11299,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), 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"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX); check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
@ -11389,7 +11460,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); ok = net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), 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"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX); check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
@ -11686,7 +11757,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
gettx_req.client = get_client_signature(); gettx_req.client = get_client_signature();
bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client); bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, *m_http_client);
THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(), THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
error::wallet_internal_error, "Failed to get transaction from daemon"); error::wallet_internal_error, "Failed to get transaction from daemon");
check_rpc_cost("/gettransactions", gettx_res.credits, pre_call_credits, gettx_res.txs.size() * COST_PER_TX); check_rpc_cost("/gettransactions", gettx_res.credits, pre_call_credits, gettx_res.txs.size() * COST_PER_TX);
@ -11703,7 +11774,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
kispent_req.client = get_client_signature(); kispent_req.client = get_client_signature();
ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout); ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, *m_http_client, rpc_timeout);
THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(), 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"); error::wallet_internal_error, "Failed to get key image spent status from daemon");
check_rpc_cost("/is_key_image_spent", kispent_res.credits, pre_call_credits, kispent_res.spent_status.size() * COST_PER_KEY_IMAGE); check_rpc_cost("/is_key_image_spent", kispent_res.credits, pre_call_credits, kispent_res.spent_status.size() * COST_PER_KEY_IMAGE);
@ -12277,7 +12348,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, daemon_resp, "is_key_image_spent"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, daemon_resp, "is_key_image_spent");
THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
"daemon returned wrong response for is_key_image_spent, wrong amounts count = " + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
@ -12366,7 +12437,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
gettxs_req.client = get_client_signature(); gettxs_req.client = get_client_signature();
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, gettxs_res, "gettransactions"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, gettxs_res, "gettransactions");
THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
"daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size())); "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
@ -13307,7 +13378,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout); r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, *m_http_client, rpc_timeout);
if (r && res.status == CORE_RPC_STATUS_OK) if (r && res.status == CORE_RPC_STATUS_OK)
check_rpc_cost("/getblocks_by_height.bin", res.credits, pre_call_credits, 3 * COST_PER_BLOCK); check_rpc_cost("/getblocks_by_height.bin", res.credits, pre_call_credits, 3 * COST_PER_BLOCK);
} }
@ -13385,7 +13456,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, m_http_client, rpc_timeout); bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_txpool_backlog", error::get_tx_pool_error); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_txpool_backlog", error::get_tx_pool_error);
check_rpc_cost("get_txpool_backlog", res.credits, pre_call_credits, COST_PER_TX_POOL_STATS * res.backlog.size()); check_rpc_cost("get_txpool_backlog", res.credits, pre_call_credits, COST_PER_TX_POOL_STATS * res.backlog.size());
} }
@ -13724,12 +13795,12 @@ void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const c
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_bytes_sent() const uint64_t wallet2::get_bytes_sent() const
{ {
return m_http_client.get_bytes_sent(); return m_http_client->get_bytes_sent();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_bytes_received() const uint64_t wallet2::get_bytes_received() const
{ {
return m_http_client.get_bytes_received(); return m_http_client->get_bytes_received();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only) std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only)
@ -13742,7 +13813,7 @@ std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only)
{ {
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
bool r = epee::net_utils::invoke_http_json("/get_public_nodes", req, res, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json("/get_public_nodes", req, res, *m_http_client, rpc_timeout);
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_public_nodes"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_public_nodes");
} }

View file

@ -269,7 +269,7 @@ private:
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1);
wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr<epee::net_utils::http::http_client_factory> http_client_factory = std::unique_ptr<epee::net_utils::http::http_simple_client_factory>(new epee::net_utils::http::http_simple_client_factory()));
~wallet2(); ~wallet2();
struct multisig_info struct multisig_info
@ -708,7 +708,7 @@ private:
*/ */
void rewrite(const std::string& wallet_name, const epee::wipeable_string& password); void rewrite(const std::string& wallet_name, const epee::wipeable_string& password);
void write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename); void write_watch_only_wallet(const std::string& wallet_name, const epee::wipeable_string& password, std::string &new_keys_filename);
void load(const std::string& wallet, const epee::wipeable_string& password); void load(const std::string& wallet, const epee::wipeable_string& password, const std::string& keys_buf = "", const std::string& cache_buf = "");
void store(); void store();
/*! /*!
* \brief store_to Stores wallet to another file(s), deleting old ones * \brief store_to Stores wallet to another file(s), deleting old ones
@ -716,6 +716,19 @@ private:
* \param password Password to protect new wallet (TODO: probably better save the password in the wallet object?) * \param password Password to protect new wallet (TODO: probably better save the password in the wallet object?)
*/ */
void store_to(const std::string &path, const epee::wipeable_string &password); void store_to(const std::string &path, const epee::wipeable_string &password);
/*!
* \brief get_keys_file_data Get wallet keys data which can be stored to a wallet file.
* \param password Password of the encrypted wallet buffer (TODO: probably better save the password in the wallet object?)
* \param watch_only true to include only view key, false to include both spend and view keys
* \return Encrypted wallet keys data which can be stored to a wallet file
*/
boost::optional<wallet2::keys_file_data> get_keys_file_data(const epee::wipeable_string& password, bool watch_only);
/*!
* \brief get_cache_file_data Get wallet cache data which can be stored to a wallet file.
* \param password Password to protect the wallet cache data (TODO: probably better save the password in the wallet object?)
* \return Encrypted wallet cache data which can be stored to a wallet file
*/
boost::optional<wallet2::cache_file_data> get_cache_file_data(const epee::wipeable_string& password);
std::string path() const; std::string path() const;
@ -1319,25 +1332,25 @@ private:
crypto::public_key get_multisig_signing_public_key(const crypto::secret_key &skey) const; crypto::public_key get_multisig_signing_public_key(const crypto::secret_key &skey) const;
template<class t_request, class t_response> template<class t_request, class t_response>
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") 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 = "POST")
{ {
if (m_offline) return false; if (m_offline) return false;
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
return epee::net_utils::invoke_http_json(uri, req, res, m_http_client, timeout, http_method); return epee::net_utils::invoke_http_json(uri, req, res, *m_http_client, timeout, http_method);
} }
template<class t_request, class t_response> template<class t_request, class t_response>
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") 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 = "POST")
{ {
if (m_offline) return false; if (m_offline) return false;
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
return epee::net_utils::invoke_http_bin(uri, req, res, m_http_client, timeout, http_method); return epee::net_utils::invoke_http_bin(uri, req, res, *m_http_client, timeout, http_method);
} }
template<class t_request, class t_response> template<class t_request, class t_response>
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") 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 = "POST", const std::string& req_id = "0")
{ {
if (m_offline) return false; if (m_offline) return false;
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); boost::lock_guard<boost::recursive_mutex> 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); return epee::net_utils::invoke_http_json_rpc(uri, method_name, req, res, *m_http_client, timeout, http_method, req_id);
} }
bool set_ring_database(const std::string &filename); bool set_ring_database(const std::string &filename);
@ -1403,11 +1416,18 @@ private:
*/ */
bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false);
/*! /*!
* \brief Load wallet information from wallet file. * \brief Load wallet keys information from wallet file.
* \param keys_file_name Name of wallet file * \param keys_file_name Name of wallet file
* \param password Password of wallet file * \param password Password of wallet file
*/ */
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password); bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
/*!
* \brief Load wallet keys information from a string buffer.
* \param keys_buf Keys buffer to load
* \param password Password of keys buffer
*/
bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password);
bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional<crypto::chacha_key>& keys_to_encrypt);
void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
bool should_skip_block(const cryptonote::block &b, uint64_t height) const; bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
@ -1502,7 +1522,7 @@ private:
std::string m_wallet_file; std::string m_wallet_file;
std::string m_keys_file; std::string m_keys_file;
std::string m_mms_file; std::string m_mms_file;
epee::net_utils::http::http_simple_client m_http_client; const std::unique_ptr<epee::net_utils::http::abstract_http_client> m_http_client;
hashchain m_blockchain; hashchain m_blockchain;
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs; std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;

View file

@ -85,7 +85,7 @@ bool wallet2::make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credit
uint64_t pre_call_credits = m_rpc_payment_state.credits; uint64_t pre_call_credits = m_rpc_payment_state.credits;
req.client = get_client_signature(); req.client = get_client_signature();
epee::json_rpc::error error; epee::json_rpc::error error;
bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_submit_nonce", req, res, error, m_http_client, rpc_timeout); bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_submit_nonce", req, res, error, *m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock(); m_daemon_rpc_mutex.unlock();
THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, error, res, "rpc_access_submit_nonce"); THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, error, res, "rpc_access_submit_nonce");
THROW_WALLET_EXCEPTION_IF(res.credits < pre_call_credits, error::wallet_internal_error, "RPC payment did not increase balance"); THROW_WALLET_EXCEPTION_IF(res.credits < pre_call_credits, error::wallet_internal_error, "RPC payment did not increase balance");

View file

@ -29,6 +29,7 @@
#include "include_base_utils.h" #include "include_base_utils.h"
#include "file_io_utils.h" #include "file_io_utils.h"
#include "net/http_client.h" #include "net/http_client.h"
#include "net/net_ssl.h"
#include "fuzzer.h" #include "fuzzer.h"
class dummy_client class dummy_client
@ -46,6 +47,10 @@ public:
data.clear(); data.clear();
return true; return true;
} }
void set_ssl(epee::net_utils::ssl_options_t ssl_options) { }
bool is_connected(bool *ssl = NULL) { return true; }
uint64_t get_bytes_sent() const { return 1; }
uint64_t get_bytes_received() const { return 1; }
void set_test_data(const std::string &s) { data = s; } void set_test_data(const std::string &s) { data = s; }