aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/api/utils.cpp3
-rw-r--r--src/wallet/api/wallet.cpp18
-rw-r--r--src/wallet/api/wallet.h5
-rw-r--r--src/wallet/api/wallet2_api.h30
-rw-r--r--src/wallet/api/wallet_manager.cpp27
-rw-r--r--src/wallet/api/wallet_manager.h15
-rw-r--r--src/wallet/wallet2.cpp272
-rw-r--r--src/wallet/wallet2.h25
-rw-r--r--src/wallet/wallet_args.cpp3
-rw-r--r--src/wallet/wallet_rpc_server.cpp71
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h25
12 files changed, 382 insertions, 114 deletions
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index aebe41e59..86fe56564 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -51,6 +51,9 @@ bool isAddressLocal(const std::string &address)
void onStartup()
{
tools::on_startup();
+#ifdef NDEBUG
+ tools::disable_core_dumps();
+#endif
}
}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 3f6bfec9e..f7c074b5a 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -366,7 +366,7 @@ void Wallet::error(const std::string &category, const std::string &str) {
}
///////////////////////// WalletImpl implementation ////////////////////////
-WalletImpl::WalletImpl(NetworkType nettype)
+WalletImpl::WalletImpl(NetworkType nettype, bool restricted, uint64_t kdf_rounds)
:m_wallet(nullptr)
, m_status(Wallet::Status_Ok)
, m_trustedDaemon(false)
@@ -377,7 +377,7 @@ WalletImpl::WalletImpl(NetworkType nettype)
, m_rebuildWalletCache(false)
, m_is_connected(false)
{
- m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype));
+ m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), restricted, kdf_rounds);
m_history = new TransactionHistoryImpl(this);
m_wallet2Callback = new Wallet2CallbackImpl(this);
m_wallet->callback(m_wallet2Callback);
@@ -2205,6 +2205,20 @@ void WalletImpl::keyReuseMitigation2(bool mitigation)
m_wallet->key_reuse_mitigation2(mitigation);
}
+bool WalletImpl::lockKeysFile()
+{
+ return m_wallet->lock_keys_file();
+}
+
+bool WalletImpl::unlockKeysFile()
+{
+ return m_wallet->unlock_keys_file();
+}
+
+bool WalletImpl::isKeysFileLocked()
+{
+ return m_wallet->is_keys_file_locked();
+}
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index eefb2fe94..28b73423d 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -52,7 +52,7 @@ struct Wallet2CallbackImpl;
class WalletImpl : public Wallet
{
public:
- WalletImpl(NetworkType nettype = MAINNET);
+ WalletImpl(NetworkType nettype = MAINNET, bool restricted = false, uint64_t kdf_rounds = 1);
~WalletImpl();
bool create(const std::string &path, const std::string &password,
const std::string &language);
@@ -188,6 +188,9 @@ public:
virtual void segregatePreForkOutputs(bool segregate) override;
virtual void segregationHeight(uint64_t height) override;
virtual void keyReuseMitigation2(bool mitigation) override;
+ virtual bool lockKeysFile() override;
+ virtual bool unlockKeysFile() override;
+ virtual bool isKeysFileLocked() override;
private:
void clearStatus() const;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index f54255e91..5a52c6b17 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -900,6 +900,12 @@ struct Wallet
//! Initiates a light wallet import wallet request
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
+
+ //! locks/unlocks the keys file; returns true on success
+ virtual bool lockKeysFile() = 0;
+ virtual bool unlockKeysFile() = 0;
+ //! returns true if the keys file is locked
+ virtual bool isKeysFileLocked() = 0;
};
/**
@@ -914,9 +920,10 @@ struct WalletManager
* \param password Password of wallet file
* \param language Language to be used to generate electrum seed mnemonic
* \param nettype Network type
+ * \param kdf_rounds Number of rounds for key derivation function
* \return Wallet instance (Wallet::status() needs to be called to check if created successfully)
*/
- virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype) = 0;
+ virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype, uint64_t kdf_rounds = 1) = 0;
Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) // deprecated
{
return createWallet(path, password, language, testnet ? TESTNET : MAINNET);
@@ -927,9 +934,10 @@ struct WalletManager
* \param path Name of wallet file
* \param password Password of wallet file
* \param nettype Network type
+ * \param kdf_rounds Number of rounds for key derivation function
* \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) = 0;
+ virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1) = 0;
Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated
{
return openWallet(path, password, testnet ? TESTNET : MAINNET);
@@ -942,10 +950,11 @@ struct WalletManager
* \param mnemonic mnemonic (25 words electrum seed)
* \param nettype Network type
* \param restoreHeight restore from start height
+ * \param kdf_rounds Number of rounds for key derivation function
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
- NetworkType nettype = MAINNET, uint64_t restoreHeight = 0) = 0;
+ NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1) = 0;
Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
bool testnet = false, uint64_t restoreHeight = 0) // deprecated
{
@@ -977,6 +986,7 @@ struct WalletManager
* \param addressString public address
* \param viewKeyString view key
* \param spendKeyString spend key (optional)
+ * \param kdf_rounds Number of rounds for key derivation function
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * createWalletFromKeys(const std::string &path,
@@ -986,7 +996,8 @@ struct WalletManager
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
- const std::string &spendKeyString = "") = 0;
+ const std::string &spendKeyString = "",
+ uint64_t kdf_rounds = 1) = 0;
Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
@@ -1037,6 +1048,7 @@ struct WalletManager
* \param deviceName Device name
* \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
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * createWalletFromDevice(const std::string &path,
@@ -1044,7 +1056,8 @@ struct WalletManager
NetworkType nettype,
const std::string &deviceName,
uint64_t restoreHeight = 0,
- const std::string &subaddressLookahead = "") = 0;
+ const std::string &subaddressLookahead = "",
+ uint64_t kdf_rounds = 1) = 0;
/*!
* \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
@@ -1069,9 +1082,14 @@ struct WalletManager
* @param keys_file_name - location of keys file
* @param password - password to verify
* @param no_spend_key - verify only view keys?
+ * @param kdf_rounds - number of rounds for key derivation function
* @return - true if password is correct
+ *
+ * @note
+ * This function will fail when the wallet keys file is opened because the wallet program locks the keys file.
+ * In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively.
*/
- virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0;
+ virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0;
/*!
* \brief findWallets - searches for the wallet files by given path name recursively
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 99eadc82f..5daf11ec0 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -50,16 +50,16 @@ namespace epee {
namespace Monero {
Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password,
- const std::string &language, NetworkType nettype)
+ const std::string &language, NetworkType nettype, uint64_t kdf_rounds)
{
- WalletImpl * wallet = new WalletImpl(nettype);
+ WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds);
wallet->create(path, password, language);
return wallet;
}
-Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype)
+Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds)
{
- WalletImpl * wallet = new WalletImpl(nettype);
+ WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds);
wallet->open(path, password);
//Refresh addressBook
wallet->addressBook()->refresh();
@@ -87,9 +87,10 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path,
const std::string &password,
const std::string &mnemonic,
NetworkType nettype,
- uint64_t restoreHeight)
+ uint64_t restoreHeight,
+ uint64_t kdf_rounds)
{
- WalletImpl * wallet = new WalletImpl(nettype);
+ WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
@@ -104,9 +105,10 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
- const std::string &spendKeyString)
+ const std::string &spendKeyString,
+ uint64_t kdf_rounds)
{
- WalletImpl * wallet = new WalletImpl(nettype);
+ WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
@@ -119,9 +121,10 @@ Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path,
NetworkType nettype,
const std::string &deviceName,
uint64_t restoreHeight,
- const std::string &subaddressLookahead)
+ const std::string &subaddressLookahead,
+ uint64_t kdf_rounds)
{
- WalletImpl * wallet = new WalletImpl(nettype);
+ WalletImpl * wallet = new WalletImpl(nettype, false, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
@@ -159,9 +162,9 @@ bool WalletManagerImpl::walletExists(const std::string &path)
return false;
}
-bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const
+bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds) const
{
- return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default"));
+ return tools::wallet2::verify_password(keys_file_name, password, no_spend_key, hw::get_device("default"), kdf_rounds);
}
std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path)
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 656a7142c..8b1c8be7f 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -39,13 +39,14 @@ class WalletManagerImpl : public WalletManager
{
public:
Wallet * createWallet(const std::string &path, const std::string &password,
- const std::string &language, NetworkType nettype) override;
- Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) override;
+ 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;
virtual Wallet * recoveryWallet(const std::string &path,
const std::string &password,
const std::string &mnemonic,
NetworkType nettype,
- uint64_t restoreHeight) override;
+ uint64_t restoreHeight,
+ uint64_t kdf_rounds = 1) override;
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
@@ -53,7 +54,8 @@ public:
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
- const std::string &spendKeyString = "") override;
+ const std::string &spendKeyString = "",
+ uint64_t kdf_rounds = 1) override;
// next two methods are deprecated - use the above version which allow setting of a password
virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight) override;
// deprecated: use createWalletFromKeys(..., password, ...) instead
@@ -69,10 +71,11 @@ public:
NetworkType nettype,
const std::string &deviceName,
uint64_t restoreHeight = 0,
- const std::string &subaddressLookahead = "") override;
+ const std::string &subaddressLookahead = "",
+ uint64_t kdf_rounds = 1) 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) const override;
+ bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const override;
std::vector<std::string> findWallets(const std::string &path) override;
std::string errorString() const override;
void setDaemonAddress(const std::string &address) override;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 228261150..1efc2ef9c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -160,6 +160,7 @@ struct options {
return val;
}
};
+ const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -203,6 +204,8 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
const network_type nettype = testnet ? TESTNET : stagenet ? STAGENET : MAINNET;
const bool restricted = command_line::get_arg(vm, opts.restricted);
+ 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");
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
@@ -236,7 +239,7 @@ 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);
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, restricted));
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, restricted, kdf_rounds));
wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
@@ -647,7 +650,7 @@ const size_t MAX_SPLIT_ATTEMPTS = 30;
constexpr const std::chrono::seconds wallet2::rpc_timeout;
const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
-wallet2::wallet2(network_type nettype, bool restricted):
+wallet2::wallet2(network_type nettype, bool restricted, uint64_t kdf_rounds):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
m_run(true),
@@ -679,6 +682,7 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_ignore_fractional_outputs(true),
m_is_initialized(false),
m_restricted(restricted),
+ m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
@@ -723,6 +727,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.restricted);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
+ command_line::add_arg(desc_params, opts.kdf_rounds);
}
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -899,6 +904,14 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub
return hwdev.get_subaddress(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
+boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
+{
+ auto index = m_subaddresses.find(address.m_spend_public_key);
+ if (index == m_subaddresses.end())
+ return boost::none;
+ return index->second;
+}
+//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const
{
hw::device &hwdev = m_account.get_device();
@@ -1557,6 +1570,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
// We got a payment ID to go with this tx
LOG_PRINT_L2("Found encrypted payment ID: " << payment_id8);
+ MINFO("Consider using subaddresses instead of encrypted payment IDs");
if (tx_pub_key != null_pkey)
{
if (!m_account.get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_account.get_keys().m_view_secret_key))
@@ -1580,12 +1594,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
+ MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
}
}
- else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- {
- LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
- }
for (const auto& i : tx_money_got_in_outs)
{
@@ -1596,6 +1607,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
payment.m_timestamp = ts;
+ payment.m_coinbase = miner_tx;
payment.m_subaddr_index = i.first;
if (pool) {
emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen});
@@ -2549,7 +2561,7 @@ bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& rece
return ok;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
+bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution)
{
uint32_t rpc_version;
boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
@@ -2683,7 +2695,7 @@ void wallet2::detach_blockchain(uint64_t height)
bool wallet2::deinit()
{
m_is_initialized=false;
- m_keys_file_locker.reset();
+ unlock_keys_file();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -2846,19 +2858,19 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
// Encrypt the entire JSON object.
crypto::chacha_key key;
- crypto::generate_chacha_key(password.data(), password.size(), key);
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string cipher;
cipher.resize(account_data.size());
keys_file_data.iv = crypto::rand<crypto::chacha_iv>();
crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
keys_file_data.account_data = cipher;
- m_keys_file_locker.reset();
+ unlock_keys_file();
std::string buf;
r = ::serialization::dump_binary(keys_file_data, buf);
r = r && epee::file_io_utils::save_string_to_file(keys_file_name, buf); //and never touch wallet_keys_file again, only read
CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << keys_file_name);
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ lock_keys_file();
return true;
}
@@ -2880,7 +2892,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = ::serialization::parse_binary(buf, keys_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
crypto::chacha_key key;
- crypto::generate_chacha_key(password.data(), password.size(), key);
+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
@@ -3082,9 +3094,13 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
* can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
*
*/
-bool wallet2::verify_password(const epee::wipeable_string& password) const
+bool wallet2::verify_password(const epee::wipeable_string& password)
{
- return verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device());
+ // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
+ unlock_keys_file();
+ bool r = verify_password(m_keys_file, password, m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
+ lock_keys_file();
+ return r;
}
/*!
@@ -3100,7 +3116,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const
* can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
*
*/
-bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev)
+bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
{
rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
@@ -3112,7 +3128,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
r = ::serialization::parse_binary(buf, keys_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
crypto::chacha_key key;
- crypto::generate_chacha_key(password.data(), password.size(), key);
+ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
@@ -3980,7 +3996,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
{
hw::device &hwdev = m_account.get_device();
- return hwdev.generate_chacha_key(m_account.get_keys(), key);
+ return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
}
//----------------------------------------------------------------------------------------------------
void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
@@ -3991,17 +4007,17 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
boost::system::error_code e;
bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
- THROW_WALLET_EXCEPTION_IF(!m_keys_file_locker->locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
+ lock_keys_file();
+ THROW_WALLET_EXCEPTION_IF(!is_keys_file_locked(), error::wallet_internal_error, "internal error: \"" + m_keys_file + "\" is opened by another wallet program");
// this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
- m_keys_file_locker.reset();
+ unlock_keys_file();
if (!load_keys(m_keys_file, password))
{
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
}
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
- m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ lock_keys_file();
//keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem
@@ -6037,6 +6053,33 @@ bool wallet2::is_output_blackballed(const crypto::public_key &output) const
catch (const std::exception &e) { return false; }
}
+bool wallet2::lock_keys_file()
+{
+ if (m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already locked.");
+ return false;
+ }
+ m_keys_file_locker.reset(new tools::file_locker(m_keys_file));
+ return true;
+}
+
+bool wallet2::unlock_keys_file()
+{
+ if (!m_keys_file_locker)
+ {
+ MDEBUG(m_keys_file << " is already unlocked.");
+ return false;
+ }
+ m_keys_file_locker.reset();
+ return true;
+}
+
+bool wallet2::is_keys_file_locked() const
+{
+ return m_keys_file_locker->locked();
+}
+
bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
{
if (!unlocked) // don't add locked outs
@@ -6177,22 +6220,42 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
+ // if we have at least one rct out, get the distribution, or fall back to the previous system
+ uint64_t rct_start_height;
+ std::vector<uint64_t> rct_offsets;
+ bool has_rct = false;
+ for (size_t idx: selected_transfers)
+ if (m_transfers[idx].is_rct())
+ { has_rct = true; break; }
+ const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
+ if (has_rct_distribution)
+ {
+ // check we're clear enough of rct start, to avoid corner cases below
+ THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE,
+ error::get_output_distribution, "Not enough rct outputs");
+ }
+
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
+ // request histogram for all outputs, except 0 if we have the rct distribution
for(size_t idx: selected_transfers)
- req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
- std::sort(req_t.amounts.begin(), req_t.amounts.end());
- auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
- req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
- req_t.unlocked = true;
- req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
- THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
+ if (!m_transfers[idx].is_rct() || !has_rct_distribution)
+ req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
+ if (!req_t.amounts.empty())
+ {
+ std::sort(req_t.amounts.begin(), req_t.amounts.end());
+ auto end = std::unique(req_t.amounts.begin(), req_t.amounts.end());
+ req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
+ req_t.unlocked = true;
+ req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
+ }
// if we want to segregate fake outs pre or post fork, get distribution
std::unordered_map<uint64_t, std::pair<uint64_t, uint64_t>> segregation_limit;
@@ -6248,6 +6311,36 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
+ struct gamma_engine
+ {
+ typedef uint64_t result_type;
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
+ result_type operator()() { return crypto::rand<result_type>(); }
+ } engine;
+ static const double shape = 19.28/*16.94*/;
+ //static const double shape = m_testnet ? 17.02 : 17.28;
+ static const double scale = 1/1.61;
+ std::gamma_distribution<double> gamma(shape, scale);
+ auto pick_gamma = [&]()
+ {
+ double x = gamma(engine);
+ x = exp(x);
+ uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range
+ if (block_offset >= rct_offsets.size() - 1)
+ return std::numeric_limits<uint64_t>::max(); // bad pick
+ block_offset = rct_offsets.size() - 2 - block_offset;
+ THROW_WALLET_EXCEPTION_IF(block_offset >= rct_offsets.size() - 1, error::wallet_internal_error, "Bad offset calculation");
+ THROW_WALLET_EXCEPTION_IF(rct_offsets[block_offset + 1] < rct_offsets[block_offset],
+ error::get_output_distribution, "Decreasing offsets in rct distribution: " +
+ std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]) + ", " +
+ std::to_string(block_offset + 1) + ": " + std::to_string(rct_offsets[block_offset + 1]));
+ uint64_t n_rct = rct_offsets[block_offset + 1] - rct_offsets[block_offset];
+ if (n_rct == 0)
+ return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
+ return rct_offsets[block_offset] + crypto::rand<uint64_t>() % n_rct;
+ };
+
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
{
@@ -6258,6 +6351,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
size_t start = req.outputs.size();
+ bool use_histogram = amount != 0 || !has_rct_distribution;
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
uint64_t num_outs = 0, num_recent_outs = 0;
@@ -6313,26 +6407,41 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
num_post_fork_outs = num_outs - segregation_limit[amount].first;
}
- LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
- THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
- "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
- THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
- "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
+ if (use_histogram)
+ {
+ LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount));
+ THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
+ "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours");
+ THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error,
+ "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount));
+ }
+ else
+ {
+ // the base offset of the first rct output in the first unlocked block (or the one to be if there's none)
+ num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE];
+ LOG_PRINT_L1("" << num_outs << " unlocked rct outputs");
+ THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error,
+ "histogram reports no unlocked rct outputs, not even ours");
+ }
- // how many fake outs to draw on a pre-fork triangular distribution
+ // how many fake outs to draw on a pre-fork distribution
size_t pre_fork_outputs_count = requested_outputs_count * pre_fork_num_out_ratio;
size_t post_fork_outputs_count = requested_outputs_count * post_fork_num_out_ratio;
// how many fake outs to draw otherwise
size_t normal_output_count = requested_outputs_count - pre_fork_outputs_count - post_fork_outputs_count;
- // X% of those outs are to be taken from recent outputs
- size_t recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
- if (recent_outputs_count == 0)
- recent_outputs_count = 1; // ensure we have at least one, if possible
- if (recent_outputs_count > num_recent_outs)
- recent_outputs_count = num_recent_outs;
- if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
- --recent_outputs_count; // if the real out is recent, pick one less recent fake out
+ size_t recent_outputs_count = 0;
+ if (use_histogram)
+ {
+ // X% of those outs are to be taken from recent outputs
+ recent_outputs_count = normal_output_count * RECENT_OUTPUT_RATIO;
+ if (recent_outputs_count == 0)
+ recent_outputs_count = 1; // ensure we have at least one, if possible
+ if (recent_outputs_count > num_recent_outs)
+ recent_outputs_count = num_recent_outs;
+ if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0)
+ --recent_outputs_count; // if the real out is recent, pick one less recent fake out
+ }
LOG_PRINT_L1("Fake output makeup: " << requested_outputs_count << " requested: " << recent_outputs_count << " recent, " <<
pre_fork_outputs_count << " pre-fork, " << post_fork_outputs_count << " post-fork, " <<
(requested_outputs_count - recent_outputs_count - pre_fork_outputs_count - post_fork_outputs_count) << " full-chain");
@@ -6412,7 +6521,26 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t i;
const char *type = "";
- if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
+ if (amount == 0 && has_rct_distribution)
+ {
+ // gamma distribution
+ if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
+ {
+ do i = pick_gamma(); while (i >= segregation_limit[amount].first);
+ type = "pre-fork gamma";
+ }
+ else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
+ {
+ do i = pick_gamma(); while (i < segregation_limit[amount].first || i >= num_outs);
+ type = "post-fork gamma";
+ }
+ else
+ {
+ do i = pick_gamma(); while (i >= num_outs);
+ type = "gamma";
+ }
+ }
+ else if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
{
// triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
@@ -6477,7 +6605,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get the keys for those
m_daemon_rpc_mutex.lock();
- r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
+ bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@@ -7437,6 +7565,7 @@ void wallet2::light_wallet_get_address_txs()
payment.m_block_height = t.height;
payment.m_unlock_time = t.unlock_time;
payment.m_timestamp = t.timestamp;
+ payment.m_coinbase = t.coinbase;
if (t.mempool) {
if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) {
@@ -7705,6 +7834,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through
+ const uint64_t min_fee = (fee_multiplier * fee_per_kb * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices)
@@ -7712,10 +7842,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
balance_subtotal += balance_per_subaddr[index_minor];
unlocked_balance_subtotal += unlocked_balance_per_subaddr[index_minor];
}
- THROW_WALLET_EXCEPTION_IF(needed_money > balance_subtotal, error::not_enough_money,
+ THROW_WALLET_EXCEPTION_IF(needed_money + min_fee > balance_subtotal, error::not_enough_money,
balance_subtotal, needed_money, 0);
// first check overall balance is enough, then unlocked one, so we throw distinct exceptions
- THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance_subtotal, error::not_enough_unlocked_money,
+ THROW_WALLET_EXCEPTION_IF(needed_money + min_fee > unlocked_balance_subtotal, error::not_enough_unlocked_money,
unlocked_balance_subtotal, needed_money, 0);
for (uint32_t i : subaddr_indices)
@@ -9918,6 +10048,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
+ std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
+
+ for (const transfer_details &td: m_transfers)
+ {
+ for (const cryptonote::txin_v& in : td.m_tx.vin)
+ {
+ if (in.type() == typeid(cryptonote::txin_to_key))
+ spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
+ }
+ }
+
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
@@ -9931,28 +10072,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
{
- bool is_spent_tx_found = false;
- for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
- {
- bool is_spent_tx = false;
- for(const cryptonote::txin_v& in : it->m_tx.vin)
- {
- if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
- {
- is_spent_tx = true;
- break;
- }
- }
- if (is_spent_tx)
- {
- is_spent_tx_found = true;
- spent_txids.insert(it->m_txid);
- break;
- }
- }
-
- if (!is_spent_tx_found)
+ const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
+ if (skii == spent_key_images.end())
swept_transfers.push_back(i);
+ else
+ spent_txids.insert(skii->second);
}
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
@@ -10553,7 +10677,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const
{
crypto::chacha_key key;
- crypto::generate_chacha_key(&skey, sizeof(skey), key);
+ crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
std::string ciphertext;
crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
@@ -10583,7 +10707,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
error::wallet_internal_error, "Unexpected ciphertext size");
crypto::chacha_key key;
- crypto::generate_chacha_key(&skey, sizeof(skey), key);
+ crypto::generate_chacha_key(&skey, sizeof(skey), key, m_kdf_rounds);
const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
std::string plaintext;
plaintext.resize(ciphertext.size() - prefix_size);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 7c74c5803..6e6c1a6ee 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -165,9 +165,9 @@ namespace tools
//! Just parses variables.
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev);
+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
- wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false);
+ wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false, uint64_t kdf_rounds = 1);
~wallet2();
struct multisig_info
@@ -260,12 +260,12 @@ namespace tools
uint64_t m_block_height;
uint64_t m_unlock_time;
uint64_t m_timestamp;
+ bool m_coinbase;
cryptonote::subaddress_index m_subaddr_index;
};
struct address_tx : payment_details
{
- bool m_coinbase;
bool m_mempool;
bool m_incoming;
};
@@ -609,7 +609,7 @@ namespace tools
/*!
* \brief verifies given password is correct for default wallet keys file
*/
- bool verify_password(const epee::wipeable_string& password) const;
+ bool verify_password(const epee::wipeable_string& password);
cryptonote::account_base& get_account(){return m_account;}
const cryptonote::account_base& get_account()const{return m_account;}
@@ -659,6 +659,7 @@ namespace tools
// Subaddress scheme
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
+ boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
@@ -1147,6 +1148,9 @@ namespace tools
bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const;
+ bool lock_keys_file();
+ bool unlock_keys_file();
+ bool is_keys_file_locked() const;
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1213,7 +1217,7 @@ namespace tools
void cache_ringdb_key();
void clear_ringdb_key();
- bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
+ bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
uint64_t get_segregation_fork_height() const;
@@ -1256,6 +1260,7 @@ namespace tools
bool m_key_on_device;
cryptonote::network_type m_nettype;
bool m_restricted;
+ uint64_t m_kdf_rounds;
std::string seed_language; /*!< Language of the mnemonics (seed). */
bool is_old_file_format; /*!< Whether the wallet file is of an old file format */
bool m_watch_only; /*!< no spend key */
@@ -1321,7 +1326,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 9)
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)
-BOOST_CLASS_VERSION(tools::wallet2::payment_details, 3)
+BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
@@ -1588,16 +1593,24 @@ namespace boost
a & x.m_timestamp;
if (ver < 2)
{
+ x.m_coinbase = false;
x.m_subaddr_index = {};
return;
}
a & x.m_subaddr_index;
if (ver < 3)
{
+ x.m_coinbase = false;
x.m_fee = 0;
return;
}
a & x.m_fee;
+ if (ver < 4)
+ {
+ x.m_coinbase = false;
+ return;
+ }
+ a & x.m_coinbase;
}
template <class Archive>
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index a629eb506..95a4e0ad6 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -109,6 +109,9 @@ namespace wallet_args
std::string lang = i18n_get_language();
tools::on_startup();
+#ifdef NDEBUG
+ tools::disable_core_dumps();
+#endif
tools::set_strict_default_file_permissions(true);
epee::string_tools::set_module_name_and_folder(argv[0]);
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index b9cf99635..c6a81d886 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -270,7 +270,7 @@ namespace tools
entry.unlock_time = pd.m_unlock_time;
entry.fee = pd.m_fee;
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
- entry.type = "in";
+ entry.type = pd.m_coinbase ? "block" : "in";
entry.subaddr_index = 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());
@@ -355,14 +355,20 @@ namespace tools
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index);
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
- for (const auto& i : balance_per_subaddress)
+ std::set<uint32_t> address_indices = req.address_indices;
+ if (address_indices.empty())
+ {
+ for (const auto& i : balance_per_subaddress)
+ address_indices.insert(i.first);
+ }
+ for (uint32_t i : address_indices)
{
wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
- info.address_index = i.first;
+ info.address_index = i;
cryptonote::subaddress_index index = {req.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
- info.balance = i.second;
- info.unlocked_balance = unlocked_balance_per_subaddress[i.first];
+ info.balance = balance_per_subaddress[i];
+ info.unlocked_balance = unlocked_balance_per_subaddress[i];
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.push_back(info);
@@ -416,6 +422,27 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+ auto index = m_wallet->get_subaddress_index(info.address);
+ if (!index)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Address doesn't belong to the wallet";
+ return false;
+ }
+ res.index = *index;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -1255,7 +1282,39 @@ namespace tools
}
}
- res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id);
+ if (req.standard_address.empty())
+ {
+ res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id);
+ }
+ else
+ {
+ cryptonote::address_parse_info info;
+ if(!get_account_address_from_str(info, m_wallet->nettype(), req.standard_address))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Invalid address";
+ return false;
+ }
+ if (info.is_subaddress)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Subaddress shouldn't be used";
+ return false;
+ }
+ if (info.has_payment_id)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ er.message = "Already integrated address";
+ return false;
+ }
+ if (req.payment_id.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Payment ID shouldn't be left unspecified";
+ return false;
+ }
+ res.integrated_address = get_account_integrated_address_as_str(m_wallet->nettype(), info.address, payment_id);
+ }
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 9cb67c593..b7e545c53 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -69,6 +69,7 @@ namespace tools
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
+ MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
@@ -146,6 +147,7 @@ namespace tools
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
+ bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er);
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 1bd572add..48d881c4c 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 1
+#define WALLET_RPC_VERSION_MINOR 2
#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
@@ -62,8 +62,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
+ std::set<uint32_t> address_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
+ KV_SERIALIZE(address_indices)
END_KV_SERIALIZE_MAP()
};
@@ -141,6 +143,25 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_ADDRESS_INDEX
+ {
+ struct request
+ {
+ std::string address;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ cryptonote::subaddress_index index;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(index)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_CREATE_ADDRESS
{
struct request
@@ -914,9 +935,11 @@ namespace wallet_rpc
{
struct request
{
+ std::string standard_address;
std::string payment_id;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(standard_address)
KV_SERIALIZE(payment_id)
END_KV_SERIALIZE_MAP()
};