aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/CMakeLists.txt1
-rw-r--r--src/wallet/api/wallet.cpp4
-rw-r--r--src/wallet/wallet2.cpp404
-rw-r--r--src/wallet/wallet2.h57
-rw-r--r--src/wallet/wallet_light_rpc.h320
-rw-r--r--src/wallet/wallet_rpc_server.cpp273
-rw-r--r--src/wallet/wallet_rpc_server.h8
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h68
8 files changed, 902 insertions, 233 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index def23aff0..d0fc21f51 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -127,6 +127,7 @@ if (BUILD_GUI_DEPS)
ringct_basic
checkpoints
version
+ net
device_trezor)
foreach(lib ${libs_to_merge})
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 82986ba2d..c1303c225 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -938,7 +938,7 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_
{
try
{
- cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response;
+ tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response;
if(!m_wallet->light_wallet_import_wallet_request(response)){
setStatusError(tr("Failed to send import wallet request"));
return false;
@@ -2173,7 +2173,7 @@ void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending)
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
- if (!m_wallet->init(daemon_address, m_daemon_login, tcp::endpoint{}, upper_transaction_size_limit))
+ if (!m_wallet->init(daemon_address, m_daemon_login, boost::asio::ip::tcp::endpoint{}, upper_transaction_size_limit))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9ba5f9946..2f7c63486 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -243,9 +243,10 @@ struct options {
const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"};
const command_line::arg_descriptor<std::string> daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""};
const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
- const command_line::arg_descriptor<std::vector<std::string>> daemon_ssl_allowed_certificates = {"daemon-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers")};
+ const command_line::arg_descriptor<std::string> daemon_ssl_ca_certificates = {"daemon-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")};
const command_line::arg_descriptor<std::vector<std::string>> daemon_ssl_allowed_fingerprints = {"daemon-ssl-allowed-fingerprints", tools::wallet2::tr("List of valid fingerprints of allowed RPC servers")};
const command_line::arg_descriptor<bool> daemon_ssl_allow_any_cert = {"daemon-ssl-allow-any-cert", tools::wallet2::tr("Allow any SSL certificate from the daemon"), false};
+ const command_line::arg_descriptor<bool> daemon_ssl_allow_chained = {"daemon-ssl-allow-chained", tools::wallet2::tr("Allow user (via --daemon-ssl-ca-certificates) chain certificates"), false};
const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false};
const command_line::arg_descriptor<std::string, false, true, 2> shared_ringdb_dir = {
@@ -314,6 +315,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
const uint64_t kdf_rounds = command_line::get_arg(vm, opts.kdf_rounds);
THROW_WALLET_EXCEPTION_IF(kdf_rounds == 0, tools::error::wallet_internal_error, "KDF rounds must not be 0");
+ const bool use_proxy = command_line::has_arg(vm, opts.proxy);
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
@@ -321,13 +323,37 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path);
auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key);
auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate);
- auto daemon_ssl_allowed_certificates = command_line::get_arg(vm, opts.daemon_ssl_allowed_certificates);
+ auto daemon_ssl_ca_file = command_line::get_arg(vm, opts.daemon_ssl_ca_certificates);
auto daemon_ssl_allowed_fingerprints = command_line::get_arg(vm, opts.daemon_ssl_allowed_fingerprints);
auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert);
auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl);
- epee::net_utils::ssl_support_t ssl_support;
- THROW_WALLET_EXCEPTION_IF(!epee::net_utils::ssl_support_from_string(ssl_support, daemon_ssl), tools::error::wallet_internal_error,
- tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name));
+
+ // user specified CA file or fingeprints implies enabled SSL by default
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+ if (command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert))
+ ssl_options.verification = epee::net_utils::ssl_verification_t::none;
+ else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
+ {
+ std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
+ std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
+
+ ssl_options = epee::net_utils::ssl_options_t{
+ std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file)
+ };
+
+ if (command_line::get_arg(vm, opts.daemon_ssl_allow_chained))
+ ssl_options.verification = epee::net_utils::ssl_verification_t::user_ca;
+ }
+
+ if (ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, opts.daemon_ssl))
+ {
+ THROW_WALLET_EXCEPTION_IF(!epee::net_utils::ssl_support_from_string(ssl_options.support, daemon_ssl), tools::error::wallet_internal_error,
+ tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name));
+ }
+
+ ssl_options.auth = epee::net_utils::ssl_authentication_t{
+ std::move(daemon_ssl_private_key), std::move(daemon_ssl_certificate)
+ };
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -357,22 +383,24 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
- boost::asio::ip::tcp::endpoint proxy{};
- if (command_line::has_arg(vm, opts.proxy))
{
- namespace ip = boost::asio::ip;
const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
- // onion and i2p addresses contain information about the server cert
- // which both authenticates and encrypts
- const bool unencrypted_proxy =
- !real_daemon.ends_with(".onion") && !real_daemon.ends_with(".i2p") &&
- daemon_ssl_allowed_certificates.empty() && daemon_ssl_allowed_fingerprints.empty();
+ const bool verification_required =
+ ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy;
+
THROW_WALLET_EXCEPTION_IF(
- unencrypted_proxy,
+ verification_required && !ssl_options.has_strong_verification(real_daemon),
tools::error::wallet_internal_error,
- std::string{"Use of --"} + opts.proxy.name + " requires --" + opts.daemon_ssl_allowed_certificates.name + " or --" + opts.daemon_ssl_allowed_fingerprints.name + " or use of a .onion/.i2p domain"
+ tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
+ opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
);
+ }
+
+ boost::asio::ip::tcp::endpoint proxy{};
+ if (use_proxy)
+ {
+ namespace ip = boost::asio::ip;
const auto proxy_address = command_line::get_arg(vm, opts.proxy);
@@ -416,22 +444,8 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
catch (const std::exception &e) { }
}
- std::list<std::string> ssl_allowed_certificates;
- for (const std::string &path: daemon_ssl_allowed_certificates)
- {
- ssl_allowed_certificates.push_back({});
- if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back()))
- {
- MERROR("Failed to load certificate: " << path);
- ssl_allowed_certificates.back() = std::string();
- }
- }
-
- std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
- std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
-
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
- wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, ssl_allowed_fingerprints, daemon_ssl_allow_any_cert);
+ wallet->init(std::move(daemon_address), std::move(login), std::move(proxy), 0, *trusted_daemon, std::move(ssl_options));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->get_message_store().set_options(vm);
@@ -512,7 +526,7 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
- const bool recover = field_scan_from_height_found;
+ const bool recover = true;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
@@ -622,6 +636,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
wallet.reset(make_basic(vm, unattended, opts, password_prompter).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
+ if (!old_language.empty())
+ wallet->set_seed_language(old_language);
try
{
@@ -1030,6 +1046,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_segregation_height(0),
m_ignore_fractional_outputs(true),
m_track_uses(false),
+ m_setup_background_mining(BackgroundMiningMaybe),
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
@@ -1098,9 +1115,10 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.daemon_ssl);
command_line::add_arg(desc_params, opts.daemon_ssl_private_key);
command_line::add_arg(desc_params, opts.daemon_ssl_certificate);
- command_line::add_arg(desc_params, opts.daemon_ssl_allowed_certificates);
+ command_line::add_arg(desc_params, opts.daemon_ssl_ca_certificates);
command_line::add_arg(desc_params, opts.daemon_ssl_allowed_fingerprints);
command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert);
+ command_line::add_arg(desc_params, opts.daemon_ssl_allow_chained);
command_line::add_arg(desc_params, opts.testnet);
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
@@ -1152,21 +1170,26 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, const std::vector<std::vector<uint8_t>> &allowed_fingerprints, bool allow_any_cert)
+bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
{
- m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
- m_is_initialized = true;
- m_upper_transaction_weight_limit = upper_transaction_weight_limit;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon;
+
+ MINFO("setting daemon to " << get_daemon_address());
+ return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
+{
+ m_checkpoints.init_default_checkpoints(m_nettype);
+ m_is_initialized = true;
+ m_upper_transaction_weight_limit = upper_transaction_weight_limit;
if (proxy != boost::asio::ip::tcp::endpoint{})
m_http_client.set_connector(net::socks::connector{std::move(proxy)});
-
- // When switching from light wallet to full wallet, we need to reset the height we got from lw node.
- return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allowed_fingerprints, allow_any_cert);
+ return set_daemon(daemon_address, daemon_login, trusted_daemon, std::move(ssl_options));
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_deterministic() const
@@ -1439,6 +1462,58 @@ void wallet2::set_unspent(size_t idx)
td.m_spent_height = 0;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::freeze(size_t idx)
+{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
+ transfer_details &td = m_transfers[idx];
+ td.m_frozen = true;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::thaw(size_t idx)
+{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
+ transfer_details &td = m_transfers[idx];
+ td.m_frozen = false;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::frozen(size_t idx) const
+{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
+ const transfer_details &td = m_transfers[idx];
+ return td.m_frozen;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::freeze(const crypto::key_image &ki)
+{
+ freeze(get_transfer_details(ki));
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::thaw(const crypto::key_image &ki)
+{
+ thaw(get_transfer_details(ki));
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::frozen(const crypto::key_image &ki) const
+{
+ return frozen(get_transfer_details(ki));
+}
+//----------------------------------------------------------------------------------------------------
+size_t wallet2::get_transfer_details(const crypto::key_image &ki) const
+{
+ for (size_t idx = 0; idx < m_transfers.size(); ++idx)
+ {
+ const transfer_details &td = m_transfers[idx];
+ if (td.m_key_image_known && td.m_key_image == ki)
+ return idx;
+ }
+ CHECK_AND_ASSERT_THROW_MES(false, "Key image not found");
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::frozen(const transfer_details &td) const
+{
+ return td.m_frozen;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const
{
hw::device &hwdev = m_account.get_device();
@@ -1856,6 +1931,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_mask = rct::identity();
td.m_rct = false;
}
+ td.m_frozen = false;
set_unspent(m_transfers.size()-1);
if (td.m_key_image_known)
m_key_images[td.m_key_image] = m_transfers.size()-1;
@@ -2212,6 +2288,12 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
add_rings(tx);
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::should_skip_block(const cryptonote::block &b, uint64_t height) const
+{
+ // seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
+ return !(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height);
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::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)
{
THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != parsed_block.o_indices.indices.size(), error::wallet_internal_error,
@@ -2221,7 +2303,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
//handle transactions from new block
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
- if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
+ if (!should_skip_block(b, height))
{
TIME_MEASURE_START(miner_tx_handle_time);
if (m_refresh_type != RefreshNoCoinbase)
@@ -2285,9 +2367,7 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids, uint64_t gra
//----------------------------------------------------------------------------------------------------
void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
{
- error = !cryptonote::parse_and_validate_block_from_blob(blob, bl);
- if (!error)
- bl_id = get_block_hash(bl);
+ error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id);
}
//----------------------------------------------------------------------------------------------------
void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
@@ -2353,6 +2433,11 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
{
THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
+ if (should_skip_block(parsed_blocks[i].block, start_height + i))
+ {
+ txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
+ continue;
+ }
if (m_refresh_type != RefreshNoCoinbase)
tpool.submit(&waiter, [&, i, txidx](){ cache_tx_data(parsed_blocks[i].block.miner_tx, get_transaction_hash(parsed_blocks[i].block.miner_tx), tx_cache_data[txidx]); });
++txidx;
@@ -2381,6 +2466,8 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
for (size_t i = 0; i < tx_cache_data.size(); ++i)
{
+ if (tx_cache_data[i].empty())
+ continue;
tpool.submit(&waiter, [&hwdev, &gender, &tx_cache_data, i]() {
auto &slot = tx_cache_data[i];
boost::unique_lock<hw::device> hwdev_lock(hwdev);
@@ -2399,6 +2486,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
if (o.target.type() == typeid(cryptonote::txout_to_key))
{
std::vector<crypto::key_derivation> additional_derivations;
+ additional_derivations.reserve(tx_cache_data[txidx].additional.size());
for (const auto &iod: tx_cache_data[txidx].additional)
additional_derivations.push_back(iod.derivation);
const auto &key = boost::get<txout_to_key>(o.target).key;
@@ -2416,6 +2504,12 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
txidx = 0;
for (size_t i = 0; i < blocks.size(); ++i)
{
+ if (should_skip_block(parsed_blocks[i].block, start_height + i))
+ {
+ txidx += 1 + parsed_blocks[i].block.tx_hashes.size();
+ continue;
+ }
+
if (m_refresh_type != RefreshType::RefreshNoCoinbase)
{
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
@@ -2453,7 +2547,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
" (height " + std::to_string(start_height) + "), local block id at this height: " +
string_tools::pod_to_hex(m_blockchain[current_index]));
- detach_blockchain(current_index);
+ detach_blockchain(current_index, output_tracker_cache);
process_new_blockchain_entry(bl, blocks[i], parsed_blocks[i], bl_id, current_index, tx_cache_data, tx_cache_data_offset, output_tracker_cache);
}
else
@@ -2897,7 +2991,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
// This call is not really needed for other purposes and can be removed if mymonero changes their backend.
- cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response res;
+ tools::COMMAND_RPC_GET_ADDRESS_INFO::response res;
// Get basic info
if(light_wallet_get_address_info(res)) {
@@ -3045,7 +3139,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// if we've got at least 10 blocks to refresh, assume we're starting
// a long refresh, and setup a tracking output cache if we need to
- if (m_track_uses && !output_tracker_cache && next_blocks.size() >= 10)
+ if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10)
output_tracker_cache = create_output_tracker_cache();
// switch to the new blocks from the daemon
@@ -3186,7 +3280,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
return true;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::detach_blockchain(uint64_t height)
+void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
LOG_PRINT_L0("Detaching blockchain on height " << height);
@@ -3203,11 +3297,21 @@ void wallet2::detach_blockchain(uint64_t height)
wallet2::transfer_details &td = m_transfers[i];
if (td.m_spent && td.m_spent_height >= height)
{
- LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image);
+ LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image);
set_unspent(i);
+ thaw(i);
}
}
+ for (transfer_details &td: m_transfers)
+ {
+ while (!td.m_uses.empty() && td.m_uses.back().first >= height)
+ td.m_uses.pop_back();
+ }
+
+ if (output_tracker_cache)
+ output_tracker_cache->clear();
+
auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
size_t i_start = it - m_transfers.begin();
@@ -3449,6 +3553,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_track_uses ? 1 : 0);
json.AddMember("track_uses", value2, json.GetAllocator());
+ value2.SetInt(m_setup_background_mining);
+ json.AddMember("setup_background_mining", value2, json.GetAllocator());
+
value2.SetUint(m_subaddress_lookahead_major);
json.AddMember("subaddress_lookahead_major", value2, json.GetAllocator());
@@ -3537,7 +3644,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_
decrypt_keys(original_password);
setup_keys(new_password);
rewrite(filename, new_password);
- store();
+ if (!filename.empty())
+ store();
}
//----------------------------------------------------------------------------------------------------
/*!
@@ -3577,13 +3685,16 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_multisig_derivations.clear();
m_always_confirm_transfers = false;
m_print_ring_members = false;
+ m_store_tx_info = true;
m_default_mixin = 0;
m_default_priority = 0;
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
+ m_refresh_from_block_height = 0;
m_confirm_missing_payment_id = true;
m_confirm_non_default_ring_size = true;
m_ask_password = AskPasswordToDecrypt;
+ cryptonote::set_default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT);
m_min_output_count = 0;
m_min_output_value = 0;
m_merge_destinations = false;
@@ -3596,6 +3707,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = 0;
m_ignore_fractional_outputs = true;
m_track_uses = false;
+ m_setup_background_mining = BackgroundMiningMaybe;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_original_keys_available = false;
@@ -3750,6 +3862,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = field_ignore_fractional_outputs;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
m_track_uses = field_track_uses;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
+ m_setup_background_mining = field_setup_background_mining;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
@@ -4051,6 +4165,17 @@ bool wallet2::query_device(hw::device::device_type& device_type, const std::stri
return true;
}
+void wallet2::init_type(hw::device::device_type device_type)
+{
+ m_account_public_address = m_account.get_keys().m_account_address;
+ m_watch_only = false;
+ m_multisig = false;
+ m_multisig_threshold = 0;
+ m_multisig_signers.clear();
+ m_original_keys_available = false;
+ m_key_device_type = device_type;
+}
+
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -4120,18 +4245,15 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
m_account.finalize_multisig(spend_public_key);
- m_account_public_address = m_account.get_keys().m_account_address;
- m_watch_only = false;
+ // Not possible to restore a multisig wallet that is able to activate the MMS
+ // (because the original keys are not (yet) part of the restore info), so
+ // keep m_original_keys_available to false
+ init_type(hw::device::device_type::SOFTWARE);
m_multisig = true;
m_multisig_threshold = threshold;
m_multisig_signers = multisig_signers;
- m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
- // Not possible to restore a multisig wallet that is able to activate the MMS
- // (because the original keys are not (yet) part of the restore info)
- m_original_keys_available = false;
-
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
setup_new_blockchain();
@@ -4164,13 +4286,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
crypto::secret_key retval = m_account.generate(recovery_param, recover, two_random);
- m_account_public_address = m_account.get_keys().m_account_address;
- m_watch_only = false;
- m_multisig = false;
- m_multisig_threshold = 0;
- m_multisig_signers.clear();
- m_original_keys_available = false;
- m_key_device_type = hw::device::device_type::SOFTWARE;
+ init_type(hw::device::device_type::SOFTWARE);
setup_keys(password);
// calculate a starting refresh height
@@ -4253,13 +4369,9 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
}
m_account.create_from_viewkey(account_public_address, viewkey);
- m_account_public_address = account_public_address;
+ init_type(hw::device::device_type::SOFTWARE);
m_watch_only = true;
- m_multisig = false;
- m_multisig_threshold = 0;
- m_multisig_signers.clear();
- m_original_keys_available = false;
- m_key_device_type = hw::device::device_type::SOFTWARE;
+ m_account_public_address = account_public_address;
setup_keys(password);
create_keys_file(wallet_, true, password, m_nettype != MAINNET || create_address_file);
@@ -4294,13 +4406,8 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
}
m_account.create_from_keys(account_public_address, spendkey, viewkey);
+ init_type(hw::device::device_type::SOFTWARE);
m_account_public_address = account_public_address;
- m_watch_only = false;
- m_multisig = false;
- m_multisig_threshold = 0;
- m_multisig_signers.clear();
- m_original_keys_available = false;
- m_key_device_type = hw::device::device_type::SOFTWARE;
setup_keys(password);
create_keys_file(wallet_, false, password, create_address_file);
@@ -4335,13 +4442,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
hwdev.set_callback(get_device_callback());
m_account.create_from_device(hwdev);
- m_key_device_type = m_account.get_device().get_type();
- m_account_public_address = m_account.get_keys().m_account_address;
- m_watch_only = false;
- m_multisig = false;
- m_multisig_threshold = 0;
- m_multisig_signers.clear();
- m_original_keys_available = false;
+ init_type(m_account.get_device().get_type());
setup_keys(password);
m_device_name = device_name;
@@ -4473,10 +4574,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
"Failed to create multisig wallet due to bad keys");
memwipe(&spend_skey, sizeof(rct::key));
- m_account_public_address = m_account.get_keys().m_account_address;
- m_watch_only = false;
+ init_type(hw::device::device_type::SOFTWARE);
+ m_original_keys_available = true;
m_multisig = true;
- m_key_device_type = hw::device::device_type::SOFTWARE;
m_multisig_threshold = threshold;
m_multisig_signers = multisig_signers;
++m_multisig_rounds_passed;
@@ -5226,7 +5326,8 @@ std::string wallet2::path() const
//----------------------------------------------------------------------------------------------------
void wallet2::store()
{
- store_to("", epee::wipeable_string());
+ if (!m_wallet_file.empty())
+ store_to("", epee::wipeable_string());
}
//----------------------------------------------------------------------------------------------------
void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
@@ -5378,7 +5479,7 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
std::map<uint32_t, uint64_t> amount_per_subaddr;
for (const auto& td: m_transfers)
{
- if (td.m_subaddr_index.major == index_major && !td.m_spent)
+ if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
{
auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
if (found == amount_per_subaddr.end())
@@ -5407,7 +5508,7 @@ std::map<uint32_t, uint64_t> wallet2::unlocked_balance_per_subaddress(uint32_t i
std::map<uint32_t, uint64_t> amount_per_subaddr;
for(const transfer_details& td: m_transfers)
{
- if(td.m_subaddr_index.major == index_major && !td.m_spent && is_transfer_unlocked(td))
+ if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen && is_transfer_unlocked(td))
{
auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
if (found == amount_per_subaddr.end())
@@ -8301,7 +8402,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
picks.push_back(i);
@@ -8316,13 +8417,13 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount()));
for (size_t j = i + 1; j < m_transfers.size(); ++j)
{
const transfer_details& td2 = m_transfers[j];
- if (!td2.m_spent && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
+ if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
{
// update our picks if those outputs are less related than any we
// already found. If the same, don't update, and oldest suitable outputs
@@ -8408,8 +8509,8 @@ bool wallet2::light_wallet_login(bool &new_address)
{
MDEBUG("Light wallet login request");
m_light_wallet_connected = false;
- cryptonote::COMMAND_RPC_LOGIN::request request;
- cryptonote::COMMAND_RPC_LOGIN::response response;
+ tools::COMMAND_RPC_LOGIN::request request;
+ tools::COMMAND_RPC_LOGIN::response response;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
// Always create account if it doesn't exist.
@@ -8433,10 +8534,10 @@ bool wallet2::light_wallet_login(bool &new_address)
return m_light_wallet_connected;
}
-bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
+bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response)
{
MDEBUG("Light wallet import wallet request");
- cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
+ tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq;
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
@@ -8452,8 +8553,8 @@ void wallet2::light_wallet_get_unspent_outs()
{
MDEBUG("Getting unspent outs");
- cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
- cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
+ tools::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq;
+ tools::COMMAND_RPC_GET_UNSPENT_OUTS::response ores;
oreq.amount = "0";
oreq.address = get_account().get_public_address_str(m_nettype);
@@ -8549,6 +8650,7 @@ void wallet2::light_wallet_get_unspent_outs()
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
td.m_spent = spent;
+ td.m_frozen = false;
tx_out txout;
txout.target = txout_to_key(public_key);
@@ -8601,11 +8703,11 @@ void wallet2::light_wallet_get_unspent_outs()
}
}
-bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
+bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response)
{
MTRACE(__FUNCTION__);
- cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request;
+ tools::COMMAND_RPC_GET_ADDRESS_INFO::request request;
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
@@ -8621,8 +8723,8 @@ void wallet2::light_wallet_get_address_txs()
{
MDEBUG("Refreshing light wallet");
- cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
- cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
+ tools::COMMAND_RPC_GET_ADDRESS_TXS::request ireq;
+ tools::COMMAND_RPC_GET_ADDRESS_TXS::response ires;
ireq.address = get_account().get_public_address_str(m_nettype);
ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
@@ -9023,7 +9125,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
continue;
}
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
const uint32_t index_minor = td.m_subaddr_index.minor;
auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
@@ -9455,7 +9557,7 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, s
THROW_WALLET_EXCEPTION_IF(ptx.change_dts.addr != ptx_vector[0].change_dts.addr, error::wallet_internal_error,
"Change goes to several different addresses");
const auto it = m_subaddresses.find(ptx_vector[0].change_dts.addr.m_spend_public_key);
- THROW_WALLET_EXCEPTION_IF(it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours");
+ THROW_WALLET_EXCEPTION_IF(change > 0 && it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours");
required[ptx_vector[0].change_dts.addr].first += change;
required[ptx_vector[0].change_dts.addr].second = ptx_vector[0].change_dts.is_subaddress;
@@ -9503,7 +9605,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
+ if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
fund_found = true;
if (below == 0 || td.amount() < below)
@@ -9551,7 +9653,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
+ if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i);
@@ -9771,8 +9873,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
}
uint64_t a = 0;
- for (size_t idx: unused_transfers_indices) a += m_transfers[idx].amount();
- for (size_t idx: unused_dust_indices) a += m_transfers[idx].amount();
+ for (const TX &tx: txes)
+ {
+ for (size_t idx: tx.selected_transfers)
+ {
+ a += m_transfers[idx].amount();
+ }
+ a -= tx.ptx.fee;
+ }
std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress));
THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check");
@@ -9856,7 +9964,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
- bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
+ bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@@ -9883,6 +9991,8 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
{
if (i->m_spent)
continue;
+ if (i->m_frozen)
+ continue;
if (i->m_key_image_partial)
continue;
if (!is_transfer_unlocked(*i))
@@ -9898,7 +10008,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
std::set<uint64_t> set;
for (const auto &td: m_transfers)
{
- if (!td.m_spent)
+ if (!td.m_spent && !td.m_frozen)
set.insert(td.is_rct() ? 0 : td.amount());
}
std::vector<uint64_t> vector;
@@ -10024,7 +10134,7 @@ void wallet2::discard_unmixable_outputs()
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
for (size_t idx : unmixable_outputs)
{
- m_transfers[idx].m_spent = true;
+ freeze(idx);
}
}
@@ -10411,7 +10521,7 @@ void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &t
void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const
{
received = 0;
- hw::device &hwdev = m_account.get_device();
+
for (size_t n = 0; n < tx.vout.size(); ++n)
{
const cryptonote::txout_to_key* const out_key = boost::get<cryptonote::txout_to_key>(std::addressof(tx.vout[n].target));
@@ -10419,13 +10529,13 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
continue;
crypto::public_key derived_out_key;
- bool r = hwdev.derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ bool r = crypto::derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
bool found = out_key->key == derived_out_key;
crypto::key_derivation found_derivation = derivation;
if (!found && !additional_derivations.empty())
{
- r = hwdev.derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ r = crypto::derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
found = out_key->key == derived_out_key;
found_derivation = additional_derivations[n];
@@ -10441,9 +10551,9 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
else
{
crypto::secret_key scalar1;
- hwdev.derivation_to_scalar(found_derivation, n, scalar1);
+ crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
- hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
@@ -10497,13 +10607,13 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
check_tx_key_helper(tx, derivation, additional_derivations, address, received);
in_pool = res.txs.front().in_pool;
- confirmations = (uint64_t)-1;
+ confirmations = 0;
if (!in_pool)
{
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
if (err.empty())
- confirmations = bc_height - (res.txs.front().block_height + 1);
+ confirmations = bc_height - res.txs.front().block_height;
}
}
@@ -10554,6 +10664,8 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const
{
+ hw::device &hwdev = m_account.get_device();
+ rct::key aP;
// determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound)
const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0;
@@ -10572,30 +10684,34 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
shared_secret.resize(num_sigs);
sig.resize(num_sigs);
- shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
+ hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key));
+ shared_secret[0] = rct::rct2pk(aP);
crypto::public_key tx_pub_key;
if (is_subaddress)
{
- tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key)));
- crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
+ hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(tx_key));
+ tx_pub_key = rct2pk(aP);
+ hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], tx_key, sig[0]);
}
else
{
- crypto::secret_key_to_public_key(tx_key, tx_pub_key);
- crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
+ hwdev.secret_key_to_public_key(tx_key, tx_pub_key);
+ hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], tx_key, sig[0]);
}
for (size_t i = 1; i < num_sigs; ++i)
{
- shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
+ hwdev.scalarmultKey(aP, rct::pk2rct(address.m_view_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
+ shared_secret[i] = rct::rct2pk(aP);
if (is_subaddress)
{
- tx_pub_key = rct2pk(rct::scalarmultKey(rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1])));
- crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ hwdev.scalarmultKey(aP, rct::pk2rct(address.m_spend_public_key), rct::sk2rct(additional_tx_keys[i - 1]));
+ tx_pub_key = rct2pk(aP);
+ hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
}
else
{
- crypto::secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
- crypto::generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
+ hwdev.secret_key_to_public_key(additional_tx_keys[i - 1], tx_pub_key);
+ hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]);
}
}
sig_str = std::string("OutProofV1");
@@ -10611,25 +10727,27 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt
sig.resize(num_sigs);
const crypto::secret_key& a = m_account.get_keys().m_view_secret_key;
- shared_secret[0] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(a)));
+ hwdev.scalarmultKey(aP, rct::pk2rct(tx_pub_key), rct::sk2rct(a));
+ shared_secret[0] = rct2pk(aP);
if (is_subaddress)
{
- crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
+ hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], a, sig[0]);
}
else
{
- crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
+ hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], a, sig[0]);
}
for (size_t i = 1; i < num_sigs; ++i)
{
- shared_secret[i] = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a)));
+ hwdev.scalarmultKey(aP,rct::pk2rct(additional_tx_pub_keys[i - 1]), rct::sk2rct(a));
+ shared_secret[i] = rct2pk(aP);
if (is_subaddress)
{
- crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
+ hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], address.m_spend_public_key, shared_secret[i], a, sig[i]);
}
else
{
- crypto::generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
+ hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]);
}
}
sig_str = std::string("InProofV1");
@@ -10691,13 +10809,13 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
return false;
in_pool = res.txs.front().in_pool;
- confirmations = (uint64_t)-1;
+ confirmations = 0;
if (!in_pool)
{
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
if (err.empty())
- confirmations = bc_height - (res.txs.front().block_height + 1);
+ confirmations = bc_height - res.txs.front().block_height;
}
return true;
@@ -10807,7 +10925,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details &td = m_transfers[i];
- if (!td.m_spent && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
+ if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
selected_transfers.push_back(i);
}
@@ -11554,6 +11672,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
for(size_t i = 0; i < offset; ++i)
{
const transfer_details &td = m_transfers[i];
+ if (td.m_frozen)
+ continue;
uint64_t amount = td.amount();
if (td.m_spent)
spent += amount;
@@ -11565,6 +11685,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
const transfer_details &td = m_transfers[i + offset];
+ if (td.m_frozen)
+ continue;
uint64_t amount = td.amount();
if (td.m_spent)
spent += amount;
@@ -12854,4 +12976,14 @@ void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const c
m_transfers[it->second].m_key_image_known = true;
}
}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_bytes_sent() const
+{
+ return m_http_client.get_bytes_sent();
+}
+//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_bytes_received() const
+{
+ return m_http_client.get_bytes_received();
+}
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 0d13235bd..1c1a0422d 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -62,6 +62,7 @@
#include "common/password.h"
#include "node_rpc_proxy.h"
#include "message_store.h"
+#include "wallet_light_rpc.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -193,6 +194,12 @@ namespace tools
AskPasswordToDecrypt = 2,
};
+ enum BackgroundMiningSetupType {
+ BackgroundMiningMaybe = 0,
+ BackgroundMiningYes = 1,
+ BackgroundMiningNo = 2,
+ };
+
static const char* tr(const char* str);
static bool has_testnet_option(const boost::program_options::variables_map& vm);
@@ -265,6 +272,7 @@ namespace tools
size_t m_internal_output_index;
uint64_t m_global_output_index;
bool m_spent;
+ bool m_frozen;
uint64_t m_spent_height;
crypto::key_image m_key_image; //TODO: key_image stored twice :(
rct::key m_mask;
@@ -290,6 +298,7 @@ namespace tools
FIELD(m_internal_output_index)
FIELD(m_global_output_index)
FIELD(m_spent)
+ FIELD(m_frozen)
FIELD(m_spent_height)
FIELD(m_key_image)
FIELD(m_mask)
@@ -530,6 +539,8 @@ namespace tools
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
std::vector<is_out_data> primary;
std::vector<is_out_data> additional;
+
+ bool empty() const { return tx_extra_fields.empty() && primary.empty() && additional.empty(); }
};
/*!
@@ -684,10 +695,10 @@ namespace tools
boost::asio::ip::tcp::endpoint proxy = {},
uint64_t upper_transaction_weight_limit = 0,
bool trusted_daemon = true,
- epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
- const std::pair<std::string, std::string> &private_key_and_certificate_path = {},
- const std::list<std::string> &allowed_certificates = {}, const std::vector<std::vector<uint8_t>> &allowed_fingerprints = {},
- bool allow_any_cert = false);
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
+ bool set_daemon(std::string daemon_address = "http://localhost:8080",
+ boost::optional<epee::net_utils::http::login> daemon_login = boost::none, bool trusted_daemon = true,
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
@@ -1005,6 +1016,8 @@ namespace tools
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
bool track_uses() const { return m_track_uses; }
void track_uses(bool value) { m_track_uses = value; }
+ BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; }
+ void setup_background_mining(BackgroundMiningSetupType value) { m_setup_background_mining = value; }
const std::string & device_name() const { return m_device_name; }
void device_name(const std::string & device_name) { m_device_name = device_name; }
const std::string & device_derivation_path() const { return m_device_derivation_path; }
@@ -1178,11 +1191,11 @@ namespace tools
// fetch txs and store in m_payments
void light_wallet_get_address_txs();
// get_address_info
- bool light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response);
+ bool light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO::response &response);
// Login. new_address is true if address hasn't been used on lw node before.
bool light_wallet_login(bool &new_address);
// Send an import request to lw node. returns info about import fee, address and payment_id
- bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response);
+ bool light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response);
// get random outputs from light wallet server
void light_wallet_get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
// Parse rct string
@@ -1245,6 +1258,17 @@ namespace tools
bool unblackball_output(const std::pair<uint64_t, uint64_t> &output);
bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const;
+ void freeze(size_t idx);
+ void thaw(size_t idx);
+ bool frozen(size_t idx) const;
+ void freeze(const crypto::key_image &ki);
+ void thaw(const crypto::key_image &ki);
+ bool frozen(const crypto::key_image &ki) const;
+ bool frozen(const transfer_details &td) const;
+
+ uint64_t get_bytes_sent() const;
+ uint64_t get_bytes_received() const;
+
// MMS -------------------------------------------------------------------------------------------------
mms::message_store& get_message_store() { return m_message_store; };
const mms::message_store& get_message_store() const { return m_message_store; };
@@ -1279,8 +1303,9 @@ namespace tools
*/
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, 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;
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 detach_blockchain(uint64_t height);
+ void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
bool clear();
void clear_soft(bool keep_key_images=false);
@@ -1327,6 +1352,7 @@ namespace tools
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
crypto::chacha_key get_ringdb_key();
void setup_keys(const epee::wipeable_string &password);
+ size_t get_transfer_details(const crypto::key_image &ki) const;
void register_devices();
hw::device& lookup_device(const std::string & device_descriptor);
@@ -1344,6 +1370,7 @@ namespace tools
void cache_tx_data(const cryptonote::transaction& tx, const crypto::hash &txid, tx_cache_data &tx_cache_data) const;
std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> create_output_tracker_cache() const;
+ void init_type(hw::device::device_type device_type);
void setup_new_blockchain();
void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file);
@@ -1432,6 +1459,7 @@ namespace tools
uint64_t m_segregation_height;
bool m_ignore_fractional_outputs;
bool m_track_uses;
+ BackgroundMiningSetupType m_setup_background_mining;
bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
@@ -1481,7 +1509,7 @@ namespace tools
};
}
BOOST_CLASS_VERSION(tools::wallet2, 28)
-BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11)
+BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
@@ -1543,6 +1571,10 @@ namespace boost
{
x.m_key_image_request = false;
}
+ if (ver < 12)
+ {
+ x.m_frozen = false;
+ }
}
template <class Archive>
@@ -1631,8 +1663,17 @@ namespace boost
}
a & x.m_key_image_request;
if (ver < 11)
+ {
+ initialize_transfer_details(a, x, ver);
return;
+ }
a & x.m_uses;
+ if (ver < 12)
+ {
+ initialize_transfer_details(a, x, ver);
+ return;
+ }
+ a & x.m_frozen;
}
template <class Archive>
diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h
new file mode 100644
index 000000000..1d35cec33
--- /dev/null
+++ b/src/wallet/wallet_light_rpc.h
@@ -0,0 +1,320 @@
+// Copyright (c) 2014-2018, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. 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.
+//
+// 3. Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS 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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "crypto/hash.h"
+
+namespace tools
+{
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_TXS
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct spent_output {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transaction
+ {
+ uint64_t id;
+ std::string hash;
+ uint64_t timestamp;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t unlock_time;
+ uint64_t height;
+ std::list<spent_output> spent_outputs;
+ std::string payment_id;
+ bool coinbase;
+ bool mempool;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(hash)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(spent_outputs)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(coinbase)
+ KV_SERIALIZE(mempool)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+
+ struct response_t
+ {
+ //std::list<std::string> txs_as_json;
+ uint64_t total_received;
+ uint64_t total_received_unlocked = 0; // OpenMonero only
+ uint64_t scanned_height;
+ std::vector<transaction> transactions;
+ uint64_t blockchain_height;
+ uint64_t scanned_block_height;
+ std::string status;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_received_unlocked)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(transactions)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_ADDRESS_INFO
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct spent_output
+ {
+ uint64_t amount;
+ std::string key_image;
+ std::string tx_pub_key;
+ uint64_t out_index;
+ uint32_t mixin;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(key_image)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(out_index)
+ KV_SERIALIZE(mixin)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t
+ {
+ uint64_t locked_funds;
+ uint64_t total_received;
+ uint64_t total_sent;
+ uint64_t scanned_height;
+ uint64_t scanned_block_height;
+ uint64_t start_height;
+ uint64_t transaction_height;
+ uint64_t blockchain_height;
+ std::list<spent_output> spent_outputs;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(locked_funds)
+ KV_SERIALIZE(total_received)
+ KV_SERIALIZE(total_sent)
+ KV_SERIALIZE(scanned_height)
+ KV_SERIALIZE(scanned_block_height)
+ KV_SERIALIZE(start_height)
+ KV_SERIALIZE(transaction_height)
+ KV_SERIALIZE(blockchain_height)
+ KV_SERIALIZE(spent_outputs)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ //-----------------------------------------------
+ struct COMMAND_RPC_GET_UNSPENT_OUTS
+ {
+ struct request_t
+ {
+ std::string amount;
+ std::string address;
+ std::string view_key;
+ // OpenMonero specific
+ uint64_t mixin;
+ bool use_dust;
+ std::string dust_threshold;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(mixin)
+ KV_SERIALIZE(use_dust)
+ KV_SERIALIZE(dust_threshold)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+
+ struct output {
+ uint64_t amount;
+ std::string public_key;
+ uint64_t index;
+ uint64_t global_index;
+ std::string rct;
+ std::string tx_hash;
+ std::string tx_pub_key;
+ std::string tx_prefix_hash;
+ std::vector<std::string> spend_key_images;
+ uint64_t timestamp;
+ uint64_t height;
+
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(public_key)
+ KV_SERIALIZE(index)
+ KV_SERIALIZE(global_index)
+ KV_SERIALIZE(rct)
+ KV_SERIALIZE(tx_hash)
+ KV_SERIALIZE(tx_pub_key)
+ KV_SERIALIZE(tx_prefix_hash)
+ KV_SERIALIZE(spend_key_images)
+ KV_SERIALIZE(timestamp)
+ KV_SERIALIZE(height)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t
+ {
+ uint64_t amount;
+ std::list<output> outputs;
+ uint64_t per_kb_fee;
+ std::string status;
+ std::string reason;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(outputs)
+ KV_SERIALIZE(per_kb_fee)
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_LOGIN
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+ bool create_account;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ KV_SERIALIZE(create_account)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ std::string reason;
+ bool new_address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ KV_SERIALIZE(new_address)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+ struct COMMAND_RPC_IMPORT_WALLET_REQUEST
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string view_key;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(view_key)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string payment_id;
+ uint64_t import_fee;
+ bool new_request;
+ bool request_fulfilled;
+ std::string payment_address;
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(import_fee)
+ KV_SERIALIZE(new_request)
+ KV_SERIALIZE(request_fulfilled)
+ KV_SERIALIZE(payment_address)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
+}
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index a1f60ea01..a89c8ee6e 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -56,6 +56,8 @@ using namespace epee;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
+#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
+
namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
@@ -66,7 +68,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"};
const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""};
const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
- const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_certificates = {"rpc-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers (all allowed if empty)")};
+ const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates = {"rpc-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")};
const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints = {"rpc-ssl-allowed-fingerprints", tools::wallet2::tr("List of certificate fingerprints to allow")};
constexpr const char default_rpc_username[] = "monero";
@@ -83,7 +85,7 @@ namespace
//------------------------------------------------------------------------------------------------------------------------------
void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward)
{
- if (entry.height >= blockchain_height)
+ if (entry.height >= blockchain_height || (entry.height == 0 && (!strcmp(entry.type.c_str(), "pending") || !strcmp(entry.type.c_str(), "pool"))))
{
entry.confirmations = 0;
entry.suggested_confirmations_threshold = 0;
@@ -124,13 +126,18 @@ namespace tools
{
m_stop = false;
m_net_server.add_idle_handler([this](){
+ if (m_auto_refresh_period == 0) // disabled
+ return true;
+ if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period))
+ return true;
try {
if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon());
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
+ m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
return true;
- }, 20000);
+ }, 1000);
m_net_server.add_idle_handler([this](){
if (m_stop.load(std::memory_order_relaxed))
{
@@ -240,37 +247,102 @@ namespace tools
auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key);
auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate);
- auto rpc_ssl_allowed_certificates = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates);
+ auto rpc_ssl_ca_file = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates);
auto rpc_ssl_allowed_fingerprints = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints);
auto rpc_ssl = command_line::get_arg(vm, arg_rpc_ssl);
- epee::net_utils::ssl_support_t rpc_ssl_support;
- if (!epee::net_utils::ssl_support_from_string(rpc_ssl_support, rpc_ssl))
+ epee::net_utils::ssl_options_t rpc_ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+
+ if (!rpc_ssl_ca_file.empty() || !rpc_ssl_allowed_fingerprints.empty())
{
- MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name));
- return false;
+ std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() };
+ std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
+
+ rpc_ssl_options = epee::net_utils::ssl_options_t{
+ std::move(allowed_fingerprints), std::move(rpc_ssl_ca_file)
+ };
}
- std::list<std::string> allowed_certificates;
- for (const std::string &path: rpc_ssl_allowed_certificates)
+
+ // user specified CA file or fingeprints implies enabled SSL by default
+ if (rpc_ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl))
{
- allowed_certificates.push_back({});
- if (!epee::file_io_utils::load_file_to_string(path, allowed_certificates.back()))
- {
- MERROR("Failed to load certificate: " << path);
- allowed_certificates.back() = std::string();
- }
+ if (!epee::net_utils::ssl_support_from_string(rpc_ssl_options.support, rpc_ssl))
+ {
+ MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name));
+ return false;
+ }
}
- std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() };
- std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
+ rpc_ssl_options.auth = epee::net_utils::ssl_authentication_t{
+ std::move(rpc_ssl_private_key), std::move(rpc_ssl_certificate)
+ };
+
+ m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD;
+ m_last_auto_refresh_time = boost::posix_time::min_date_time;
+
+ check_background_mining();
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
- rpc_ssl_support, std::make_pair(rpc_ssl_private_key, rpc_ssl_certificate), std::move(allowed_certificates), std::move(allowed_fingerprints)
+ std::move(rpc_ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::check_background_mining()
+ {
+ if (!m_wallet)
+ return;
+
+ tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining();
+ if (setup == tools::wallet2::BackgroundMiningNo)
+ {
+ MLOG_RED(el::Level::Warning, "Background mining not enabled. Run \"set setup-background-mining 1\" in monero-wallet-cli to change.");
+ return;
+ }
+
+ if (!m_wallet->is_trusted_daemon())
+ {
+ MDEBUG("Using an untrusted daemon, skipping background mining check");
+ return;
+ }
+
+ cryptonote::COMMAND_RPC_MINING_STATUS::request req;
+ cryptonote::COMMAND_RPC_MINING_STATUS::response res;
+ bool r = m_wallet->invoke_http_json("/mining_status", req, res);
+ if (!r || res.status != CORE_RPC_STATUS_OK)
+ {
+ MERROR("Failed to query mining status: " << (r ? res.status : "No connection to daemon"));
+ return;
+ }
+ if (res.active || res.is_background_mining_enabled)
+ return;
+
+ if (setup == tools::wallet2::BackgroundMiningMaybe)
+ {
+ MINFO("The daemon is not set up to background mine.");
+ MINFO("With background mining enabled, the daemon will mine when idle and not on batttery.");
+ MINFO("Enabling this supports the network you are using, and makes you eligible for receiving new monero");
+ MINFO("Set setup-background-mining to 1 in monero-wallet-cli to change.");
+ return;
+ }
+
+ cryptonote::COMMAND_RPC_START_MINING::request req2;
+ cryptonote::COMMAND_RPC_START_MINING::response res2;
+ req2.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
+ req2.threads_count = 1;
+ req2.do_background_mining = true;
+ req2.ignore_battery = false;
+ r = m_wallet->invoke_http_json("/start_mining", req2, res);
+ if (!r || res2.status != CORE_RPC_STATUS_OK)
+ {
+ MERROR("Failed to setup background mining: " << (r ? res.status : "No connection to daemon"));
+ return;
+ }
+
+ MINFO("Background mining enabled. The daemon will mine when idle and not on batttery.");
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::not_open(epee::json_rpc::error& er)
{
er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN;
@@ -292,6 +364,7 @@ namespace tools
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = pd.m_coinbase ? "block" : "in";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.subaddr_indices.push_back(pd.m_subaddr_index);
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
@@ -363,6 +436,7 @@ namespace tools
entry.double_spend_seen = ppd.m_double_spend_seen;
entry.type = "pool";
entry.subaddr_index = pd.m_subaddr_index;
+ entry.subaddr_indices.push_back(pd.m_subaddr_index);
entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
}
@@ -891,15 +965,7 @@ namespace tools
try
{
- uint64_t mixin;
- if(req.ring_size != 0)
- {
- mixin = m_wallet->adjust_mixin(req.ring_size - 1);
- }
- else
- {
- mixin = m_wallet->adjust_mixin(req.mixin);
- }
+ uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
@@ -951,15 +1017,7 @@ namespace tools
try
{
- uint64_t mixin;
- if(req.ring_size != 0)
- {
- mixin = m_wallet->adjust_mixin(req.ring_size - 1);
- }
- else
- {
- mixin = m_wallet->adjust_mixin(req.mixin);
- }
+ uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
@@ -1177,7 +1235,7 @@ namespace tools
{
const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
- if (has_encrypted_payment_id && !entry.is_subaddress)
+ if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original)
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
auto i = dests.find(entry.addr);
if (i == dests.end())
@@ -1369,15 +1427,7 @@ namespace tools
try
{
- uint64_t mixin;
- if(req.ring_size != 0)
- {
- mixin = m_wallet->adjust_mixin(req.ring_size - 1);
- }
- else
- {
- mixin = m_wallet->adjust_mixin(req.mixin);
- }
+ uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
@@ -1432,15 +1482,7 @@ namespace tools
try
{
- uint64_t mixin;
- if(req.ring_size != 0)
- {
- mixin = m_wallet->adjust_mixin(req.ring_size - 1);
- }
- else
- {
- mixin = m_wallet->adjust_mixin(req.mixin);
- }
+ uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra);
@@ -1852,7 +1894,7 @@ namespace tools
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
- er.message = "The wallet is watch-only. Cannot display seed.";
+ er.message = "The wallet is watch-only. Cannot retrieve seed.";
return false;
}
if (!m_wallet->is_deterministic())
@@ -1877,6 +1919,12 @@ namespace tools
}
else if(req.key_type.compare("spend_key") == 0)
{
+ if (m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "The wallet is watch-only. Cannot retrieve spend key.";
+ return false;
+ }
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
res.key = std::string(key.data(), key.size());
}
@@ -2834,6 +2882,28 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ try
+ {
+ m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0;
+ MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled")));
+ return true;
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
@@ -2908,7 +2978,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
- crypto::ElectrumWords::get_language_list(res.languages);
+ crypto::ElectrumWords::get_language_list(res.languages, true);
+ crypto::ElectrumWords::get_language_list(res.languages_local, false);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -2936,17 +3007,22 @@ namespace tools
er.message = "Invalid filename";
return false;
}
- std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
{
std::vector<std::string> languages;
- crypto::ElectrumWords::get_language_list(languages);
+ crypto::ElectrumWords::get_language_list(languages, false);
std::vector<std::string>::iterator it;
it = std::find(languages.begin(), languages.end(), req.language);
if (it == languages.end())
{
+ crypto::ElectrumWords::get_language_list(languages, true);
+ it = std::find(languages.begin(), languages.end(), req.language);
+ }
+ if (it == languages.end())
+ {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Unknown language";
+ er.message = "Unknown language: " + req.language;
return false;
}
}
@@ -3221,12 +3297,6 @@ namespace tools
}
// early check for mandatory fields
- if (req.filename.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to.";
- return false;
- }
if (req.viewkey.empty())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3255,7 +3325,7 @@ namespace tools
er.message = "Invalid filename";
return false;
}
- std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
// check if wallet file already exists
if (!wallet_file.empty())
{
@@ -3365,7 +3435,8 @@ namespace tools
{
try
{
- m_wallet->store();
+ if (!wallet_file.empty())
+ m_wallet->store();
}
catch (const std::exception &e)
{
@@ -3389,12 +3460,6 @@ namespace tools
}
// early check for mandatory fields
- if (req.filename.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to.";
- return false;
- }
if (req.seed.empty())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -3417,7 +3482,7 @@ namespace tools
er.message = "Invalid filename";
return false;
}
- std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
// check if wallet file already exists
if (!wallet_file.empty())
{
@@ -3533,7 +3598,7 @@ namespace tools
er.message = "Failed to encode seed";
return false;
}
- res.seed = electrum_words.data();
+ res.seed = std::string(electrum_words.data(), electrum_words.size());
if (!wal)
{
@@ -4042,6 +4107,58 @@ namespace tools
return false;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
+ ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
+ for (const std::string &fp: req.ssl_allowed_fingerprints)
+ {
+ ssl_allowed_fingerprints.push_back({});
+ std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
+ for (auto c: fp)
+ v.push_back(c);
+ }
+
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+ if (req.ssl_allow_any_cert)
+ ssl_options.verification = epee::net_utils::ssl_verification_t::none;
+ else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
+ ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(req.ssl_ca_file)};
+
+ if (!epee::net_utils::ssl_support_from_string(ssl_options.support, req.ssl_support))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
+ er.message = std::string("Invalid ssl support mode");
+ return false;
+ }
+
+ ssl_options.auth = epee::net_utils::ssl_authentication_t{
+ std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
+ };
+
+ if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled && !ssl_options.has_strong_verification(boost::string_ref{}))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
+ er.message = "SSL is enabled but no user certificate or fingerprints were provided";
+ }
+
+ if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
+ er.message = std::string("Unable to set daemon");
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
res.version = WALLET_RPC_VERSION;
@@ -4241,7 +4358,7 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_rpc_ssl);
command_line::add_arg(desc_params, arg_rpc_ssl_private_key);
command_line::add_arg(desc_params, arg_rpc_ssl_certificate);
- command_line::add_arg(desc_params, arg_rpc_ssl_allowed_certificates);
+ command_line::add_arg(desc_params, arg_rpc_ssl_ca_certificates);
command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints);
daemonizer::init_options(hidden_options, desc_params);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index affaf10f7..7d2272dd0 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -129,6 +129,7 @@ namespace tools
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
+ MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
@@ -149,6 +150,7 @@ namespace tools
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
MAP_JON_RPC_WE("validate_address", on_validate_address, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS)
+ MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON)
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -210,6 +212,7 @@ namespace tools
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -230,6 +233,7 @@ namespace tools
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
//json rpc v2
@@ -250,11 +254,15 @@ namespace tools
bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er);
+ void check_background_mining();
+
wallet2 *m_wallet;
std::string m_wallet_dir;
tools::private_file rpc_login_file;
std::atomic<bool> m_stop;
bool m_restricted;
const boost::program_options::variables_map *m_vm;
+ uint32_t m_auto_refresh_period;
+ boost::posix_time::ptime m_last_auto_refresh_time;
};
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 36775fa1e..4c945ab41 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 8
+#define WALLET_RPC_VERSION_MINOR 9
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -446,7 +446,6 @@ namespace wallet_rpc
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
- uint64_t mixin;
uint64_t ring_size;
uint64_t unlock_time;
std::string payment_id;
@@ -460,7 +459,6 @@ namespace wallet_rpc
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
- KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
@@ -505,7 +503,6 @@ namespace wallet_rpc
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
- uint64_t mixin;
uint64_t ring_size;
uint64_t unlock_time;
std::string payment_id;
@@ -519,7 +516,6 @@ namespace wallet_rpc
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
- KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
@@ -746,7 +742,6 @@ namespace wallet_rpc
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
uint32_t priority;
- uint64_t mixin;
uint64_t ring_size;
uint64_t outputs;
uint64_t unlock_time;
@@ -762,7 +757,6 @@ namespace wallet_rpc
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE(priority)
- KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE_OPT(outputs, (uint64_t)1)
KV_SERIALIZE(unlock_time)
@@ -816,7 +810,6 @@ namespace wallet_rpc
{
std::string address;
uint32_t priority;
- uint64_t mixin;
uint64_t ring_size;
uint64_t outputs;
uint64_t unlock_time;
@@ -830,7 +823,6 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(priority)
- KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE_OPT(outputs, (uint64_t)1)
KV_SERIALIZE(unlock_time)
@@ -1932,6 +1924,28 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_AUTO_REFRESH
+ {
+ struct request_t
+ {
+ bool enable;
+ uint32_t period; // seconds
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(enable, true)
+ KV_SERIALIZE_OPT(period, (uint32_t)0)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_START_MINING
{
struct request_t
@@ -1985,9 +1999,11 @@ namespace wallet_rpc
struct response_t
{
std::vector<std::string> languages;
+ std::vector<std::string> languages_local;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(languages)
+ KV_SERIALIZE(languages_local)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2423,5 +2439,39 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SET_DAEMON
+ {
+ struct request_t
+ {
+ std::string address;
+ bool trusted;
+ std::string ssl_support; // disabled, enabled, autodetect
+ std::string ssl_private_key_path;
+ std::string ssl_certificate_path;
+ std::string ssl_ca_file;
+ std::vector<std::string> ssl_allowed_fingerprints;
+ bool ssl_allow_any_cert;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE_OPT(trusted, false)
+ KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect")
+ KV_SERIALIZE(ssl_private_key_path)
+ KV_SERIALIZE(ssl_certificate_path)
+ KV_SERIALIZE(ssl_ca_file)
+ KV_SERIALIZE(ssl_allowed_fingerprints)
+ KV_SERIALIZE_OPT(ssl_allow_any_cert, false)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
}
}