aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/wallet.cpp12
-rw-r--r--src/wallet/api/wallet2_api.h27
-rw-r--r--src/wallet/api/wallet_manager.cpp15
-rw-r--r--src/wallet/api/wallet_manager.h5
-rw-r--r--src/wallet/ringdb.cpp32
-rw-r--r--src/wallet/ringdb.h1
-rw-r--r--src/wallet/wallet2.cpp336
-rw-r--r--src/wallet/wallet2.h39
-rw-r--r--src/wallet/wallet_rpc_server.cpp155
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h12
11 files changed, 459 insertions, 177 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index c1303c225..032b873d6 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -249,6 +249,13 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
+ virtual void on_device_button_pressed()
+ {
+ if (m_listener) {
+ m_listener->onDeviceButtonPressed();
+ }
+ }
+
virtual boost::optional<epee::wipeable_string> on_device_pin_request()
{
if (m_listener) {
@@ -449,6 +456,11 @@ WalletImpl::~WalletImpl()
close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
stopRefresh();
+
+ if (m_wallet2Callback->getListener()) {
+ m_wallet2Callback->getListener()->onSetWallet(nullptr);
+ }
+
LOG_PRINT_L1(__FUNCTION__ << " finished");
}
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index ee1d6ae79..0af3b1867 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -37,6 +37,7 @@
#include <set>
#include <ctime>
#include <iostream>
+#include <stdexcept>
// Public interface for libwallet library
namespace Monero {
@@ -337,6 +338,7 @@ protected:
bool m_indeterminate;
};
+struct Wallet;
struct WalletListener
{
virtual ~WalletListener() = 0;
@@ -381,7 +383,12 @@ struct WalletListener
/**
* @brief called by device if the action is required
*/
- virtual void onDeviceButtonRequest(uint64_t code) {}
+ virtual void onDeviceButtonRequest(uint64_t code) { (void)code; }
+
+ /**
+ * @brief called by device if the button was pressed
+ */
+ virtual void onDeviceButtonPressed() { }
/**
* @brief called by device when PIN is needed
@@ -401,7 +408,12 @@ struct WalletListener
/**
* @brief Signalizes device operation progress
*/
- virtual void onDeviceProgress(const DeviceProgress & event) {};
+ virtual void onDeviceProgress(const DeviceProgress & event) { (void)event; };
+
+ /**
+ * @brief If the listener is created before the wallet this enables to set created wallet object
+ */
+ virtual void onSetWallet(Wallet * wallet) { (void)wallet; };
};
@@ -440,8 +452,8 @@ struct Wallet
//! returns both error and error string atomically. suggested to use in instead of status() and errorString()
virtual void statusWithErrorString(int& status, std::string& errorString) const = 0;
virtual bool setPassword(const std::string &password) = 0;
- virtual bool setDevicePin(const std::string &password) { return false; };
- virtual bool setDevicePassphrase(const std::string &password) { return false; };
+ virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
+ virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string path() const = 0;
@@ -1020,9 +1032,10 @@ struct WalletManager
* \param password Password of wallet file
* \param nettype Network type
* \param kdf_rounds Number of rounds for key derivation function
+ * \param listener Wallet listener to set to the wallet after creation
* \return Wallet instance (Wallet::status() needs to be called to check if opened successfully)
*/
- virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1) = 0;
+ virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1, WalletListener * listener = nullptr) = 0;
Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated
{
return openWallet(path, password, testnet ? TESTNET : MAINNET);
@@ -1134,6 +1147,7 @@ struct WalletManager
* \param restoreHeight restore from start height (0 sets to current height)
* \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value)
* \param kdf_rounds Number of rounds for key derivation function
+ * \param listener Wallet listener to set to the wallet after creation
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * createWalletFromDevice(const std::string &path,
@@ -1142,7 +1156,8 @@ struct WalletManager
const std::string &deviceName,
uint64_t restoreHeight = 0,
const std::string &subaddressLookahead = "",
- uint64_t kdf_rounds = 1) = 0;
+ uint64_t kdf_rounds = 1,
+ WalletListener * listener = nullptr) = 0;
/*!
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index f584e88ac..ef2ed2015 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -57,9 +57,14 @@ Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::stri
return wallet;
}
-Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds)
+Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds, WalletListener * listener)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
+ wallet->setListener(listener);
+ if (listener){
+ listener->onSetWallet(wallet);
+ }
+
wallet->open(path, password);
//Refresh addressBook
wallet->addressBook()->refresh();
@@ -122,9 +127,15 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
const std::string &deviceName,
uint64_t restoreHeight,
const std::string &subaddressLookahead,
- uint64_t kdf_rounds)
+ uint64_t kdf_rounds,
+ WalletListener * listener)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
+ wallet->setListener(listener);
+ if (listener){
+ listener->onSetWallet(wallet);
+ }
+
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
} else {
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 0c83d794f..235f96e17 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -40,7 +40,7 @@ class WalletManagerImpl : public WalletManager
public:
Wallet * createWallet(const std::string &path, const std::string &password,
const std::string &language, NetworkType nettype, uint64_t kdf_rounds = 1) override;
- Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1) override;
+ Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1, WalletListener * listener = nullptr) override;
virtual Wallet * recoveryWallet(const std::string &path,
const std::string &password,
const std::string &mnemonic,
@@ -72,7 +72,8 @@ public:
const std::string &deviceName,
uint64_t restoreHeight = 0,
const std::string &subaddressLookahead = "",
- uint64_t kdf_rounds = 1) override;
+ uint64_t kdf_rounds = 1,
+ WalletListener * listener = nullptr) override;
virtual bool closeWallet(Wallet *wallet, bool store = true) override;
bool walletExists(const std::string &path) override;
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index b69022af4..8da95de7b 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -281,7 +281,7 @@ bool ringdb::add_rings(const crypto::chacha_key &chacha_key, const cryptonote::t
return true;
}
-bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
+bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images)
{
MDB_txn *txn;
int dbr;
@@ -294,17 +294,10 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
- for (const auto &in: tx.vin)
+ for (const crypto::key_image &key_image: key_images)
{
- if (in.type() != typeid(cryptonote::txin_to_key))
- continue;
- const auto &txin = boost::get<cryptonote::txin_to_key>(in);
- const uint32_t ring_size = txin.key_offsets.size();
- if (ring_size == 1)
- continue;
-
MDB_val key, data;
- std::string key_ciphertext = encrypt(txin.k_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
@@ -314,7 +307,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
continue;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
- MDEBUG("Removing ring data for key image " << txin.k_image);
+ MDEBUG("Removing ring data for key image " << key_image);
dbr = mdb_del(txn, dbi_rings, &key, NULL);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to remove ring to database: " + std::string(mdb_strerror(dbr)));
}
@@ -325,6 +318,23 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
return true;
}
+bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
+{
+ std::vector<crypto::key_image> key_images;
+ key_images.reserve(tx.vin.size());
+ for (const auto &in: tx.vin)
+ {
+ if (in.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ const auto &txin = boost::get<cryptonote::txin_to_key>(in);
+ const uint32_t ring_size = txin.key_offsets.size();
+ if (ring_size == 1)
+ continue;
+ key_images.push_back(txin.k_image);
+ }
+ return remove_rings(chacha_key, key_images);
+}
+
bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
MDB_txn *txn;
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 7b448b0d7..9c7e624bc 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -45,6 +45,7 @@ namespace tools
~ringdb();
bool add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
+ bool remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images);
bool remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 0e82f1a91..b288994a5 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 = {
@@ -264,6 +265,7 @@ struct options {
const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
+ const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
@@ -314,6 +316,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 +324,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 +384,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,28 +445,17 @@ 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);
wallet->device_name(device_name);
wallet->device_derivation_path(device_derivation_path);
+ if (command_line::get_arg(vm, opts.no_dns))
+ wallet->enable_dns(false);
+
try
{
if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
@@ -978,6 +996,12 @@ void wallet_device_callback::on_button_request(uint64_t code)
wallet->on_device_button_request(code);
}
+void wallet_device_callback::on_button_pressed()
+{
+ if (wallet)
+ wallet->on_device_button_pressed();
+}
+
boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request()
{
if (wallet)
@@ -1032,6 +1056,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),
@@ -1057,7 +1082,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_encrypt_keys_after_refresh(boost::none),
m_unattended(unattended),
m_devices_registered(false),
- m_device_last_key_image_sync(0)
+ m_device_last_key_image_sync(0),
+ m_use_dns(true)
{
}
@@ -1100,9 +1126,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);
@@ -1111,6 +1138,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.hw_device);
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
command_line::add_arg(desc_params, opts.tx_notify);
+ command_line::add_arg(desc_params, opts.no_dns);
}
std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -1154,10 +1182,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, 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)
{
if(m_http_client.is_connected())
m_http_client.disconnect();
@@ -1166,17 +1191,17 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
m_trusted_daemon = trusted_daemon;
MINFO("setting daemon to " << get_daemon_address());
- 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 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_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::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)});
- return set_daemon(daemon_address, daemon_login, trusted_daemon, 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
@@ -2275,6 +2300,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,
@@ -2284,7 +2315,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)
@@ -2348,9 +2379,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)
@@ -2416,6 +2445,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;
@@ -2444,6 +2478,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);
@@ -2462,6 +2498,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;
@@ -2479,6 +2516,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");
@@ -3522,6 +3565,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());
@@ -3673,6 +3719,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;
@@ -3827,6 +3874,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);
@@ -4128,6 +4177,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
@@ -4197,18 +4257,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();
@@ -4241,13 +4298,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
@@ -4330,13 +4381,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);
@@ -4371,13 +4418,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);
@@ -4412,13 +4454,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;
@@ -4550,10 +4586,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;
@@ -5441,13 +5476,19 @@ uint64_t wallet2::balance(uint32_t index_major) const
return amount;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance(uint32_t index_major) const
+uint64_t wallet2::unlocked_balance(uint32_t index_major, uint64_t *blocks_to_unlock) const
{
uint64_t amount = 0;
+ if (blocks_to_unlock)
+ *blocks_to_unlock = 0;
if(m_light_wallet)
return m_light_wallet_balance;
for (const auto& i : unlocked_balance_per_subaddress(index_major))
- amount += i.second;
+ {
+ amount += i.second.first;
+ if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
+ *blocks_to_unlock = i.second.second;
+ }
return amount;
}
//----------------------------------------------------------------------------------------------------
@@ -5480,18 +5521,36 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
return amount_per_subaddr;
}
//----------------------------------------------------------------------------------------------------
-std::map<uint32_t, uint64_t> wallet2::unlocked_balance_per_subaddress(uint32_t index_major) const
+std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major) const
{
- std::map<uint32_t, uint64_t> amount_per_subaddr;
+ std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr;
+ const uint64_t blockchain_height = get_blockchain_current_height();
for(const transfer_details& td: m_transfers)
{
- if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen && is_transfer_unlocked(td))
+ if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
{
+ uint64_t amount = 0, blocks_to_unlock = 0;
+ if (is_transfer_unlocked(td))
+ {
+ amount = td.amount();
+ blocks_to_unlock = 0;
+ }
+ else
+ {
+ uint64_t unlock_height = td.m_block_height + std::max<uint64_t>(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
+ if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height)
+ unlock_height = td.m_tx.unlock_time;
+ blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0;
+ amount = 0;
+ }
auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
if (found == amount_per_subaddr.end())
- amount_per_subaddr[td.m_subaddr_index.minor] = td.amount();
+ amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock);
else
- found->second += td.amount();
+ {
+ found->second.first += amount;
+ found->second.second = std::max(found->second.second, blocks_to_unlock);
+ }
}
}
return amount_per_subaddr;
@@ -5505,11 +5564,18 @@ uint64_t wallet2::balance_all() const
return r;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance_all() const
+uint64_t wallet2::unlocked_balance_all(uint64_t *blocks_to_unlock) const
{
uint64_t r = 0;
+ if (blocks_to_unlock)
+ *blocks_to_unlock = 0;
for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
- r += unlocked_balance(index_major);
+ {
+ uint64_t local_blocks_to_unlock;
+ r += unlocked_balance(index_major, blocks_to_unlock ? &local_blocks_to_unlock : NULL);
+ if (blocks_to_unlock)
+ *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
+ }
return r;
}
//----------------------------------------------------------------------------------------------------
@@ -5719,7 +5785,7 @@ namespace
{
CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
- size_t idx = crypto::rand<size_t>() % vec.size();
+ size_t idx = crypto::rand_idx(vec.size());
return pop_index (vec, idx);
}
@@ -5822,7 +5888,7 @@ size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::ve
}
else
{
- idx = crypto::rand<size_t>() % candidates.size();
+ idx = crypto::rand_idx(candidates.size());
}
return pop_index (unused_indices, candidates[idx]);
}
@@ -7040,6 +7106,43 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
catch (const std::exception &e) { return false; }
}
+bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
+{
+ if (!m_ringdb)
+ return false;
+
+ try { return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
+ catch (const std::exception &e) { return false; }
+}
+
+bool wallet2::unset_ring(const crypto::hash &txid)
+{
+ if (!m_ringdb)
+ return false;
+
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = true;
+ m_daemon_rpc_mutex.lock();
+ bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
+ if (res.txs.empty())
+ return false;
+ THROW_WALLET_EXCEPTION_IF(res.txs.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::transaction tx;
+ crypto::hash tx_hash;
+ if (!get_pruned_tx(res.txs.front(), tx, tx_hash))
+ return false;
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
+ catch (const std::exception &e) { return false; }
+}
+
bool wallet2::find_and_save_rings(bool force)
{
if (!force && m_ring_history_saved)
@@ -7474,7 +7577,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (n_rct == 0)
return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset + rct_start_height);
- return first_rct + crypto::rand<uint64_t>() % n_rct;
+ return first_rct + crypto::rand_idx(n_rct);
};
size_t num_selected_transfers = 0;
@@ -9055,7 +9158,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// throw if attempting a transaction with no money
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
- std::map<uint32_t, uint64_t> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account);
if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
@@ -9073,7 +9176,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (uint32_t index_minor : subaddr_indices)
{
balance_subtotal += balance_per_subaddr[index_minor];
- unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor];
+ unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor].first;
}
THROW_WALLET_EXCEPTION_IF(needed_money + min_fee > balance_subtotal, error::not_enough_money,
balance_subtotal, needed_money, 0);
@@ -9139,7 +9242,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
auto sort_predicate = [&unlocked_balance_per_subaddr] (const std::pair<uint32_t, std::vector<size_t>>& x, const std::pair<uint32_t, std::vector<size_t>>& y)
{
- return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first];
+ return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first;
};
std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate);
std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate);
@@ -9530,14 +9633,16 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, s
change -= r.second.first;
MDEBUG("Adding " << cryptonote::print_money(change) << " expected change");
+ // for all txes that have actual change, check change is coming back to the sending wallet
for (const pending_tx &ptx: ptx_vector)
- 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(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;
+ {
+ if (ptx.change_dts.amount == 0)
+ continue;
+ THROW_WALLET_EXCEPTION_IF(m_subaddresses.find(ptx.change_dts.addr.m_spend_public_key) == m_subaddresses.end(),
+ error::wallet_internal_error, "Change address is not ours");
+ required[ptx.change_dts.addr].first += ptx.change_dts.amount;
+ required[ptx.change_dts.addr].second = ptx.change_dts.is_subaddress;
+ }
for (const auto &r: required)
{
@@ -9603,7 +9708,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
unused_transfer_dust_indices_per_subaddr.erase(0);
auto i = unused_transfer_dust_indices_per_subaddr.begin();
- std::advance(i, crypto::rand<size_t>() % unused_transfer_dust_indices_per_subaddr.size());
+ std::advance(i, crypto::rand_idx(unused_transfer_dust_indices_per_subaddr.size()));
unused_transfers_indices = i->second.first;
unused_dust_indices = i->second.second;
LOG_PRINT_L2("Spending from subaddress index " << i->first);
@@ -10584,13 +10689,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;
}
}
@@ -10786,13 +10891,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;
@@ -12774,8 +12879,7 @@ uint64_t wallet2::get_segregation_fork_height() const
if (m_segregation_height > 0)
return m_segregation_height;
- static const bool use_dns = true;
- if (use_dns)
+ if (m_use_dns)
{
// All four MoneroPulse domains have DNSSEC on and valid
static const std::vector<std::string> dns_urls = {
@@ -12855,6 +12959,12 @@ void wallet2::on_device_button_request(uint64_t code)
m_callback->on_device_button_request(code);
}
//----------------------------------------------------------------------------------------------------
+void wallet2::on_device_button_pressed()
+{
+ if (nullptr != m_callback)
+ m_callback->on_device_button_pressed();
+}
+//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> wallet2::on_device_pin_request()
{
if (nullptr != m_callback)
@@ -12953,4 +13063,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 2eb7de94a..39380c9df 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -105,6 +105,7 @@ namespace tools
virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
// Device callbacks
virtual void on_device_button_request(uint64_t code) {}
+ virtual void on_device_button_pressed() {}
virtual boost::optional<epee::wipeable_string> on_device_pin_request() { return boost::none; }
virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) { return boost::none; }
virtual void on_device_progress(const hw::device_progress& event) {};
@@ -118,6 +119,7 @@ namespace tools
public:
wallet_device_callback(wallet2 * wallet): wallet(wallet) {};
void on_button_request(uint64_t code=0) override;
+ void on_button_pressed() override;
boost::optional<epee::wipeable_string> on_pin_request() override;
boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override;
void on_progress(const hw::device_progress& event) override;
@@ -194,6 +196,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);
@@ -533,6 +541,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(); }
};
/*!
@@ -687,16 +697,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_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);
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
@@ -771,13 +775,13 @@ namespace tools
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
- uint64_t unlocked_balance(uint32_t subaddr_index_major) const;
+ uint64_t unlocked_balance(uint32_t subaddr_index_major, uint64_t *blocks_to_unlock = NULL) const;
// locked & unlocked balance per subaddress of given or current subaddress account
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major) const;
- std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const;
+ std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const;
// all locked & unlocked balances of all subaddress accounts
uint64_t balance_all() const;
- uint64_t unlocked_balance_all() const;
+ uint64_t unlocked_balance_all(uint64_t *blocks_to_unlock = NULL) const;
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
@@ -1014,6 +1018,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; }
@@ -1247,6 +1253,8 @@ namespace tools
bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs);
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
+ bool unset_ring(const std::vector<crypto::key_image> &key_images);
+ bool unset_ring(const crypto::hash &txid);
bool find_and_save_rings(bool force = true);
bool blackball_output(const std::pair<uint64_t, uint64_t> &output);
@@ -1262,6 +1270,9 @@ namespace tools
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,6 +1290,7 @@ namespace tools
void hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const;
uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const;
void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash);
+ void enable_dns(bool enable) { m_use_dns = enable; }
private:
/*!
@@ -1296,6 +1308,7 @@ 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, 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;
@@ -1362,11 +1375,13 @@ 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);
wallet_device_callback * get_device_callback();
void on_device_button_request(uint64_t code);
+ void on_device_button_pressed();
boost::optional<epee::wipeable_string> on_device_pin_request();
boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device);
void on_device_progress(const hw::device_progress& event);
@@ -1450,6 +1465,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];
@@ -1457,6 +1473,7 @@ namespace tools
std::string m_device_name;
std::string m_device_derivation_path;
uint64_t m_device_last_key_image_sync;
+ bool m_use_dns;
// Aux transaction data from device
std::unordered_map<crypto::hash, std::string> m_tx_device;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 95cda7f1e..71c64d3c1 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -68,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";
@@ -247,40 +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;
@@ -385,10 +447,10 @@ namespace tools
try
{
res.balance = req.all_accounts ? m_wallet->balance_all() : m_wallet->balance(req.account_index);
- res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all() : m_wallet->unlocked_balance(req.account_index);
+ res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(&res.blocks_to_unlock) : m_wallet->unlocked_balance(req.account_index, &res.blocks_to_unlock);
res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images();
std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account;
- std::map<uint32_t, std::map<uint32_t, uint64_t>> unlocked_balance_per_subaddress_per_account;
+ std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account;
if (req.all_accounts)
{
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
@@ -408,7 +470,7 @@ namespace tools
{
uint32_t account_index = p.first;
std::map<uint32_t, uint64_t> balance_per_subaddress = p.second;
- std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index];
+ std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index];
std::set<uint32_t> address_indices;
if (!req.all_accounts && !req.address_indices.empty())
{
@@ -427,7 +489,8 @@ namespace tools
cryptonote::subaddress_index index = {info.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.balance = balance_per_subaddress[i];
- info.unlocked_balance = unlocked_balance_per_subaddress[i];
+ info.unlocked_balance = unlocked_balance_per_subaddress[i].first;
+ info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second;
info.label = m_wallet->get_subaddress_label(index);
info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; });
res.per_subaddress.emplace_back(std::move(info));
@@ -1173,7 +1236,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())
@@ -2916,7 +2979,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;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -2947,14 +3011,19 @@ namespace tools
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;
}
}
@@ -4048,13 +4117,7 @@ namespace tools
er.message = "Command unavailable in restricted mode.";
return false;
}
- epee::net_utils::ssl_support_t ssl_support;
- if (!epee::net_utils::ssl_support_from_string(ssl_support, req.ssl_support))
- {
- er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
- er.message = std::string("Invalid ssl support 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)
@@ -4064,7 +4127,31 @@ namespace tools
for (auto c: fp)
v.push_back(c);
}
- if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, ssl_support, std::make_pair(req.ssl_private_key_path, req.ssl_certificate_path), req.ssl_allowed_certificates, ssl_allowed_fingerprints, req.ssl_allow_any_cert))
+
+ 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");
@@ -4272,7 +4359,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 fb0c48a80..7d2272dd0 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -254,6 +254,8 @@ 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;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 298f34f66..bb360ae01 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
@@ -81,6 +81,7 @@ namespace wallet_rpc
uint64_t unlocked_balance;
std::string label;
uint64_t num_unspent_outputs;
+ uint64_t blocks_to_unlock;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
@@ -90,6 +91,7 @@ namespace wallet_rpc
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
KV_SERIALIZE(num_unspent_outputs)
+ KV_SERIALIZE(blocks_to_unlock)
END_KV_SERIALIZE_MAP()
};
@@ -99,12 +101,14 @@ namespace wallet_rpc
uint64_t unlocked_balance;
bool multisig_import_needed;
std::vector<per_subaddress_info> per_subaddress;
+ uint64_t blocks_to_unlock;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(multisig_import_needed)
KV_SERIALIZE(per_subaddress)
+ KV_SERIALIZE(blocks_to_unlock)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1999,9 +2003,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;
@@ -2446,7 +2452,7 @@ namespace wallet_rpc
std::string ssl_support; // disabled, enabled, autodetect
std::string ssl_private_key_path;
std::string ssl_certificate_path;
- std::list<std::string> ssl_allowed_certificates;
+ std::string ssl_ca_file;
std::vector<std::string> ssl_allowed_fingerprints;
bool ssl_allow_any_cert;
@@ -2456,7 +2462,7 @@ namespace wallet_rpc
KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect")
KV_SERIALIZE(ssl_private_key_path)
KV_SERIALIZE(ssl_certificate_path)
- KV_SERIALIZE(ssl_allowed_certificates)
+ KV_SERIALIZE(ssl_ca_file)
KV_SERIALIZE(ssl_allowed_fingerprints)
KV_SERIALIZE_OPT(ssl_allow_any_cert, false)
END_KV_SERIALIZE_MAP()