aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.cpp10
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp2
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp2
-rw-r--r--src/checkpoints/checkpoints.cpp10
-rw-r--r--src/checkpoints/checkpoints.h4
-rw-r--r--src/common/download.cpp61
-rw-r--r--src/common/stack_trace.cpp3
-rw-r--r--src/common/updates.cpp2
-rw-r--r--src/common/util.cpp14
-rw-r--r--src/crypto/CMakeLists.txt4
-rw-r--r--src/crypto/chacha.c (renamed from src/crypto/chacha8.c)16
-rw-r--r--src/crypto/chacha.h (renamed from src/crypto/chacha8.h)29
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp12
-rw-r--r--src/cryptonote_core/blockchain.cpp58
-rw-r--r--src/cryptonote_core/blockchain.h26
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp59
-rw-r--r--src/cryptonote_core/cryptonote_core.h21
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/cryptonote_protocol/block_queue.cpp3
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl28
-rw-r--r--src/daemon/command_parser_executor.cpp6
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/daemonizer/windows_service.cpp3
-rw-r--r--src/debug_utilities/cn_deserialize.cpp8
-rw-r--r--src/debug_utilities/object_sizes.cpp2
-rw-r--r--src/mnemonics/electrum-words.cpp2
-rw-r--r--src/p2p/net_node.inl11
-rw-r--r--src/ringct/rctOps.cpp3
-rw-r--r--src/rpc/core_rpc_server.cpp34
-rw-r--r--src/rpc/daemon_handler.cpp4
-rw-r--r--src/rpc/message.cpp2
-rw-r--r--src/serialization/crypto.h4
-rw-r--r--src/simplewallet/simplewallet.cpp147
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp4
-rw-r--r--src/wallet/api/utils.cpp5
-rw-r--r--src/wallet/api/wallet2_api.h1
-rw-r--r--src/wallet/node_rpc_proxy.cpp1
-rw-r--r--src/wallet/wallet2.cpp239
-rw-r--r--src/wallet/wallet2.h32
-rw-r--r--src/wallet/wallet_rpc_server.cpp116
-rw-r--r--src/wallet/wallet_rpc_server.h10
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h98
45 files changed, 817 insertions, 292 deletions
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 07b2451b0..7e77953c8 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -197,7 +197,7 @@ uint64_t BlockchainDB::add_block( const block& blk
{
// sanity
if (blk.tx_hashes.size() != txs.size())
- throw new std::runtime_error("Inconsistent tx/hashes sizes");
+ throw std::runtime_error("Inconsistent tx/hashes sizes");
block_txn_start(false);
@@ -283,7 +283,7 @@ block BlockchainDB::get_block_from_height(const uint64_t& height) const
blobdata bd = get_block_blob_from_height(height);
block b;
if (!parse_and_validate_block_from_blob(bd, b))
- throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse block from blob retrieved from the db");
return b;
}
@@ -293,7 +293,7 @@ block BlockchainDB::get_block(const crypto::hash& h) const
blobdata bd = get_block_blob(h);
block b;
if (!parse_and_validate_block_from_blob(bd, b))
- throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse block from blob retrieved from the db");
return b;
}
@@ -304,7 +304,7 @@ bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) co
if (!get_tx_blob(h, bd))
return false;
if (!parse_and_validate_tx_from_blob(bd, tx))
- throw new DB_ERROR("Failed to parse transaction from blob retrieved from the db");
+ throw DB_ERROR("Failed to parse transaction from blob retrieved from the db");
return true;
}
@@ -313,7 +313,7 @@ transaction BlockchainDB::get_tx(const crypto::hash& h) const
{
transaction tx;
if (!get_tx(h, tx))
- throw new TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str());
+ throw TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str());
return tx;
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 07a0e67b1..ee4368e86 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -2894,7 +2894,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
{
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs);
}
- catch (DB_ERROR_TXN_START& e)
+ catch (const DB_ERROR_TXN_START& e)
{
throw;
}
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 70d1dd696..758deb7e4 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -459,7 +459,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
// tx number 1: coinbase tx
// tx number 2 onwards: archived_txs
- for (transaction tx : archived_txs)
+ for (const transaction &tx : archived_txs)
{
// add blocks with verification.
// for Blockchain and blockchain_storage add_new_block().
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index c66c4f5d6..67a313bc2 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -204,7 +204,7 @@ namespace cryptonote
return true;
}
- bool checkpoints::load_checkpoints_from_json(const std::string json_hashfile_fullpath)
+ bool checkpoints::load_checkpoints_from_json(const std::string &json_hashfile_fullpath)
{
boost::system::error_code errcode;
if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode)))
@@ -218,7 +218,11 @@ namespace cryptonote
uint64_t prev_max_height = get_max_height();
LOG_PRINT_L1("Hard-coded max checkpoint height is " << prev_max_height);
t_hash_json hashes;
- epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath);
+ if (!epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath))
+ {
+ MERROR("Error loading checkpoints from " << json_hashfile_fullpath);
+ return false;
+ }
for (std::vector<t_hashline>::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); )
{
uint64_t height;
@@ -286,7 +290,7 @@ namespace cryptonote
return true;
}
- bool checkpoints::load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet, bool dns)
+ bool checkpoints::load_new_checkpoints(const std::string &json_hashfile_fullpath, bool testnet, bool dns)
{
bool result;
diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h
index 3e034f6f0..83969f7b8 100644
--- a/src/checkpoints/checkpoints.h
+++ b/src/checkpoints/checkpoints.h
@@ -165,7 +165,7 @@ namespace cryptonote
*
* @return true if loading successful and no conflicts
*/
- bool load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet=false, bool dns=true);
+ bool load_new_checkpoints(const std::string &json_hashfile_fullpath, bool testnet=false, bool dns=true);
/**
* @brief load new checkpoints from json
@@ -174,7 +174,7 @@ namespace cryptonote
*
* @return true if loading successful and no conflicts
*/
- bool load_checkpoints_from_json(const std::string json_hashfile_fullpath);
+ bool load_checkpoints_from_json(const std::string &json_hashfile_fullpath);
/**
* @brief load new checkpoints from DNS
diff --git a/src/common/download.cpp b/src/common/download.cpp
index 28aac5a59..87814fa5e 100644
--- a/src/common/download.cpp
+++ b/src/common/download.cpp
@@ -33,6 +33,7 @@
#include <boost/thread/thread.hpp>
#include "cryptonote_config.h"
#include "include_base_utils.h"
+#include "file_io_utils.h"
#include "net/http_client.h"
#include "download.h"
@@ -74,9 +75,20 @@ namespace tools
try
{
boost::unique_lock<boost::mutex> lock(control->mutex);
- MINFO("Downloading " << control->uri << " to " << control->path);
+ std::ios_base::openmode mode = std::ios_base::out | std::ios_base::binary;
+ uint64_t existing_size = 0;
+ if (epee::file_io_utils::get_file_size(control->path, existing_size) && existing_size > 0)
+ {
+ MINFO("Resuming downloading " << control->uri << " to " << control->path << " from " << existing_size);
+ mode |= std::ios_base::app;
+ }
+ else
+ {
+ MINFO("Downloading " << control->uri << " to " << control->path);
+ mode |= std::ios_base::trunc;
+ }
std::ofstream f;
- f.open(control->path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ f.open(control->path, mode);
if (!f.good()) {
MERROR("Failed to open file " << control->path);
control->result_cb(control->path, control->uri, control->success);
@@ -85,11 +97,13 @@ namespace tools
class download_client: public epee::net_utils::http::http_simple_client
{
public:
- download_client(download_async_handle control, std::ofstream &f):
- control(control), f(f), content_length(-1), total(0) {}
+ download_client(download_async_handle control, std::ofstream &f, uint64_t offset = 0):
+ control(control), f(f), content_length(-1), total(0), offset(offset) {}
virtual ~download_client() { f.close(); }
virtual bool on_header(const epee::net_utils::http::http_response_info &headers)
{
+ for (const auto &kv: headers.m_header_info.m_etc_fields)
+ MDEBUG("Header: " << kv.first << ": " << kv.second);
ssize_t length;
if (epee::string_tools::get_xtype_from_string(length, headers.m_header_info.m_content_length) && length >= 0)
{
@@ -104,6 +118,26 @@ namespace tools
return false;
}
}
+ if (offset > 0)
+ {
+ // we requested a range, so check if we're getting it, otherwise truncate
+ bool got_range = false;
+ const std::string prefix = "bytes=" + std::to_string(offset) + "-";
+ for (const auto &kv: headers.m_header_info.m_etc_fields)
+ {
+ if (kv.first == "Content-Range" && strncmp(kv.second.c_str(), prefix.c_str(), prefix.size()))
+ {
+ got_range = true;
+ break;
+ }
+ }
+ if (!got_range)
+ {
+ MWARNING("We did not get the requested range, downloading from start");
+ f.close();
+ f.open(control->path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
+ }
+ }
return true;
}
virtual bool handle_target_data(std::string &piece_of_transfer)
@@ -130,7 +164,8 @@ namespace tools
std::ofstream &f;
ssize_t content_length;
size_t total;
- } client(control, f);
+ uint64_t offset;
+ } client(control, f, existing_size);
epee::net_utils::http::url_content u_c;
if (!epee::net_utils::parse_url(control->uri, u_c))
{
@@ -147,9 +182,10 @@ namespace tools
lock.unlock();
- uint16_t port = u_c.port ? u_c.port : 80;
+ bool ssl = u_c.schema == "https";
+ uint16_t port = u_c.port ? u_c.port : ssl ? 443 : 80;
MDEBUG("Connecting to " << u_c.host << ":" << port);
- client.set_server(u_c.host, std::to_string(port), boost::none);
+ client.set_server(u_c.host, std::to_string(port), boost::none, ssl);
if (!client.connect(std::chrono::seconds(30)))
{
boost::lock_guard<boost::mutex> lock(control->mutex);
@@ -159,7 +195,14 @@ namespace tools
}
MDEBUG("GETting " << u_c.uri);
const epee::net_utils::http::http_response_info *info = NULL;
- if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info))
+ epee::net_utils::http::fields_list fields;
+ if (existing_size > 0)
+ {
+ const std::string range = "bytes=" + std::to_string(existing_size) + "-";
+ MDEBUG("Asking for range: " << range);
+ fields.push_back(std::make_pair("Range", range));
+ }
+ if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info, fields))
{
boost::lock_guard<boost::mutex> lock(control->mutex);
MERROR("Failed to connect to " << control->uri);
@@ -189,7 +232,7 @@ namespace tools
MDEBUG("response body: " << info->m_body);
for (const auto &f: info->m_additional_fields)
MDEBUG("additional field: " << f.first << ": " << f.second);
- if (info->m_response_code != 200)
+ if (info->m_response_code != 200 && info->m_response_code != 206)
{
boost::lock_guard<boost::mutex> lock(control->mutex);
MERROR("Status code " << info->m_response_code);
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp
index bcdf72b60..ed1093309 100644
--- a/src/common/stack_trace.cpp
+++ b/src/common/stack_trace.cpp
@@ -28,7 +28,10 @@
#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__
#define USE_UNWIND
+#else
+#define ELPP_FEATURE_CRASH_LOG 1
#endif
+#include "easylogging++/easylogging++.h"
#include <stdexcept>
#ifdef USE_UNWIND
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
index eff6754af..2d9c2d89c 100644
--- a/src/common/updates.cpp
+++ b/src/common/updates.cpp
@@ -99,7 +99,7 @@ namespace tools
std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user)
{
- const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/";
+ const char *base = user ? "https://downloads.getmonero.org/" : "https://updates.getmonero.org/";
#ifdef _WIN32
static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe";
#else
diff --git a/src/common/util.cpp b/src/common/util.cpp
index d9e12ffe4..2a2f50c4f 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -635,13 +635,13 @@ std::string get_nix_version_display_string()
int vercmp(const char *v0, const char *v1)
{
std::vector<std::string> f0, f1;
- boost::split(f0, v0, boost::is_any_of("."));
- boost::split(f1, v1, boost::is_any_of("."));
- while (f0.size() < f1.size())
- f0.push_back("0");
- while (f1.size() < f0.size())
- f1.push_back("0");
- for (size_t i = 0; i < f0.size(); ++i) {
+ boost::split(f0, v0, boost::is_any_of(".-"));
+ boost::split(f1, v1, boost::is_any_of(".-"));
+ for (size_t i = 0; i < std::max(f0.size(), f1.size()); ++i) {
+ if (i >= f0.size())
+ return -1;
+ if (i >= f1.size())
+ return 1;
int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str());
int n = f0i - f1i;
if (n)
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 1e06a0dfd..fd71a87e7 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -29,7 +29,7 @@
set(crypto_sources
aesb.c
blake256.c
- chacha8.c
+ chacha.c
crypto-ops-data.c
crypto-ops.c
crypto.cpp
@@ -51,7 +51,7 @@ set(crypto_headers)
set(crypto_private_headers
blake256.h
- chacha8.h
+ chacha.h
crypto-ops.h
crypto.h
generic-ops.h
diff --git a/src/crypto/chacha8.c b/src/crypto/chacha.c
index df135af59..f573083be 100644
--- a/src/crypto/chacha8.c
+++ b/src/crypto/chacha.c
@@ -8,7 +8,7 @@ Public domain.
#include <stdio.h>
#include <sys/param.h>
-#include "chacha8.h"
+#include "chacha.h"
#include "common/int-util.h"
#include "warnings.h"
@@ -40,7 +40,7 @@ static const char sigma[] = "expand 32-byte k";
DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing)
-void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) {
+static void chacha(unsigned rounds, const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) {
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
char* ctarget = 0;
@@ -89,7 +89,7 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t*
x13 = j13;
x14 = j14;
x15 = j15;
- for (i = 8;i > 0;i -= 2) {
+ for (i = rounds;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
@@ -168,3 +168,13 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t*
data = (uint8_t*)data + 64;
}
}
+
+void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
+{
+ chacha(8, data, length, key, iv, cipher);
+}
+
+void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher)
+{
+ chacha(20, data, length, key, iv, cipher);
+}
diff --git a/src/crypto/chacha8.h b/src/crypto/chacha.h
index dcbe6a933..a9665030d 100644
--- a/src/crypto/chacha8.h
+++ b/src/crypto/chacha.h
@@ -33,8 +33,8 @@
#include <stdint.h>
#include <stddef.h>
-#define CHACHA8_KEY_SIZE 32
-#define CHACHA8_IV_SIZE 8
+#define CHACHA_KEY_SIZE 32
+#define CHACHA_IV_SIZE 8
#if defined(__cplusplus)
#include <memory.h>
@@ -46,33 +46,38 @@ namespace crypto {
extern "C" {
#endif
void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
+ void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher);
#if defined(__cplusplus)
}
- using chacha8_key = tools::scrubbed_arr<uint8_t, CHACHA8_KEY_SIZE>;
+ using chacha_key = tools::scrubbed_arr<uint8_t, CHACHA_KEY_SIZE>;
#pragma pack(push, 1)
- // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct
- struct chacha8_iv {
- uint8_t data[CHACHA8_IV_SIZE];
+ // MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct
+ struct chacha_iv {
+ uint8_t data[CHACHA_IV_SIZE];
};
#pragma pack(pop)
- static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size");
+ static_assert(sizeof(chacha_key) == CHACHA_KEY_SIZE && sizeof(chacha_iv) == CHACHA_IV_SIZE, "Invalid structure size");
- inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) {
+ inline void chacha8(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
chacha8(data, length, key.data(), reinterpret_cast<const uint8_t*>(&iv), cipher);
}
- inline void generate_chacha8_key(const void *data, size_t size, chacha8_key& key) {
- static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key");
+ inline void chacha20(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) {
+ 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) {
+ 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());
memcpy(&key, pwd_hash.data(), sizeof(key));
}
- inline void generate_chacha8_key(std::string password, chacha8_key& key) {
- return generate_chacha8_key(password.data(), password.size(), key);
+ inline void generate_chacha_key(std::string password, chacha_key& key) {
+ return generate_chacha_key(password.data(), password.size(), key);
}
}
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 89dda8c3d..821c21d84 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -259,7 +259,7 @@ namespace cryptonote
ar.tag("rctsig_prunable");
ar.begin_object();
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
- vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
+ vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
if (!r || !ar.stream().good()) return false;
ar.end_object();
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 8f7ab94db..21fa63842 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -630,17 +630,21 @@ namespace cryptonote
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, const std::vector<crypto::public_key>& additional_tx_pub_keys, size_t output_index)
{
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::public_key pk;
- derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
if (pk == out_key.key)
return true;
// try additional tx pubkeys if available
if (!additional_tx_pub_keys.empty())
{
CHECK_AND_ASSERT_MES(output_index < additional_tx_pub_keys.size(), false, "wrong number of additional tx pubkeys");
- generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
- derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ r = generate_key_derivation(additional_tx_pub_keys[output_index], acc.m_view_secret_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
+ r = derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to derive public key");
return pk == out_key.key;
}
return false;
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 123bd194b..709c5e852 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -321,6 +321,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const
if (!db->is_open())
{
LOG_ERROR("Attempted to init Blockchain with unopened DB");
+ delete db;
return false;
}
@@ -471,7 +472,7 @@ bool Blockchain::deinit()
// memory operation), otherwise we may cause a loop.
if (m_db == NULL)
{
- throw new DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
+ throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
}
try
@@ -489,7 +490,9 @@ bool Blockchain::deinit()
}
delete m_hardfork;
+ m_hardfork = NULL;
delete m_db;
+ m_db = NULL;
return true;
}
//------------------------------------------------------------------
@@ -2050,49 +2053,6 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
return true;
}
//------------------------------------------------------------------
-void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto h = m_db->height();
- if(start_index > h)
- {
- MERROR("Wrong starter index set: " << start_index << ", expected max index " << h);
- return;
- }
-
- for(size_t i = start_index; i <= h && i != end_index; i++)
- {
- ss << "height " << i << ", timestamp " << m_db->get_block_timestamp(i) << ", cumul_dif " << m_db->get_block_cumulative_difficulty(i) << ", size " << m_db->get_block_size(i) << "\nid\t\t" << m_db->get_block_hash_from_height(i) << "\ndifficulty\t\t" << m_db->get_block_difficulty(i) << ", nonce " << m_db->get_block_from_height(i).nonce << ", tx_count " << m_db->get_block_from_height(i).tx_hashes.size() << std::endl;
- }
- MCINFO("globlal", "Current blockchain:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-void Blockchain::print_blockchain_index() const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto height = m_db->height();
- if (height != 0)
- {
- for(uint64_t i = 0; i <= height; i++)
- {
- ss << "height: " << i << ", hash: " << m_db->get_block_hash_from_height(i);
- }
- }
-
- MINFO("Current blockchain index:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-//TODO: remove this function and references to it
-void Blockchain::print_blockchain_outs(const std::string& file) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- return;
-}
-//------------------------------------------------------------------
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
@@ -2338,7 +2298,7 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx)
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
}
}
@@ -2373,7 +2333,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx));
}
if (!res)
@@ -2466,6 +2426,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
rv.mixRing[m].clear();
@@ -2480,6 +2441,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
@@ -2811,7 +2773,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
for (size_t n = 0; n < tx.vin.size(); ++n)
{
- if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
+ if (rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
{
MERROR_VER("Failed to check ringct signatures: mismatched key image");
return false;
@@ -2864,7 +2826,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Failed to check ringct signatures: Bad MGs size");
return false;
}
- if (rv.p.MGs[0].II.size() != tx.vin.size())
+ if (rv.p.MGs.empty() || rv.p.MGs[0].II.size() != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched II/vin sizes");
return false;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b76d0555f..2d5307ac0 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -680,32 +680,6 @@ namespace cryptonote
//debug functions
/**
- * @brief prints data about a snippet of the blockchain
- *
- * if start_index is greater than the blockchain height, do nothing
- *
- * @param start_index height on chain to start at
- * @param end_index height on chain to end at
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @brief prints every block's hash
- *
- * WARNING: This function will absolutely crush a terminal in prints, so
- * it is recommended to redirect this output to a log file (or null sink
- * if a log file is already set up, as should be the default)
- */
- void print_blockchain_index() const;
-
- /**
- * @brief currently does nothing, candidate for removal
- *
- * @param file
- */
- void print_blockchain_outs(const std::string& file) const;
-
- /**
* @brief check the blockchain against a set of checkpoints
*
* If a block fails a checkpoint and enforce is enabled, the blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 415657f9c..adbc727b0 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -47,6 +47,7 @@ using namespace epee;
#include "cryptonote_config.h"
#include "cryptonote_tx_utils.h"
#include "misc_language.h"
+#include "file_io_utils.h"
#include <csignal>
#include "checkpoints/checkpoints.h"
#include "ringct/rctTypes.h"
@@ -377,7 +378,7 @@ namespace cryptonote
// folder might not be a directory, etc, etc
catch (...) { }
- BlockchainDB* db = new_db(db_type);
+ std::unique_ptr<BlockchainDB> db(new_db(db_type));
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type");
@@ -468,7 +469,7 @@ namespace cryptonote
m_blockchain_storage.set_user_options(blocks_threads,
blocks_per_sync, sync_mode, fast_sync);
- r = m_blockchain_storage.init(db, m_testnet, m_offline, test_options);
+ r = m_blockchain_storage.init(db.release(), m_testnet, m_offline, test_options);
r = m_mempool.init();
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -1052,21 +1053,6 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count);
}
//-----------------------------------------------------------------------------------------------
- void core::print_blockchain(uint64_t start_index, uint64_t end_index) const
- {
- m_blockchain_storage.print_blockchain(start_index, end_index);
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_index() const
- {
- m_blockchain_storage.print_blockchain_index();
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_outs(const std::string& file)
- {
- m_blockchain_storage.print_blockchain_outs(file);
- }
- //-----------------------------------------------------------------------------------------------
bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
{
return m_blockchain_storage.get_random_outs_for_amounts(req, res);
@@ -1452,27 +1438,56 @@ namespace cryptonote
if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash)))
{
MCDEBUG("updates", "We don't have that file already, downloading");
+ const std::string tmppath = path.string() + ".tmp";
+ if (epee::file_io_utils::is_file_exist(tmppath))
+ {
+ MCDEBUG("updates", "We have part of the file already, resuming download");
+ }
m_last_update_length = 0;
- m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) {
+ m_update_download = tools::download_async(tmppath, url, [this, hash, path](const std::string &tmppath, const std::string &uri, bool success) {
+ bool remove = false, good = true;
if (success)
{
crypto::hash file_hash;
- if (!tools::sha256sum(path, file_hash))
+ if (!tools::sha256sum(tmppath, file_hash))
{
- MCERROR("updates", "Failed to hash " << path);
+ MCERROR("updates", "Failed to hash " << tmppath);
+ remove = true;
+ good = false;
}
- if (hash != epee::string_tools::pod_to_hex(file_hash))
+ else if (hash != epee::string_tools::pod_to_hex(file_hash))
{
MCERROR("updates", "Download from " << uri << " does not match the expected hash");
+ remove = true;
+ good = false;
}
- MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path);
}
else
{
MCERROR("updates", "Failed to download " << uri);
+ good = false;
}
boost::unique_lock<boost::mutex> lock(m_update_mutex);
m_update_download = 0;
+ if (success && !remove)
+ {
+ std::error_code e = tools::replace_file(tmppath, path.string());
+ if (e)
+ {
+ MCERROR("updates", "Failed to rename downloaded file");
+ good = false;
+ }
+ }
+ else if (remove)
+ {
+ if (!boost::filesystem::remove(tmppath))
+ {
+ MCERROR("updates", "Failed to remove invalid downloaded file");
+ good = false;
+ }
+ }
+ if (good)
+ MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path.string());
}, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) {
if (length >= m_last_update_length + 1024 * 1024 * 10)
{
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 9f84ed303..adc201fb5 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -601,20 +601,6 @@ namespace cryptonote
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
/**
- * @copydoc Blockchain::print_blockchain
- *
- * @note see Blockchain::print_blockchain
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @copydoc Blockchain::print_blockchain_index
- *
- * @note see Blockchain::print_blockchain_index
- */
- void print_blockchain_index() const;
-
- /**
* @copydoc tx_memory_pool::print_pool
*
* @note see tx_memory_pool::print_pool
@@ -622,13 +608,6 @@ namespace cryptonote
std::string print_pool(bool short_format) const;
/**
- * @copydoc Blockchain::print_blockchain_outs
- *
- * @note see Blockchain::print_blockchain_outs
- */
- void print_blockchain_outs(const std::string& file);
-
- /**
* @copydoc miner::on_synchronized
*
* @note see miner::on_synchronized
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 89f24a4d4..916b1e05a 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -190,6 +190,12 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
{
+ if (sources.empty())
+ {
+ LOG_ERROR("Empty sources");
+ return false;
+ }
+
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index bfff35456..3844d3751 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -62,6 +62,7 @@ void block_queue::add_blocks(uint64_t height, std::list<cryptonote::block_comple
void block_queue::add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time)
{
+ CHECK_AND_ASSERT_THROW_MES(nblocks > 0, "Empty span");
boost::unique_lock<boost::recursive_mutex> lock(mutex);
blocks.insert(span(height, nblocks, connection_id, time));
}
@@ -384,7 +385,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const
i->second = (i->second + span.rate) / 2;
}
float conn_rate = -1, best_rate = 0;
- for (auto i: speeds)
+ for (const auto &i: speeds)
{
if (i.first == connection_id)
conn_rate = i.second;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 389e8ba84..8aef31a5a 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -266,13 +266,17 @@ namespace cryptonote
return true;
// from v6, if the peer advertises a top block version, reject if it's not what it should be (will only work if no voting)
- const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1);
- if (version >= 6 && version != hshd.top_version)
+ if (hshd.current_height > 0)
{
- if (version < hshd.top_version)
- MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think - we may be forked from the network and a software upgrade may be needed");
- LOG_DEBUG_CC(context, "Ignoring due to wrong top version for block " << (hshd.current_height - 1) << ": " << (unsigned)hshd.top_version << ", expected " << (unsigned)version);
- return false;
+ const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1);
+ if (version >= 6 && version != hshd.top_version)
+ {
+ if (version < hshd.top_version)
+ MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think (" <<
+ (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << "instead of " << (unsigned)version <<
+ ") - we may be forked from the network and a software upgrade may be needed");
+ return false;
+ }
}
context.m_remote_blockchain_height = hshd.current_height;
@@ -999,6 +1003,11 @@ skip:
MDEBUG(context << " next span in the queue has blocks " << start_height << "-" << (start_height + blocks.size() - 1)
<< ", we need " << previous_height);
+ if (blocks.empty())
+ {
+ MERROR("Next span has no blocks");
+ break;
+ }
block new_block;
if (!parse_and_validate_block_from_blob(blocks.front().block, new_block))
@@ -1494,6 +1503,7 @@ skip:
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
m_core.get_short_chain_history(r.block_ids);
+ CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty");
if (!start_from_current_chain)
{
@@ -1581,6 +1591,12 @@ skip:
drop_connection(context, true, false);
return 1;
}
+ if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size())
+ {
+ LOG_ERROR_CCONTEXT("sent invalid start/nblocks/height, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
context.m_remote_blockchain_height = arg.total_height;
context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1;
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 5307b2472..8970f9407 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -173,7 +173,7 @@ bool t_command_parser_executor::print_block(const std::vector<std::string>& args
uint64_t height = boost::lexical_cast<uint64_t>(arg);
return m_executor.print_block_by_height(height);
}
- catch (boost::bad_lexical_cast&)
+ catch (const boost::bad_lexical_cast&)
{
crypto::hash block_hash;
if (parse_hash256(arg, block_hash))
@@ -420,7 +420,7 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args)
limit = std::stoi(args[0]);
}
- catch(std::exception& ex) {
+ catch(const std::exception& ex) {
_erro("stoi exception");
return false;
}
@@ -450,7 +450,7 @@ bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& a
try {
version = std::stoi(args[0]);
}
- catch(std::exception& ex) {
+ catch(const std::exception& ex) {
return false;
}
if (version <= 0 || version > 255)
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index d7ee28baa..8aca668ad 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1611,7 +1611,7 @@ bool t_rpc_command_executor::alt_chain_info()
}
tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto chain: res.chains)
+ for (const auto &chain: res.chains)
{
uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp
index d540f5bf8..9b8e46615 100644
--- a/src/daemonizer/windows_service.cpp
+++ b/src/daemonizer/windows_service.cpp
@@ -26,6 +26,9 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <boost/chrono/chrono.hpp>
+#include <boost/thread/thread.hpp>
+
#undef UNICODE
#undef _UNICODE
diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp
index a59c04dc6..04c0935c8 100644
--- a/src/debug_utilities/cn_deserialize.cpp
+++ b/src/debug_utilities/cn_deserialize.cpp
@@ -154,7 +154,11 @@ int main(int argc, char* argv[])
std::cout << "Parsed transaction:" << std::endl;
std::cout << cryptonote::obj_to_json_str(tx) << std::endl;
- if (cryptonote::parse_tx_extra(tx.extra, fields))
+ bool parsed = cryptonote::parse_tx_extra(tx.extra, fields);
+ if (!parsed)
+ std::cout << "Failed to parse tx_extra" << std::endl;
+
+ if (!fields.empty())
{
std::cout << "tx_extra has " << fields.size() << " field(s)" << std::endl;
for (size_t n = 0; n < fields.size(); ++n)
@@ -171,7 +175,7 @@ int main(int argc, char* argv[])
}
else
{
- std::cout << "Failed to parse tx_extra" << std::endl;
+ std::cout << "No fields were found in tx_extra" << std::endl;
}
}
else
diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp
index 82d8a4add..967742229 100644
--- a/src/debug_utilities/object_sizes.cpp
+++ b/src/debug_utilities/object_sizes.cpp
@@ -51,7 +51,7 @@ class size_logger
public:
~size_logger()
{
- for (const auto i: types)
+ for (const auto &i: types)
std::cout << std::to_string(i.first) << "\t" << i.second << std::endl;
}
void add(const char *type, size_t size) { types.insert(std::make_pair(size, type)); }
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 1b14905f6..ba67952aa 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -205,6 +205,8 @@ namespace
*/
bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length)
{
+ if (seed.empty())
+ return false;
// The last word is the checksum.
std::string last_word = seed.back();
seed.pop_back();
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 55be7c2cb..269a9ba87 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -649,6 +649,10 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::send_stop_signal()
{
+ MDEBUG("[node] sending stop signal");
+ m_net_server.send_stop_signal();
+ MDEBUG("[node] Stop signal sent");
+
std::list<boost::uuids::uuid> connection_ids;
m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) {
connection_ids.push_back(cntxt.m_connection_id);
@@ -658,8 +662,7 @@ namespace nodetool
m_net_server.get_config_object().close(connection_id);
m_payload_handler.stop();
- m_net_server.send_stop_signal();
- MDEBUG("[node] Stop signal sent");
+
return true;
}
//-----------------------------------------------------------------------------------
@@ -1856,8 +1859,8 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::has_too_many_connections(const epee::net_utils::network_address &address)
{
- const uint8_t max_connections = 1;
- uint8_t count = 0;
+ const size_t max_connections = 1;
+ size_t count = 0;
m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
{
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index a7311482c..cc46d0aa7 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -76,6 +76,7 @@ namespace rct {
//Generates a vector of secret key
//Mainly used in testing
keyV skvGen(size_t rows ) {
+ CHECK_AND_ASSERT_THROW_MES(rows > 0, "0 keys requested");
keyV rv(rows);
size_t i = 0;
crypto::rand(rows * sizeof(key), (uint8_t*)&rv[0]);
@@ -351,6 +352,7 @@ namespace rct {
//This takes the outputs and commitments
//and hashes them into a 32 byte sized key
key cn_fast_hash(const ctkeyV &PC) {
+ if (PC.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0));
key rv;
cn_fast_hash(rv, &PC[0], 64*PC.size());
return rv;
@@ -367,6 +369,7 @@ namespace rct {
//put them in the key vector and it concatenates them
//and then hashes them
key cn_fast_hash(const keyV &keys) {
+ if (keys.empty()) return rct::hash2rct(crypto::cn_fast_hash("", 0));
key rv;
cn_fast_hash(rv, &keys[0], keys.size() * sizeof(keys[0]));
//dp(rv);
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 803588cbd..a6109cb89 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -492,6 +492,11 @@ namespace cryptonote
{
if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
{
+ if (txs.empty())
+ {
+ res.status = "Failed: internal error - txs is empty";
+ return true;
+ }
// core returns the ones it finds in the right order
if (get_transaction_hash(txs.front()) != h)
{
@@ -1150,7 +1155,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1188,7 +1193,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by height. Height = " + boost::lexical_cast<std::string>(h) + ". Hash = " + epee::string_tools::pod_to_hex(block_hash) + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1274,7 +1279,7 @@ namespace cryptonote
error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
return false;
}
- if (blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
@@ -1436,19 +1441,25 @@ namespace cryptonote
{
failed = true;
}
- crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
- txids.push_back(txid);
+ else
+ {
+ crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+ txids.push_back(txid);
+ }
}
}
if (!m_core.get_blockchain_storage().flush_txes_from_pool(txids))
{
- res.status = "Failed to remove one more tx";
+ res.status = "Failed to remove one or more tx(es)";
return false;
}
if (failed)
{
- res.status = "Failed to parse txid";
+ if (txids.empty())
+ res.status = "Failed to parse txid";
+ else
+ res.status = "Failed to parse some of the txids";
return false;
}
@@ -1705,13 +1716,16 @@ namespace cryptonote
PERF_TIMER(on_relay_tx);
bool failed = false;
+ res.status = "";
for (const auto &str: req.txids)
{
cryptonote::blobdata txid_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(str, txid_data))
{
- res.status = std::string("Invalid transaction id: ") + str;
+ if (!res.status.empty()) res.status += ", ";
+ res.status += std::string("invalid transaction id: ") + str;
failed = true;
+ continue;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -1727,8 +1741,10 @@ namespace cryptonote
}
else
{
- res.status = std::string("Transaction not found in pool: ") + str;
+ if (!res.status.empty()) res.status += ", ";
+ res.status += std::string("transaction not found in pool: ") + str;
failed = true;
+ continue;
}
}
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 6643ce4e4..908f9e187 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -799,6 +799,10 @@ namespace rpc
}
header.hash = hash_in;
+ if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen))
+ {
+ return false;
+ }
header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height;
header.major_version = b.major_version;
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index d6da124d1..98b40e667 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -111,7 +111,7 @@ FullMessage::FullMessage(Message* message)
FullMessage::FullMessage(const std::string& json_string, bool request)
{
doc.Parse(json_string.c_str());
- if (doc.HasParseError())
+ if (doc.HasParseError() || !doc.IsObject())
{
throw cryptonote::json::PARSE_FAIL();
}
diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h
index 4213f2e58..8083bdeb1 100644
--- a/src/serialization/crypto.h
+++ b/src/serialization/crypto.h
@@ -34,7 +34,7 @@
#include "serialization.h"
#include "debug_archive.h"
-#include "crypto/chacha8.h"
+#include "crypto/chacha.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
@@ -77,7 +77,7 @@ bool do_serialize(Archive<true> &ar, std::vector<crypto::signature> &v)
return true;
}
-BLOB_SERIALIZER(crypto::chacha8_iv);
+BLOB_SERIALIZER(crypto::chacha_iv);
BLOB_SERIALIZER(crypto::hash);
BLOB_SERIALIZER(crypto::hash8);
BLOB_SERIALIZER(crypto::public_key);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index ffe921ae7..608844b80 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1575,8 +1575,20 @@ simple_wallet::simple_wallet()
tr("Change the current log detail (level must be <0-4>)."));
m_cmd_binder.set_handler("account",
boost::bind(&simple_wallet::account, this, _1),
- tr("account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>]"),
- tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances. If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). If the \"switch\" argument is specified, the wallet switches to the account specified by <index>. If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text."));
+ tr("account\n"
+ " account new <label text with white spaces allowed>\n"
+ " account switch <index> \n"
+ " account label <index> <label text with white spaces allowed>\n"
+ " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
+ " account untag <account_index_1> [<account_index_2> ...]\n"
+ " account tag_description <tag_name> <description>"),
+ tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
+ "If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
+ "If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n"
+ "If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text.\n"
+ "If the \"tag\" argument is specified, a tag <tag_name> is assigned to the specified accounts <account_index_1>, <account_index_2>, ....\n"
+ "If the \"untag\" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed.\n"
+ "If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>."));
m_cmd_binder.set_handler("address",
boost::bind(&simple_wallet::print_address, this, _1),
tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"),
@@ -3119,6 +3131,8 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
if (m_wallet->has_multisig_partial_key_images())
extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)");
success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0});
+ const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
+ success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", "
<< tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra;
std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account);
@@ -4321,9 +4335,9 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen");
return true;
}
- if (ptx_vector[0].selected_transfers.size() > 1)
+ if (ptx_vector[0].selected_transfers.size() != 1)
{
- fail_msg_writer() << tr("The transaction uses multiple inputs, which is not supposed to happen");
+ fail_msg_writer() << tr("The transaction uses multiple or no inputs, which is not supposed to happen");
return true;
}
@@ -5094,7 +5108,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try {
min_height = boost::lexical_cast<uint64_t>(local_args[0]);
}
- catch (boost::bad_lexical_cast &) {
+ catch (const boost::bad_lexical_cast &) {
fail_msg_writer() << tr("bad min_height parameter:") << " " << local_args[0];
return true;
}
@@ -5106,7 +5120,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try {
max_height = boost::lexical_cast<uint64_t>(local_args[0]);
}
- catch (boost::bad_lexical_cast &) {
+ catch (const boost::bad_lexical_cast &) {
fail_msg_writer() << tr("bad max_height parameter:") << " " << local_args[0];
return true;
}
@@ -5269,7 +5283,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
for (const auto& td : transfers)
{
uint64_t amount = td.amount();
- if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || subaddr_indices.count(td.m_subaddr_index.minor) == 0)
+ if (td.m_spent || amount < min_amount || amount > max_amount || td.m_subaddr_index.major != m_current_subaddress_account || (subaddr_indices.count(td.m_subaddr_index.minor) == 0 && !subaddr_indices.empty()))
continue;
amount_to_tds[amount].push_back(td);
if (min_height > td.m_block_height) min_height = td.m_block_height;
@@ -5432,6 +5446,9 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
// account new <label text with white spaces allowed>
// account switch <index>
// account label <index> <label text with white spaces allowed>
+ // account tag <tag_name> <account_index_1> [<account_index_2> ...]
+ // account untag <account_index_1> [<account_index_2> ...]
+ // account tag_description <tag_name> <description>
if (args.empty())
{
@@ -5496,18 +5513,128 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
fail_msg_writer() << e.what();
}
}
+ else if (command == "tag" && local_args.size() >= 2)
+ {
+ const std::string tag = local_args[0];
+ std::set<uint32_t> account_indices;
+ for (size_t i = 1; i < local_args.size(); ++i)
+ {
+ uint32_t account_index;
+ if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
+ {
+ fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
+ return true;
+ }
+ account_indices.insert(account_index);
+ }
+ try
+ {
+ m_wallet->set_account_tag(account_indices, tag);
+ print_accounts(tag);
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
+ else if (command == "untag" && local_args.size() >= 1)
+ {
+ std::set<uint32_t> account_indices;
+ for (size_t i = 0; i < local_args.size(); ++i)
+ {
+ uint32_t account_index;
+ if (!epee::string_tools::get_xtype_from_string(account_index, local_args[i]))
+ {
+ fail_msg_writer() << tr("failed to parse index: ") << local_args[i];
+ return true;
+ }
+ account_indices.insert(account_index);
+ }
+ try
+ {
+ m_wallet->set_account_tag(account_indices, "");
+ print_accounts();
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
+ else if (command == "tag_description" && local_args.size() >= 1)
+ {
+ const std::string tag = local_args[0];
+ std::string description;
+ if (local_args.size() > 1)
+ {
+ local_args.erase(local_args.begin());
+ description = boost::join(local_args, " ");
+ }
+ try
+ {
+ m_wallet->set_account_tag_description(tag, description);
+ print_accounts(tag);
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ }
else
{
- fail_msg_writer() << tr("usage: account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>]");
+ fail_msg_writer() << tr("usage:\n"
+ " account\n"
+ " account new <label text with white spaces allowed>\n"
+ " account switch <index>\n"
+ " account label <index> <label text with white spaces allowed>\n"
+ " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
+ " account untag <account_index_1> [<account_index_2> ...]\n"
+ " account tag_description <tag_name> <description>");
}
return true;
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts()
{
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
+ size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
+ for (const std::pair<std::string, std::string>& p : account_tags.first)
+ {
+ const std::string& tag = p.first;
+ print_accounts(tag);
+ num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
+ success_msg_writer() << "";
+ }
+
+ if (num_untagged_accounts > 0)
+ print_accounts("");
+
+ if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts())
+ success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_money(m_wallet->balance_all()) << tr(", unlocked balance: ") << print_money(m_wallet->unlocked_balance_all());
+}
+//----------------------------------------------------------------------------------------------------
+void simple_wallet::print_accounts(const std::string& tag)
+{
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
+ if (tag.empty())
+ {
+ success_msg_writer() << tr("Untagged accounts:");
+ }
+ else
+ {
+ if (account_tags.first.count(tag) == 0)
+ {
+ fail_msg_writer() << boost::format(tr("Tag %s is unregistered.")) % tag;
+ return;
+ }
+ success_msg_writer() << tr("Accounts with tag: ") << tag;
+ success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
+ }
success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label");
+ uint64_t total_balance = 0, total_unlocked_balance = 0;
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
{
+ if (account_tags.second[account_index] != tag)
+ continue;
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
% (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index
@@ -5515,9 +5642,11 @@ void simple_wallet::print_accounts()
% print_money(m_wallet->balance(account_index))
% print_money(m_wallet->unlocked_balance(account_index))
% m_wallet->get_subaddress_label({account_index, 0});
+ total_balance += m_wallet->balance(account_index);
+ total_unlocked_balance += m_wallet->unlocked_balance(account_index);
}
success_msg_writer() << tr("----------------------------------------------------------------------------------");
- success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(m_wallet->balance_all()) % print_money(m_wallet->unlocked_balance_all());
+ success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(total_balance) % print_money(total_unlocked_balance);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index c0c33f6b8..e5c00e542 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -153,6 +153,7 @@ namespace cryptonote
);
bool account(const std::vector<std::string> &args = std::vector<std::string>());
void print_accounts();
+ void print_accounts(const std::string& tag);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
diff --git a/src/version.cpp.in b/src/version.cpp.in
index d1444f867..18d62db6b 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.11.0.0"
+#define DEF_MONERO_VERSION "0.11.1.0-master"
#define DEF_MONERO_RELEASE_NAME "Helium Hydra"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 4c8c5ade2..d22719189 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -293,6 +293,10 @@ std::vector<std::string> UnsignedTransactionImpl::recipientAddress() const
// TODO: return integrated address if short payment ID exists
std::vector<string> result;
for (const auto &utx: m_unsigned_tx_set.txes) {
+ if (utx.dests.empty()) {
+ MERROR("empty destinations, skipped");
+ continue;
+ }
result.push_back(cryptonote::get_account_address_as_str(m_wallet.m_wallet->testnet(), utx.dests[0].is_subaddress, utx.dests[0].addr));
}
return result;
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index a9646e038..e54dd9f1c 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -48,6 +48,11 @@ bool isAddressLocal(const std::string &address)
}
}
+void onStartup()
+{
+ tools::on_startup();
+}
+
}
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 8593bd1f9..ab1a48d6e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -42,6 +42,7 @@ namespace Monero {
namespace Utils {
bool isAddressLocal(const std::string &hostaddr);
+ void onStartup();
}
template<typename T>
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 9f30e4ac5..07185d12c 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -68,7 +68,6 @@ void NodeRPCProxy::invalidate()
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const
{
- const time_t now = time(NULL);
if (m_rpc_version == 0)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 04c6ee236..d6f4b9b98 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -144,6 +144,16 @@ uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, ui
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier);
}
+std::string get_size_string(size_t sz)
+{
+ return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)";
+}
+
+std::string get_size_string(const cryptonote::blobdata &tx)
+{
+ return get_size_string(tx.size());
+}
+
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
{
const bool testnet = command_line::get_arg(vm, opts.testnet);
@@ -540,6 +550,11 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx)
{
if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
+ if (ptx.dests.empty())
+ {
+ MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
+ return crypto::null_hash8;
+ }
decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key);
}
}
@@ -710,7 +725,11 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
crypto::secret_key key = get_account().get_keys().m_spend_secret_key;
if (!passphrase.empty())
key = cryptonote::encrypt_key(key, passphrase);
- crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language);
+ if (!crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language))
+ {
+ std::cout << "Failed to create seed from key for language: " << seed_language << std::endl;
+ return false;
+ }
return true;
}
@@ -986,7 +1005,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
if(0 != m_callback)
m_callback->on_skip_transaction(height, txid, tx);
- return;
+ break;
}
int num_vouts_received = 0;
@@ -2364,12 +2383,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
account_data = buffer.GetString();
// Encrypt the entire JSON object.
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string cipher;
cipher.resize(account_data.size());
- keys_file_data.iv = crypto::rand<crypto::chacha8_iv>();
- crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]);
+ 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;
std::string buf;
@@ -2397,6 +2416,7 @@ namespace
*/
bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password)
{
+ rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
@@ -2405,14 +2425,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
// Decrypt the contents
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::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
- crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+ crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
// The contents should be JSON if the wallet follows the new format.
- rapidjson::Document json;
if (json.Parse(account_data.c_str()).HasParseError())
{
is_old_file_format = true;
@@ -2582,6 +2603,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const
*/
bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key)
{
+ rapidjson::Document json;
wallet2::keys_file_data keys_file_data;
std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
@@ -2590,14 +2612,15 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
// Decrypt the contents
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::chacha8_key key;
- crypto::generate_chacha8_key(password.data(), password.size(), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(password.data(), password.size(), key);
std::string account_data;
account_data.resize(keys_file_data.account_data.size());
- crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+ if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+ crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
// The contents should be JSON if the wallet follows the new format.
- rapidjson::Document json;
if (json.Parse(account_data.c_str()).HasParseError())
{
// old format before JSON wallet key file format
@@ -3283,7 +3306,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const
+bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
{
const account_keys &keys = m_account.get_keys();
const crypto::secret_key &view_key = keys.m_view_secret_key;
@@ -3292,7 +3315,7 @@ bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) co
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_chacha8_key(data.data(), sizeof(data), key);
+ crypto::generate_chacha_key(data.data(), sizeof(data), key);
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -3332,34 +3355,46 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
r = ::serialization::parse_binary(buf, cache_file_data);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
- crypto::chacha8_key key;
- generate_chacha8_key_from_secret_keys(key);
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
std::string cache_data;
cache_data.resize(cache_file_data.cache_data.size());
- crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
- std::stringstream iss;
- iss << cache_data;
try {
+ std::stringstream iss;
+ iss << cache_data;
boost::archive::portable_binary_iarchive ar(iss);
ar >> *this;
}
catch (...)
{
- LOG_PRINT_L0("Failed to open portable binary, trying unportable");
- boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
- iss.str("");
- iss << cache_data;
- boost::archive::binary_iarchive ar(iss);
- ar >> *this;
+ crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
+ try
+ {
+ std::stringstream iss;
+ iss << cache_data;
+ boost::archive::portable_binary_iarchive ar(iss);
+ ar >> *this;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L0("Failed to open portable binary, trying unportable");
+ boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
+ std::stringstream iss;
+ iss.str("");
+ iss << cache_data;
+ boost::archive::binary_iarchive ar(iss);
+ ar >> *this;
+ }
}
}
catch (...)
{
LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted");
- std::stringstream iss;
- iss << buf;
try {
+ std::stringstream iss;
+ iss << buf;
boost::archive::portable_binary_iarchive ar(iss);
ar >> *this;
}
@@ -3367,6 +3402,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
+ std::stringstream iss;
iss.str("");
iss << buf;
boost::archive::binary_iarchive ar(iss);
@@ -3491,12 +3527,12 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
cache_file_data.cache_data = oss.str();
- crypto::chacha8_key key;
- generate_chacha8_key_from_secret_keys(key);
+ crypto::chacha_key key;
+ generate_chacha_key_from_secret_keys(key);
std::string cipher;
cipher.resize(cache_file_data.cache_data.size());
- cache_file_data.iv = crypto::rand<crypto::chacha8_iv>();
- crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
+ cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
cache_file_data.cache_data = cipher;
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
@@ -3504,14 +3540,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_keys_file = m_keys_file;
const std::string old_address_file = m_wallet_file + ".address.txt";
- // save to new file
- std::ofstream ostr;
- ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
- binary_archive<true> oar(ostr);
- bool success = ::serialization::serialize(oar, cache_file_data);
- ostr.close();
- THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
-
// save keys to the new file
// if we here, main wallet file is saved and we only need to save keys and address files
if (!same_file) {
@@ -3538,6 +3566,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
LOG_ERROR("error removing file: " << old_address_file);
}
} else {
+ // save to new file
+ std::ofstream ostr;
+ ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ binary_archive<true> oar(ostr);
+ bool success = ::serialization::serialize(oar, cache_file_data);
+ ostr.close();
+ THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
+
// here we have "*.new" file, we need to rename it to be without ".new"
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
@@ -4050,6 +4086,11 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
crypto::hash8 payment_id8 = null_hash8;
if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
+ if (ptx.dests.empty())
+ {
+ MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt");
+ return crypto::null_hash;
+ }
if (decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key))
{
memcpy(payment_id.data, payment_id8.data, 8);
@@ -4274,6 +4315,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
for (size_t n = 0; n < exported_txs.txes.size(); ++n)
{
tools::wallet2::tx_construction_data &sd = exported_txs.txes[n];
+ THROW_WALLET_EXCEPTION_IF(sd.sources.empty(), error::wallet_internal_error, "Empty sources");
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
@@ -4950,6 +4992,7 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
if (global_index == real_index) // don't re-add real one
return false;
auto item = std::make_tuple(global_index, tx_public_key, mask);
+ CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
outs.back().push_back(item);
@@ -5114,7 +5157,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if there are just enough outputs to mix with, use all of them.
// Eventually this should become impossible.
uint64_t num_outs = 0, num_recent_outs = 0;
- for (auto he: resp_t.result.histogram)
+ for (const auto &he: resp_t.result.histogram)
{
if (he.amount == amount)
{
@@ -5715,7 +5758,19 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
LOG_PRINT_L2("pick_preferred_rct_inputs: needed_money " << print_money(needed_money));
- // try to find two outputs
+ // try to find a rct input of enough size
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ {
+ LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
+ picks.push_back(i);
+ return picks;
+ }
+ }
+
+ // then try to find two outputs
// this could be made better by picking one of the outputs to be a small one, since those
// are less useful since often below the needed money, so if one can be used in a pair,
// it gets rid of it for the future
@@ -6223,7 +6278,8 @@ bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const cr
if (decrypt) {
// Decrypt the mask
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
crypto::secret_key scalar;
crypto::derivation_to_scalar(derivation, internal_output_index, scalar);
sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes);
@@ -6632,10 +6688,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
- if (needed_fee > available_for_fee && dsts[0].amount > 0)
+ if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
{
// we don't have enough for the fee, but we've only partially paid the current address,
// so we can take the fee from the paid amount, since we'll have to make another tx anyway
@@ -6674,11 +6730,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6730,7 +6786,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -6882,7 +6938,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << ((txBlob.size() + 1023) / 1024) << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@@ -6898,11 +6954,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
- LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
}
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
@@ -6929,7 +6985,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
- ": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
+ ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@@ -7413,12 +7469,14 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
continue;
crypto::public_key derived_out_key;
- derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ bool r = derive_public_key(derivation, n, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
bool found = out_key->key == derived_out_key;
crypto::key_derivation found_derivation = derivation;
if (!found && !additional_derivations.empty())
{
- derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ r = derive_public_key(additional_derivations[n], n, address.m_spend_public_key, derived_out_key);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to derive public key");
found = out_key->key == derived_out_key;
found_derivation = additional_derivations[n];
}
@@ -7818,6 +7876,46 @@ std::string wallet2::get_description() const
return get_attribute(ATTRIBUTE_DESCRIPTION);
}
+const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
+{
+ // ensure consistency
+ if (m_account_tags.second.size() != get_num_subaddress_accounts())
+ m_account_tags.second.resize(get_num_subaddress_accounts(), "");
+ for (const std::string& tag : m_account_tags.second)
+ {
+ if (!tag.empty() && m_account_tags.first.count(tag) == 0)
+ m_account_tags.first.insert({tag, ""});
+ }
+ for (auto i = m_account_tags.first.begin(); i != m_account_tags.first.end(); )
+ {
+ if (std::find(m_account_tags.second.begin(), m_account_tags.second.end(), i->first) == m_account_tags.second.end())
+ i = m_account_tags.first.erase(i);
+ else
+ ++i;
+ }
+ return m_account_tags;
+}
+
+void wallet2::set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag)
+{
+ for (uint32_t account_index : account_indices)
+ {
+ THROW_WALLET_EXCEPTION_IF(account_index >= get_num_subaddress_accounts(), error::wallet_internal_error, "Account index out of bound");
+ if (m_account_tags.second[account_index] == tag)
+ MDEBUG("This tag is already assigned to this account");
+ else
+ m_account_tags.second[account_index] = tag;
+ }
+ get_account_tags();
+}
+
+void wallet2::set_account_tag_description(const std::string& tag, const std::string& description)
+{
+ THROW_WALLET_EXCEPTION_IF(tag.empty(), error::wallet_internal_error, "Tag must not be empty");
+ THROW_WALLET_EXCEPTION_IF(m_account_tags.first.count(tag) == 0, error::wallet_internal_error, "Tag is unregistered");
+ m_account_tags.first[tag] = description;
+}
+
std::string wallet2::sign(const std::string &data) const
{
crypto::hash hash;
@@ -7883,13 +7981,15 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
- generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ bool r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
}
while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) {
const crypto::public_key tx_pub_key = pub_key_field.pub_key;
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
{
@@ -8176,13 +8276,15 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
const cryptonote::account_keys& keys = m_account.get_keys();
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ bool r = generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(spent_tx);
std::vector<crypto::key_derivation> additional_derivations;
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
- generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ r = generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation");
}
size_t output_index = 0;
for (const cryptonote::tx_out& out : spent_tx.vout)
@@ -8657,12 +8759,12 @@ 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::chacha8_key key;
- crypto::generate_chacha8_key(&skey, sizeof(skey), key);
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(&skey, sizeof(skey), key);
std::string ciphertext;
- crypto::chacha8_iv iv = crypto::rand<crypto::chacha8_iv>();
+ crypto::chacha_iv iv = crypto::rand<crypto::chacha_iv>();
ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0));
- crypto::chacha8(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
+ crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
memcpy(&ciphertext[0], &iv, sizeof(iv));
if (authenticated)
{
@@ -8683,13 +8785,13 @@ std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext,
//----------------------------------------------------------------------------------------------------
std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const
{
- const size_t prefix_size = sizeof(chacha8_iv) + (authenticated ? sizeof(crypto::signature) : 0);
+ const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0);
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size,
error::wallet_internal_error, "Unexpected ciphertext size");
- crypto::chacha8_key key;
- crypto::generate_chacha8_key(&skey, sizeof(skey), key);
- const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0];
+ crypto::chacha_key key;
+ crypto::generate_chacha_key(&skey, sizeof(skey), key);
+ const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0];
std::string plaintext;
plaintext.resize(ciphertext.size() - prefix_size);
if (authenticated)
@@ -8702,7 +8804,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
error::wallet_internal_error, "Failed to authenticate ciphertext");
}
- crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
+ crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
return plaintext;
}
//----------------------------------------------------------------------------------------------------
@@ -8904,6 +9006,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
throw std::runtime_error(oss.str());
}
cryptonote::block blk_min, blk_mid, blk_max;
+ if (res.blocks.size() < 3) throw std::runtime_error("Not enough blocks returned from daemon");
if (!parse_and_validate_block_from_blob(res.blocks[0].block, blk_min)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_min));
if (!parse_and_validate_block_from_blob(res.blocks[1].block, blk_mid)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_mid));
if (!parse_and_validate_block_from_blob(res.blocks[2].block, blk_max)) throw std::runtime_error("failed to parse blob at height " + std::to_string(height_max));
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 399287c3e..b1115f67b 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -49,7 +49,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "common/unordered_containers_boost_serialization.h"
-#include "crypto/chacha8.h"
+#include "crypto/chacha.h"
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
@@ -404,7 +404,7 @@ namespace tools
struct keys_file_data
{
- crypto::chacha8_iv iv;
+ crypto::chacha_iv iv;
std::string account_data;
BEGIN_SERIALIZE_OBJECT()
@@ -415,7 +415,7 @@ namespace tools
struct cache_file_data
{
- crypto::chacha8_iv iv;
+ crypto::chacha_iv iv;
std::string cache_data;
BEGIN_SERIALIZE_OBJECT()
@@ -767,6 +767,9 @@ namespace tools
if(ver < 22)
return;
a & m_unconfirmed_payments;
+ if(ver < 23)
+ return;
+ a & m_account_tags;
}
/*!
@@ -863,6 +866,24 @@ namespace tools
void set_description(const std::string &description);
std::string get_description() const;
+ /*!
+ * \brief Get the list of registered account tags.
+ * \return first.Key=(tag's name), first.Value=(tag's label), second[i]=(i-th account's tag)
+ */
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& get_account_tags();
+ /*!
+ * \brief Set a tag to the given accounts.
+ * \param account_indices Indices of accounts.
+ * \param tag Tag's name. If empty, the accounts become untagged.
+ */
+ void set_account_tag(const std::set<uint32_t> account_indices, const std::string& tag);
+ /*!
+ * \brief Set the label of the given tag.
+ * \param tag Tag's name (which must be non-empty).
+ * \param label Tag's description.
+ */
+ void set_account_tag_description(const std::string& tag, const std::string& description);
+
std::string sign(const std::string &data) const;
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const;
@@ -975,7 +996,7 @@ namespace tools
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set<uint32_t>& subaddr_indices);
void generate_genesis(cryptonote::block& b);
void check_genesis(const crypto::hash& genesis_hash) const; //throws
- bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
+ bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
@@ -1025,6 +1046,7 @@ namespace tools
std::unordered_map<crypto::hash, std::string> m_tx_notes;
std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book;
+ std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@@ -1077,7 +1099,7 @@ namespace tools
std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 22)
+BOOST_CLASS_VERSION(tools::wallet2, 23)
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)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0482b9dd6..f031b765d 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -356,13 +356,24 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
- res.addresses.resize(m_wallet->get_num_subaddresses(req.account_index));
+ res.addresses.clear();
+ std::vector<uint32_t> req_address_index;
+ if (req.address_index.empty())
+ {
+ for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
+ req_address_index.push_back(i);
+ }
+ else
+ {
+ req_address_index = req.address_index;
+ }
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
- cryptonote::subaddress_index index = {req.account_index, 0};
- for (; index.minor < m_wallet->get_num_subaddresses(req.account_index); ++index.minor)
+ for (uint32_t i : req_address_index)
{
- auto& info = res.addresses[index.minor];
+ res.addresses.resize(res.addresses.size() + 1);
+ auto& info = res.addresses.back();
+ const cryptonote::subaddress_index index = {req.account_index, i};
info.address = m_wallet->get_subaddress_as_str(index);
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
@@ -418,14 +429,24 @@ namespace tools
res.total_balance = 0;
res.total_unlocked_balance = 0;
cryptonote::subaddress_index subaddr_index = {0,0};
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
+ if (!req.tag.empty() && account_tags.first.count(req.tag) == 0)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = (boost::format(tr("Tag %s is unregistered.")) % req.tag).str();
+ return false;
+ }
for (; subaddr_index.major < m_wallet->get_num_subaddress_accounts(); ++subaddr_index.major)
{
+ if (!req.tag.empty() && req.tag != account_tags.second[subaddr_index.major])
+ continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.balance = m_wallet->balance(subaddr_index.major);
info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major);
info.label = m_wallet->get_subaddress_label(subaddr_index);
+ info.tag = account_tags.second[subaddr_index.major];
res.subaddress_accounts.push_back(info);
res.total_balance += info.balance;
res.total_unlocked_balance += info.unlocked_balance;
@@ -471,6 +492,66 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er)
+ {
+ const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
+ for (const std::pair<std::string, std::string>& p : account_tags.first)
+ {
+ res.account_tags.resize(res.account_tags.size() + 1);
+ auto& info = res.account_tags.back();
+ info.tag = p.first;
+ info.label = p.second;
+ for (size_t i = 0; i < account_tags.second.size(); ++i)
+ {
+ if (account_tags.second[i] == info.tag)
+ info.accounts.push_back(i);
+ }
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, req.tag);
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag(req.accounts, "");
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er)
+ {
+ try
+ {
+ m_wallet->set_account_tag_description(req.tag, req.description);
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -486,7 +567,7 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
+ bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er)
{
crypto::hash8 integrated_payment_id = crypto::null_hash8;
std::string extra_nonce;
@@ -541,6 +622,13 @@ namespace tools
}
}
+ if (at_least_one_destination && dsts.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION;
+ er.message = "No destinations for this transfer";
+ return false;
+ }
+
if (!payment_id.empty())
{
@@ -592,7 +680,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -602,6 +690,13 @@ namespace tools
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ if (ptx_vector.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
+ er.message = "No transaction created";
+ return false;
+ }
+
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
{
@@ -683,7 +778,7 @@ namespace tools
}
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
- if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -880,7 +975,7 @@ namespace tools
destination.push_back(wallet_rpc::transfer_destination());
destination.back().amount = 0;
destination.back().address = req.address;
- if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -979,7 +1074,7 @@ namespace tools
destination.push_back(wallet_rpc::transfer_destination());
destination.back().amount = 0;
destination.back().address = req.address;
- if (!validate_transfer(destination, req.payment_id, dsts, extra, er))
+ if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
{
return false;
}
@@ -1254,6 +1349,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
res.payments.push_back(rpc_payment);
}
@@ -1280,6 +1376,7 @@ namespace tools
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
res.payments.push_back(std::move(rpc_payment));
}
@@ -1330,6 +1427,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
+ rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
res.payments.push_back(std::move(rpc_payment));
}
}
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 79f589623..b20198b78 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -74,6 +74,10 @@ namespace tools
MAP_JON_RPC_WE("get_accounts", on_get_accounts, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS)
MAP_JON_RPC_WE("create_account", on_create_account, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT)
MAP_JON_RPC_WE("label_account", on_label_account, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT)
+ MAP_JON_RPC_WE("get_account_tags", on_get_account_tags, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS)
+ MAP_JON_RPC_WE("tag_accounts", on_tag_accounts, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS)
+ MAP_JON_RPC_WE("untag_accounts", on_untag_accounts, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS)
+ MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION)
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
@@ -136,8 +140,12 @@ namespace tools
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);
bool on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er);
bool on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er);
+ bool on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er);
+ bool on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er);
+ bool on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er);
+ bool on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er);
bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er);
- bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
+ bool validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::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 57cc01e27..76c02039b 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -95,8 +95,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
+ std::vector<uint32_t> address_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
+ KV_SERIALIZE(address_index)
END_KV_SERIALIZE_MAP()
};
@@ -176,7 +178,10 @@ namespace wallet_rpc
{
struct request
{
+ std::string tag; // all accounts if empty, otherwise those accounts with this tag
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -187,6 +192,7 @@ namespace wallet_rpc
uint64_t balance;
uint64_t unlocked_balance;
std::string label;
+ std::string tag;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
@@ -194,6 +200,7 @@ namespace wallet_rpc
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(label)
+ KV_SERIALIZE(tag)
END_KV_SERIALIZE_MAP()
};
@@ -252,6 +259,95 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_GET_ACCOUNT_TAGS
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct account_tag_info
+ {
+ std::string tag;
+ std::string label;
+ std::vector<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag);
+ KV_SERIALIZE(label);
+ KV_SERIALIZE(accounts);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::vector<account_tag_info> account_tags;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(account_tags)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_TAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::string tag;
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_UNTAG_ACCOUNTS
+ {
+ struct request
+ {
+ std::set<uint32_t> accounts;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(accounts)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION
+ {
+ struct request
+ {
+ std::string tag;
+ std::string description;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(tag)
+ KV_SERIALIZE(description)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_HEIGHT
{
struct request
@@ -601,6 +697,7 @@ namespace wallet_rpc
uint64_t block_height;
uint64_t unlock_time;
cryptonote::subaddress_index subaddr_index;
+ std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(payment_id)
@@ -609,6 +706,7 @@ namespace wallet_rpc
KV_SERIALIZE(block_height)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(subaddr_index)
+ KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};