aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp4
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp13
-rw-r--r--src/common/stack_trace.cpp11
-rw-r--r--src/common/util.cpp16
-rw-r--r--src/common/util.h2
-rw-r--r--src/crypto/chacha.h12
-rw-r--r--src/crypto/crypto.cpp3
-rw-r--r--src/crypto/crypto.h5
-rw-r--r--src/cryptonote_core/blockchain.cpp34
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl15
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/device/device.hpp2
-rw-r--r--src/device/device_default.cpp4
-rw-r--r--src/device/device_default.hpp2
-rw-r--r--src/device/device_ledger.cpp6
-rw-r--r--src/device/device_ledger.hpp2
-rw-r--r--src/mnemonics/electrum-words.cpp62
-rw-r--r--src/p2p/net_node.inl6
-rw-r--r--src/simplewallet/simplewallet.cpp114
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/api/utils.cpp3
-rw-r--r--src/wallet/api/wallet.cpp4
-rw-r--r--src/wallet/api/wallet.h2
-rw-r--r--src/wallet/api/wallet2_api.h20
-rw-r--r--src/wallet/api/wallet_manager.cpp27
-rw-r--r--src/wallet/api/wallet_manager.h15
-rw-r--r--src/wallet/wallet2.cpp273
-rw-r--r--src/wallet/wallet2.h21
-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
32 files changed, 573 insertions, 209 deletions
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index a6eb3d880..627038ca7 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -2985,10 +2985,10 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions)
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
if ((batch_transactions) && (m_batch_transactions))
{
- LOG_PRINT_L0("WARNING: batch transaction mode already enabled, but asked to enable batch mode");
+ MINFO("batch transaction mode already enabled, but asked to enable batch mode");
}
m_batch_transactions = batch_transactions;
- LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled"));
+ MINFO("batch transactions " << (m_batch_transactions ? "enabled" : "disabled"));
}
// return true if we started the txn, false if already started
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 52d2938cd..65b6384dc 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -404,6 +404,11 @@ int main(int argc, char* argv[])
cryptonote::block b = core_storage[0]->get_db().get_block_from_height(0);
tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_block_hash(b)));
+ bool stop_requested = false;
+ tools::signal_handler::install([&stop_requested](int type) {
+ stop_requested = true;
+ });
+
for (size_t n = 0; n < inputs.size(); ++n)
{
const std::string canonical = boost::filesystem::canonical(inputs[n]).string();
@@ -489,9 +494,17 @@ int main(int argc, char* argv[])
}
state.relative_rings[txin.k_image] = new_ring;
}
+ if (stop_requested)
+ {
+ MINFO("Stopping scan, secondary passes will still happen...");
+ return false;
+ }
return true;
});
+ LOG_PRINT_L0("blockchain from " << inputs[n] << " processed still height " << start_idx);
state.processed_heights[canonical] = start_idx;
+ if (stop_requested)
+ break;
}
while (!newly_spent.empty())
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp
index d6dc4d7cc..141621427 100644
--- a/src/common/stack_trace.cpp
+++ b/src/common/stack_trace.cpp
@@ -49,7 +49,16 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "stacktrace"
-#define ST_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x
+#define ST_LOG(x) \
+ do { \
+ auto elpp = ELPP; \
+ if (elpp) { \
+ CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x; \
+ } \
+ else { \
+ std::cout << x << std::endl; \
+ } \
+ } while(0)
// from https://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c
diff --git a/src/common/util.cpp b/src/common/util.cpp
index f644c573c..5e0d2726e 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -37,6 +37,7 @@
#ifdef __GLIBC__
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/resource.h>
#include <ustat.h>
#include <unistd.h>
#include <dirent.h>
@@ -682,6 +683,21 @@ std::string get_nix_version_display_string()
static void setup_crash_dump() {}
#endif
+ bool disable_core_dumps()
+ {
+#ifdef __GLIBC__
+ // disable core dumps in release mode
+ struct rlimit rlimit;
+ rlimit.rlim_cur = rlimit.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &rlimit))
+ {
+ MWARNING("Failed to disable core dumps");
+ return false;
+ }
+#endif
+ return true;
+ }
+
bool on_startup()
{
mlog_configure("", true);
diff --git a/src/common/util.h b/src/common/util.h
index 6ec901e7f..8815232e2 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -149,6 +149,8 @@ namespace tools
bool sanitize_locale();
+ bool disable_core_dumps();
+
bool on_startup();
/*! \brief Defines a signal handler for win32 and *nix
diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h
index 2b3ed8043..1dc270faf 100644
--- a/src/crypto/chacha.h
+++ b/src/crypto/chacha.h
@@ -69,22 +69,26 @@ namespace crypto {
chacha20(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
}
- inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) {
+ inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
+ for (uint64_t n = 1; n < kdf_rounds; ++n)
+ crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
}
- inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) {
+ inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
+ for (uint64_t n = 1; n < kdf_rounds; ++n)
+ crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
memcpy(&unwrap(key), pwd_hash.data(), sizeof(key));
}
- inline void generate_chacha_key(std::string password, chacha_key& key) {
- return generate_chacha_key(password.data(), password.size(), key);
+ inline void generate_chacha_key(std::string password, chacha_key& key, uint64_t kdf_rounds) {
+ return generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
}
}
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index 0c019938d..4243c71fd 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -70,6 +70,9 @@ namespace crypto {
#include "random.h"
}
+ const crypto::public_key null_pkey = crypto::public_key{};
+ const crypto::secret_key null_skey = crypto::secret_key{};
+
static inline unsigned char *operator &(ec_point &point) {
return &reinterpret_cast<unsigned char &>(point);
}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 449af8f6d..a2d61b04e 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -34,7 +34,6 @@
#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
-#include <boost/utility/value_init.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <vector>
@@ -278,8 +277,8 @@ namespace crypto {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
- const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>();
- const static crypto::secret_key null_skey = boost::value_initialized<crypto::secret_key>();
+ const extern crypto::public_key null_pkey;
+ const extern crypto::secret_key null_skey;
}
CRYPTO_MAKE_HASHABLE(public_key)
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index dad30906e..87ef47c11 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -4042,6 +4042,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
TIME_MEASURE_START(prepare);
bool stop_batch;
uint64_t bytes = 0;
+ size_t total_txs = 0;
// Order of locking must be:
// m_incoming_tx_lock (optional)
@@ -4070,6 +4071,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
{
bytes += tx_blob.size();
}
+ total_txs += entry.txs.size();
}
while (!(stop_batch = m_db->batch_start(blocks_entry.size(), bytes))) {
m_blockchain_lock.unlock();
@@ -4129,7 +4131,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(block);
+ blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
}
@@ -4150,7 +4152,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
break;
}
- blocks[i].push_back(block);
+ blocks[i].push_back(std::move(block));
std::advance(it, 1);
}
@@ -4206,6 +4208,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
std::map<uint64_t, std::vector<uint64_t>> offset_map;
// [output] stores all output_data_t for each absolute_offset
std::map<uint64_t, std::vector<output_data_t>> tx_map;
+ std::vector<std::pair<cryptonote::transaction, crypto::hash>> txes(total_txs);
#define SCAN_TABLE_QUIT(m) \
do { \
@@ -4215,6 +4218,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
+ size_t tx_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -4222,12 +4226,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (const auto &tx_blob : entry.txs)
{
- crypto::hash tx_hash = null_hash;
- crypto::hash tx_prefix_hash = null_hash;
- transaction tx;
+ if (tx_index >= txes.size())
+ SCAN_TABLE_QUIT("tx_index is out of sync");
+ transaction &tx = txes[tx_index].first;
+ crypto::hash &tx_prefix_hash = txes[tx_index].second;
+ ++tx_index;
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
+ if (!parse_and_validate_tx_base_from_blob(tx_blob, tx))
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
+ cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
auto its = m_scan_table.find(tx_prefix_hash);
if (its != m_scan_table.end())
@@ -4313,9 +4320,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
- int total_txs = 0;
-
// now generate a table for each tx_prefix and k_image hashes
+ tx_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -4323,14 +4329,12 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
for (const auto &tx_blob : entry.txs)
{
- crypto::hash tx_hash = null_hash;
- crypto::hash tx_prefix_hash = null_hash;
- transaction tx;
-
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash))
- SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
+ if (tx_index >= txes.size())
+ SCAN_TABLE_QUIT("tx_index is out of sync");
+ const transaction &tx = txes[tx_index].first;
+ const crypto::hash &tx_prefix_hash = txes[tx_index].second;
+ ++tx_index;
- ++total_txs;
auto its = m_scan_table.find(tx_prefix_hash);
if (its == m_scan_table.end())
SCAN_TABLE_QUIT("Tx not found on scan table from incoming blocks.");
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 56aa1dc06..a931d3b57 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -1168,8 +1168,20 @@ skip:
+ " blocks/sec), " + std::to_string(m_block_queue.get_data_size() / 1048576.f) + " MB queued";
if (ELPP->vRegistry()->allowed(el::Level::Debug, "sync-info"))
timing_message += std::string(": ") + m_block_queue.get_overview();
- MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
+ if(m_core.get_target_blockchain_height() == 0){
+ MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
+ } else {
+ const int completition_percent = (m_core.get_current_blockchain_height() * 100 / m_core.get_target_blockchain_height());
+ if(completition_percent < 99) {//printing completion percent only if % is < of 99 cause for 99 >= this is useless
+ MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
+ << " (" << completition_percent << "% " << (m_core.get_target_blockchain_height() - m_core.get_current_blockchain_height())
+ << " blocks remaining)" << timing_message);
+ } else {
+ MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
+ << timing_message);
+ }
+ }
}
}
}
@@ -1753,3 +1765,4 @@ skip:
m_core.stop();
}
} // namespace
+
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index e2b42c806..45ba81e16 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -557,7 +557,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
if (!first)
std::cout << std::endl;
std::cout
- << "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty
+ << "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
diff --git a/src/device/device.hpp b/src/device/device.hpp
index 9df0cb39d..c21456daf 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -125,7 +125,7 @@ namespace hw {
/* ======================================================================= */
virtual bool get_public_address(cryptonote::account_public_address &pubkey) = 0;
virtual bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) = 0;
- virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) = 0;
+ virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) = 0;
/* ======================================================================= */
/* SUB ADDRESS */
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index 0071f7d4f..bf14813ea 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -100,14 +100,14 @@ namespace hw {
/* WALLET & ADDRESS */
/* ======================================================================= */
- bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) {
+ bool device_default::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) {
const crypto::secret_key &view_key = keys.m_view_secret_key;
const crypto::secret_key &spend_key = keys.m_spend_secret_key;
tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1> data;
memcpy(data.data(), &view_key, sizeof(view_key));
memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
- crypto::generate_chacha_key(data.data(), sizeof(data), key);
+ crypto::generate_chacha_key(data.data(), sizeof(data), key, kdf_rounds);
return true;
}
bool device_default::get_public_address(cryptonote::account_public_address &pubkey) {
diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp
index 771fbba72..8d841d9de 100644
--- a/src/device/device_default.hpp
+++ b/src/device/device_default.hpp
@@ -73,7 +73,7 @@ namespace hw {
/* ======================================================================= */
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
- bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) override;
+ bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override;
/* ======================================================================= */
/* SUB ADDRESS */
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index f7bf58531..7a34dad5e 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -531,20 +531,20 @@ namespace hw {
return true;
}
- bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) {
+ bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) {
AUTO_LOCK_CMD();
#ifdef DEBUG_HWDEVICE
crypto::chacha_key key_x;
cryptonote::account_keys keys_x = hw::ledger::decrypt(keys);
- this->controle_device->generate_chacha_key(keys_x, key_x);
+ this->controle_device->generate_chacha_key(keys_x, key_x, kdf_rounds);
#endif
send_simple(INS_GET_CHACHA8_PREKEY);
char prekey[200];
memmove(prekey, &this->buffer_recv[0], 200);
- crypto::generate_chacha_key_prehashed(&prekey[0], sizeof(prekey), key);
+ crypto::generate_chacha_key_prehashed(&prekey[0], sizeof(prekey), key, kdf_rounds);
#ifdef DEBUG_HWDEVICE
hw::ledger::check32("generate_chacha_key_prehashed", "key", (char*)key_x.data(), (char*)key.data());
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index c30a38aca..e6c6e5b52 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -156,7 +156,7 @@ namespace hw {
/* ======================================================================= */
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
- bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key) override;
+ bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override;
/* ======================================================================= */
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 7dd09ecb9..19a9c26bb 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -70,6 +70,14 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "mnemonic"
+namespace crypto
+{
+ namespace ElectrumWords
+ {
+ std::vector<const Language::Base*> get_language_list();
+ }
+}
+
namespace
{
uint32_t create_checksum_index(const std::vector<std::string> &word_list,
@@ -376,56 +384,14 @@ namespace crypto
if (len % 4 != 0 || len == 0) return false;
- Language::Base *language;
- if (language_name == "English")
- {
- language = Language::Singleton<Language::English>::instance();
- }
- else if (language_name == "Nederlands")
- {
- language = Language::Singleton<Language::Dutch>::instance();
- }
- else if (language_name == "Français")
- {
- language = Language::Singleton<Language::French>::instance();
- }
- else if (language_name == "Español")
- {
- language = Language::Singleton<Language::Spanish>::instance();
- }
- else if (language_name == "Português")
+ const Language::Base *language = NULL;
+ const std::vector<const Language::Base*> language_list = crypto::ElectrumWords::get_language_list();
+ for (const Language::Base *l: language_list)
{
- language = Language::Singleton<Language::Portuguese>::instance();
+ if (language_name == l->get_language_name() || language_name == l->get_english_language_name())
+ language = l;
}
- else if (language_name == "日本語")
- {
- language = Language::Singleton<Language::Japanese>::instance();
- }
- else if (language_name == "Italiano")
- {
- language = Language::Singleton<Language::Italian>::instance();
- }
- else if (language_name == "Deutsch")
- {
- language = Language::Singleton<Language::German>::instance();
- }
- else if (language_name == "русский язык")
- {
- language = Language::Singleton<Language::Russian>::instance();
- }
- else if (language_name == "简体中文 (中国)")
- {
- language = Language::Singleton<Language::Chinese_Simplified>::instance();
- }
- else if (language_name == "Esperanto")
- {
- language = Language::Singleton<Language::Esperanto>::instance();
- }
- else if (language_name == "Lojban")
- {
- language = Language::Singleton<Language::Lojban>::instance();
- }
- else
+ if (!language)
{
return false;
}
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 9390626a8..74924e4f4 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -937,7 +937,7 @@ namespace nodetool
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
epee::string_tools::num_to_string_fast(ipv4.port()),
m_config.m_net_config.connection_timeout,
- con);
+ con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
if(!res)
{
@@ -1002,7 +1002,7 @@ namespace nodetool
bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
epee::string_tools::num_to_string_fast(ipv4.port()),
m_config.m_net_config.connection_timeout,
- con);
+ con, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
if (!res) {
bool is_priority = is_priority_node(na);
@@ -1617,7 +1617,7 @@ namespace nodetool
return false;
}
return true;
- });
+ }, m_bind_ip.empty() ? "0.0.0.0" : m_bind_ip);
if(!r)
{
LOG_WARNING_CC(context, "Failed to call connect_async, network error.");
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 775b7c359..ff81aea62 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -628,7 +628,7 @@ bool simple_wallet::print_seed(bool encrypted)
epee::wipeable_string seed_pass;
if (encrypted)
{
- auto pwd_container = password_prompter(tr("Enter optional seed encryption passphrase, empty to see raw seed"), true);
+ auto pwd_container = password_prompter(tr("Enter optional seed offset passphrase, empty to see raw seed"), true);
if (std::cin.eof() || !pwd_container)
return true;
seed_pass = pwd_container->password();
@@ -1825,6 +1825,7 @@ bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = st
if (pwd_container)
{
parse_bool_and_use(args[1], [&](bool auto_refresh) {
+ m_auto_refresh_enabled.store(false, std::memory_order_relaxed);
m_wallet->auto_refresh(auto_refresh);
m_idle_mutex.lock();
m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed);
@@ -2302,6 +2303,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::get_tx_key, this, _1),
tr("get_tx_key <txid>"),
tr("Get the transaction key (r) for a given <txid>."));
+ m_cmd_binder.set_handler("set_tx_key",
+ boost::bind(&simple_wallet::set_tx_key, this, _1),
+ tr("set_tx_key <txid> <tx_key>"),
+ tr("Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet."));
m_cmd_binder.set_handler("check_tx_key",
boost::bind(&simple_wallet::check_tx_key, this, _1),
tr("check_tx_key <txid> <txkey> <address>"),
@@ -2334,7 +2339,7 @@ simple_wallet::simple_wallet()
tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>."));
m_cmd_binder.set_handler("show_transfers",
boost::bind(&simple_wallet::show_transfers, this, _1),
- tr("show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
+ tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
tr("Show the incoming/outgoing transfers within an optional height range."));
m_cmd_binder.set_handler("unspent_outputs",
boost::bind(&simple_wallet::unspent_outputs, this, _1),
@@ -2793,7 +2798,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
}
- auto pwd_container = password_prompter(tr("Enter seed encryption passphrase, empty if none"), false);
+ auto pwd_container = password_prompter(tr("Enter seed offset passphrase, empty if none"), false);
if (std::cin.eof() || !pwd_container)
return false;
epee::wipeable_string seed_pass = pwd_container->password();
@@ -3348,14 +3353,16 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
*/
std::string simple_wallet::get_mnemonic_language()
{
- std::vector<std::string> language_list;
+ std::vector<std::string> language_list_self, language_list_english;
+ const std::vector<std::string> &language_list = m_use_english_language_names ? language_list_english : language_list_self;
std::string language_choice;
int language_number = -1;
- crypto::ElectrumWords::get_language_list(language_list, m_use_english_language_names);
+ crypto::ElectrumWords::get_language_list(language_list_self, false);
+ crypto::ElectrumWords::get_language_list(language_list_english, true);
std::cout << tr("List of available languages for your wallet's seed:") << std::endl;
std::cout << tr("If your display freezes, exit blind with ^C, then run again with --use-english-language-names") << std::endl;
int ii;
- std::vector<std::string>::iterator it;
+ std::vector<std::string>::const_iterator it;
for (it = language_list.begin(), ii = 0; it != language_list.end(); it++, ii++)
{
std::cout << ii << " : " << *it << std::endl;
@@ -3379,7 +3386,7 @@ std::string simple_wallet::get_mnemonic_language()
fail_msg_writer() << tr("invalid language choice entered. Please try again.\n");
}
}
- return language_list[language_number];
+ return language_list_self[language_number];
}
//----------------------------------------------------------------------------------------------------
boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const
@@ -5828,6 +5835,64 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
}
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::set_tx_key(const std::vector<std::string> &args_)
+{
+ std::vector<std::string> local_args = args_;
+
+ if(local_args.size() != 2) {
+ fail_msg_writer() << tr("usage: set_tx_key <txid> <tx_key>");
+ return true;
+ }
+
+ crypto::hash txid;
+ if (!epee::string_tools::hex_to_pod(local_args[0], txid))
+ {
+ fail_msg_writer() << tr("failed to parse txid");
+ return true;
+ }
+
+ crypto::secret_key tx_key;
+ std::vector<crypto::secret_key> additional_tx_keys;
+ try
+ {
+ if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), tx_key))
+ {
+ fail_msg_writer() << tr("failed to parse tx_key");
+ return true;
+ }
+ while(true)
+ {
+ local_args[1] = local_args[1].substr(64);
+ if (local_args[1].empty())
+ break;
+ additional_tx_keys.resize(additional_tx_keys.size() + 1);
+ if (!epee::string_tools::hex_to_pod(local_args[1].substr(0, 64), additional_tx_keys.back()))
+ {
+ fail_msg_writer() << tr("failed to parse tx_key");
+ return true;
+ }
+ }
+ }
+ catch (const std::out_of_range &e)
+ {
+ fail_msg_writer() << tr("failed to parse tx_key");
+ return true;
+ }
+
+ LOCK_IDLE_SCOPE();
+
+ try
+ {
+ m_wallet->set_tx_key(txid, tx_key, additional_tx_keys);
+ success_msg_writer() << tr("Tx key successfully stored.");
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Failed to store tx key: ") << e.what();
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
{
if (m_wallet->key_on_device())
@@ -6280,12 +6345,13 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
bool pending = true;
bool failed = true;
bool pool = true;
+ bool coinbase = true;
uint64_t min_height = 0;
uint64_t max_height = (uint64_t)-1;
boost::optional<uint32_t> subaddr_index;
if(local_args.size() > 4) {
- fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
+ fail_msg_writer() << tr("usage: show_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]");
return true;
}
@@ -6298,19 +6364,24 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
local_args.erase(local_args.begin());
}
else if (local_args[0] == "out" || local_args[0] == "outgoing") {
- in = pool = false;
+ in = pool = coinbase = false;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "pending") {
- in = out = failed = false;
+ in = out = failed = coinbase = false;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "failed") {
- in = out = pending = pool = false;
+ in = out = pending = pool = coinbase = false;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "pool") {
- in = out = pending = failed = false;
+ in = out = pending = failed = coinbase = false;
+ local_args.erase(local_args.begin());
+ }
+ else if (local_args[0] == "coinbase") {
+ in = out = pending = failed = pool = false;
+ coinbase = true;
local_args.erase(local_args.begin());
}
else if (local_args[0] == "all" || local_args[0] == "both") {
@@ -6351,20 +6422,23 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
local_args.erase(local_args.begin());
}
- std::multimap<uint64_t, std::pair<bool,std::string>> output;
+ std::multimap<uint64_t, std::tuple<epee::console_colors, std::string, std::string>> output;
PAUSE_READLINE();
- if (in) {
+ if (in || coinbase) {
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
const tools::wallet2::payment_details &pd = i->second;
+ if (!pd.m_coinbase && !in)
+ continue;
std::string payment_id = string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
- output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str())));
+ const std::string type = pd.m_coinbase ? tr("block") : tr("in");
+ output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str())));
}
}
@@ -6397,15 +6471,15 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(i->first);
- output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
+ output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
}
}
// print in and out sorted by height
- for (std::map<uint64_t, std::pair<bool, std::string>>::const_iterator i = output.begin(); i != output.end(); ++i) {
- message_writer(i->second.first ? console_color_green : console_color_magenta, false) <<
+ for (std::multimap<uint64_t, std::tuple<epee::console_colors, std::string, std::string>>::const_iterator i = output.begin(); i != output.end(); ++i) {
+ message_writer(std::get<0>(i->second), false) <<
boost::format("%8.8llu %6.6s %s") %
- ((unsigned long long)i->first) % (i->second.first ? tr("in") : tr("out")) % i->second.second;
+ ((unsigned long long)i->first) % std::get<1>(i->second) % std::get<2>(i->second);
}
if (pool) {
@@ -6464,7 +6538,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
auto local_args = args_;
std::set<uint32_t> subaddr_indices;
- if (local_args.size() > 0 && local_args[0].substr(0, 6) != "index=")
+ if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index b8f7bb84b..e08f31607 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -177,6 +177,7 @@ namespace cryptonote
bool rescan_spent(const std::vector<std::string> &args);
bool set_log(const std::vector<std::string> &args);
bool get_tx_key(const std::vector<std::string> &args);
+ bool set_tx_key(const std::vector<std::string> &args);
bool check_tx_key(const std::vector<std::string> &args);
bool get_tx_proof(const std::vector<std::string> &args);
bool check_tx_proof(const std::vector<std::string> &args);
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 680da26ce..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);
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 58be686fc..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);
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 0cd0ff5cf..5a52c6b17 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -920,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);
@@ -933,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);
@@ -948,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
{
@@ -983,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,
@@ -992,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,
@@ -1043,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,
@@ -1050,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
@@ -1075,13 +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 ed0964004..98412d08e 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();
@@ -1594,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});
@@ -2547,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);
@@ -2844,7 +2858,7 @@ 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>();
@@ -2878,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]);
@@ -3084,7 +3098,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password)
{
// 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());
+ 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;
}
@@ -3102,7 +3116,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password)
* 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;
@@ -3114,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]);
@@ -3982,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)
@@ -6206,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;
@@ -6277,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)
{
@@ -6287,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;
@@ -6342,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");
@@ -6441,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);
@@ -6506,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");
@@ -7466,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()) {
@@ -7734,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)
@@ -7741,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)
@@ -8612,6 +8713,54 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
return true;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys)
+{
+ // fetch tx from daemon and check if secret keys agree with corresponding public keys
+ COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = false;
+ COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
+ bool r;
+ {
+ const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ }
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd), error::wallet_internal_error, "failed to parse tx from hexstr");
+ cryptonote::transaction tx;
+ crypto::hash tx_hash, tx_prefix_hash;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "txid mismatch");
+ std::vector<tx_extra_field> tx_extra_fields;
+ THROW_WALLET_EXCEPTION_IF(!parse_tx_extra(tx.extra, tx_extra_fields), error::wallet_internal_error, "Transaction extra has unsupported format");
+ tx_extra_pub_key pub_key_field;
+ bool found = false;
+ size_t index = 0;
+ while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, index++))
+ {
+ crypto::public_key calculated_pub_key;
+ crypto::secret_key_to_public_key(tx_key, calculated_pub_key);
+ if (calculated_pub_key == pub_key_field.pub_key)
+ {
+ found = true;
+ break;
+ }
+ }
+ THROW_WALLET_EXCEPTION_IF(!found, error::wallet_internal_error, "Given tx secret key doesn't agree with the tx public key in the blockchain");
+ tx_extra_additional_pub_keys additional_tx_pub_keys;
+ find_tx_extra_field_by_type(tx_extra_fields, additional_tx_pub_keys);
+ THROW_WALLET_EXCEPTION_IF(additional_tx_keys.size() != additional_tx_pub_keys.data.size(), error::wallet_internal_error, "The number of additional tx secret keys doesn't agree with the number of additional tx public keys in the blockchain" );
+ m_tx_keys.insert(std::make_pair(txid, tx_key));
+ m_additional_tx_keys.insert(std::make_pair(txid, additional_tx_keys));
+}
+//----------------------------------------------------------------------------------------------------
std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string &message)
{
THROW_WALLET_EXCEPTION_IF(m_watch_only, error::wallet_internal_error,
@@ -9899,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];
@@ -9912,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");
@@ -10171,18 +10314,20 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
+ THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
+ error::wallet_internal_error, "Unsupported output type");
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_partial = false;
- THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key,
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i));
m_key_images[td.m_key_image] = m_transfers.size();
m_pub_keys[td.get_public_key()] = m_transfers.size();
- m_transfers.push_back(td);
+ m_transfers.push_back(std::move(td));
}
return m_transfers.size();
@@ -10533,7 +10678,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));
@@ -10563,7 +10708,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 220eca8a8..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;
};
@@ -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;
@@ -931,6 +932,7 @@ namespace tools
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
+ void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations);
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message);
@@ -1215,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;
@@ -1258,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 */
@@ -1323,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)
@@ -1590,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()
};