aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/blockchain_db/blockchain_db.cpp24
-rw-r--r--src/blockchain_db/blockchain_db.h13
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp51
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h9
-rw-r--r--src/blockchain_utilities/CMakeLists.txt6
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp117
-rw-r--r--src/blockchain_utilities/blockchain_utilities.h1
-rw-r--r--src/blockchain_utilities/bootstrap_file.cpp104
-rw-r--r--src/blockchain_utilities/bootstrap_file.h2
-rw-r--r--src/blocks/checkpoints.datbin38400036 -> 44480036 bytes
-rw-r--r--src/checkpoints/CMakeLists.txt60
-rw-r--r--src/checkpoints/checkpoints.cpp (renamed from src/cryptonote_basic/checkpoints.cpp)13
-rw-r--r--src/checkpoints/checkpoints.h (renamed from src/cryptonote_basic/checkpoints.h)7
-rw-r--r--src/common/CMakeLists.txt7
-rw-r--r--src/common/apply_permutation.h68
-rw-r--r--src/common/base58.cpp14
-rw-r--r--src/common/command_line.cpp22
-rw-r--r--src/common/command_line.h4
-rw-r--r--src/common/common_fwd.h3
-rw-r--r--src/common/dns_utils.cpp4
-rw-r--r--src/common/sfinae_helpers.h147
-rw-r--r--src/common/task_region.cpp94
-rw-r--r--src/common/task_region.h223
-rw-r--r--src/common/thread_group.cpp153
-rw-r--r--src/common/thread_group.h143
-rw-r--r--src/common/threadpool.cpp117
-rw-r--r--src/common/threadpool.h87
-rw-r--r--src/common/util.cpp76
-rw-r--r--src/common/util.h26
-rw-r--r--src/crypto/crypto.h22
-rw-r--r--src/crypto/hash.h13
-rw-r--r--src/cryptonote_basic/CMakeLists.txt5
-rw-r--r--src/cryptonote_basic/connection_context.h2
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h5
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.h25
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp19
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/miner.cpp13
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/CMakeLists.txt3
-rw-r--r--src/cryptonote_core/blockchain.cpp349
-rw-r--r--src/cryptonote_core/blockchain.h65
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp112
-rw-r--r--src/cryptonote_core/cryptonote_core.h34
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp44
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h2
-rw-r--r--src/cryptonote_core/tx_pool.cpp102
-rw-r--r--src/cryptonote_core/tx_pool.h23
-rw-r--r--src/cryptonote_protocol/CMakeLists.txt2
-rw-r--r--src/cryptonote_protocol/block_queue.cpp2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl60
-rw-r--r--src/daemon/CMakeLists.txt5
-rw-r--r--src/daemon/command_line_args.h24
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp4
-rw-r--r--src/daemon/daemon.cpp41
-rw-r--r--src/daemon/daemon.h2
-rw-r--r--src/daemon/main.cpp8
-rw-r--r--src/daemon/rpc_command_executor.cpp8
-rw-r--r--src/debug_utilities/CMakeLists.txt5
-rw-r--r--src/p2p/CMakeLists.txt3
-rw-r--r--src/p2p/net_node.h6
-rw-r--r--src/p2p/net_node.inl151
-rw-r--r--src/p2p/net_peerlist.h21
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h2
-rw-r--r--src/ringct/rctSigs.cpp49
-rw-r--r--src/rpc/CMakeLists.txt67
-rw-r--r--src/rpc/core_rpc_server.cpp103
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h2
-rw-r--r--src/rpc/daemon_handler.cpp887
-rw-r--r--src/rpc/daemon_handler.h145
-rw-r--r--src/rpc/daemon_messages.cpp900
-rw-r--r--src/rpc/daemon_messages.h421
-rw-r--r--src/rpc/daemon_rpc_version.h44
-rw-r--r--src/rpc/message.cpp286
-rw-r--r--src/rpc/message.h131
-rw-r--r--src/rpc/message_data_structs.h189
-rw-r--r--src/rpc/rpc_handler.h54
-rw-r--r--src/rpc/zmq_server.cpp141
-rw-r--r--src/rpc/zmq_server.h83
-rw-r--r--src/serialization/CMakeLists.txt54
-rw-r--r--src/serialization/json_object.cpp1167
-rw-r--r--src/serialization/json_object.h371
-rw-r--r--src/serialization/serialization.h17
-rw-r--r--src/serialization/vector.h19
-rw-r--r--src/simplewallet/CMakeLists.txt3
-rw-r--r--src/simplewallet/simplewallet.cpp135
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/version.cmake4
-rw-r--r--src/version.cpp.in11
-rw-r--r--src/version.h6
-rw-r--r--src/version.h.in4
-rw-r--r--src/wallet/CMakeLists.txt3
-rw-r--r--src/wallet/api/address_book.cpp6
-rw-r--r--src/wallet/api/unsigned_transaction.cpp8
-rw-r--r--src/wallet/api/wallet.cpp52
-rw-r--r--src/wallet/api/wallet.h3
-rw-r--r--src/wallet/api/wallet_manager.cpp6
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/wallet2.cpp636
-rw-r--r--src/wallet/wallet2.h90
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--src/wallet/wallet_args.cpp36
-rw-r--r--src/wallet/wallet_rpc_server.cpp78
-rw-r--r--src/wallet/wallet_rpc_server.h3
108 files changed, 7475 insertions, 1544 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e473c2984..57ff28bfc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -96,9 +96,13 @@ function (monero_add_library name)
PRIVATE $<TARGET_PROPERTY:${name},INTERFACE_COMPILE_DEFINITIONS>)
endfunction ()
+set_source_files_properties(${CMAKE_BINARY_DIR}/version.cpp PROPERTIES GENERATED ON)
+monero_add_library(version ${CMAKE_BINARY_DIR}/version.cpp)
+
add_subdirectory(common)
add_subdirectory(crypto)
add_subdirectory(ringct)
+add_subdirectory(checkpoints)
add_subdirectory(cryptonote_basic)
add_subdirectory(cryptonote_core)
if(NOT IOS)
@@ -107,6 +111,7 @@ endif()
add_subdirectory(mnemonics)
if(NOT IOS)
add_subdirectory(rpc)
+ add_subdirectory(serialization)
endif()
add_subdirectory(wallet)
if(NOT IOS)
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 01a59e079..d62a250ff 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -78,6 +78,23 @@ std::string blockchain_db_types(const std::string& sep)
return ret;
}
+std::string arg_db_type_description = "Specify database type, available: " + cryptonote::blockchain_db_types(", ");
+const command_line::arg_descriptor<std::string> arg_db_type = {
+ "db-type"
+, arg_db_type_description.c_str()
+, DEFAULT_DB_TYPE
+};
+const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
+ "db-sync-mode"
+, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[nblocks_per_sync]."
+, "fast:async:1000"
+};
+const command_line::arg_descriptor<bool> arg_db_salvage = {
+ "db-salvage"
+, "Try to salvage a blockchain database if it seems corrupted"
+, false
+};
+
BlockchainDB *new_db(const std::string& db_type)
{
if (db_type == "lmdb")
@@ -89,6 +106,13 @@ BlockchainDB *new_db(const std::string& db_type)
return NULL;
}
+void BlockchainDB::init_options(boost::program_options::options_description& desc)
+{
+ command_line::add_arg(desc, arg_db_type);
+ command_line::add_arg(desc, arg_db_sync_mode);
+ command_line::add_arg(desc, arg_db_salvage);
+}
+
void BlockchainDB::pop_block()
{
block blk;
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index ad246d85e..838385e8a 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -33,6 +33,8 @@
#include <list>
#include <string>
#include <exception>
+#include <boost/program_options.hpp>
+#include "common/command_line.h"
#include "crypto/hash.h"
#include "cryptonote_protocol/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"
@@ -101,6 +103,10 @@ namespace cryptonote
/** a pair of <transaction hash, output index>, typedef for convenience */
typedef std::pair<crypto::hash, uint64_t> tx_out_index;
+extern const command_line::arg_descriptor<std::string> arg_db_type;
+extern const command_line::arg_descriptor<std::string> arg_db_sync_mode;
+extern const command_line::arg_descriptor<bool, false> arg_db_salvage;
+
#pragma pack(push, 1)
/**
@@ -536,6 +542,11 @@ public:
virtual ~BlockchainDB() { };
/**
+ * @brief init command line options
+ */
+ static void init_options(boost::program_options::options_description& desc);
+
+ /**
* @brief reset profiling stats
*/
void reset_stats();
@@ -700,7 +711,7 @@ public:
*
* @return true if we started the batch, false if already started
*/
- virtual bool batch_start(uint64_t batch_num_blocks=0) = 0;
+ virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) = 0;
/**
* @brief ends a batch transaction
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 4100d9cca..985244f6b 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -548,7 +548,7 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const
#endif
}
-void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks)
+void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
LOG_PRINT_L1("[" << __func__ << "] " << "checking DB size");
@@ -557,7 +557,7 @@ void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks)
uint64_t increase_size = 0;
if (batch_num_blocks > 0)
{
- threshold_size = get_estimated_batch_size(batch_num_blocks);
+ threshold_size = get_estimated_batch_size(batch_num_blocks, batch_bytes);
LOG_PRINT_L1("calculated batch size: " << threshold_size);
// The increased DB size could be a multiple of threshold_size, a fixed
@@ -580,7 +580,7 @@ void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks)
}
}
-uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) const
+uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
uint64_t threshold_size = 0;
@@ -607,6 +607,11 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con
uint64_t total_block_size = 0;
LOG_PRINT_L1("[" << __func__ << "] " << "m_height: " << m_height << " block_start: " << block_start << " block_stop: " << block_stop);
size_t avg_block_size = 0;
+ if (batch_bytes)
+ {
+ avg_block_size = batch_bytes / batch_num_blocks;
+ goto estim;
+ }
if (m_height == 0)
{
LOG_PRINT_L1("No existing blocks to check for average block size");
@@ -635,6 +640,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con
avg_block_size = total_block_size / num_blocks_used;
LOG_PRINT_L1("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size);
}
+estim:
if (avg_block_size < min_block_size)
avg_block_size = min_block_size;
LOG_PRINT_L1("estimated average block size for batch: " << avg_block_size);
@@ -2420,8 +2426,8 @@ bool BlockchainLMDB::for_blocks_range(const uint64_t& h1, const uint64_t& h2, st
MDB_cursor_op op;
if (h1)
{
- MDB_val_set(k, h1);
- op = MDB_SET;
+ k = MDB_val{sizeof(h1), (void*)&h1};
+ op = MDB_SET;
} else
{
op = MDB_FIRST;
@@ -2540,7 +2546,7 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c
}
// batch_num_blocks: (optional) Used to check if resize needed before batch transaction starts.
-bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
+bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks, uint64_t batch_bytes)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
if (! m_batch_transactions)
@@ -2554,7 +2560,7 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
check_open();
m_writer = boost::this_thread::get_id();
- check_and_resize_for_batch(batch_num_blocks);
+ check_and_resize_for_batch(batch_num_blocks, batch_bytes);
m_write_batch_txn = new mdb_txn_safe();
@@ -2604,6 +2610,16 @@ void BlockchainLMDB::batch_commit()
memset(&m_wcursors, 0, sizeof(m_wcursors));
}
+void BlockchainLMDB::cleanup_batch()
+{
+ // for destruction of batch transaction
+ m_write_txn = nullptr;
+ delete m_write_batch_txn;
+ m_write_batch_txn = nullptr;
+ m_batch_active = false;
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
+}
+
void BlockchainLMDB::batch_stop()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -2618,15 +2634,18 @@ void BlockchainLMDB::batch_stop()
check_open();
LOG_PRINT_L3("batch transaction: committing...");
TIME_MEASURE_START(time1);
- m_write_txn->commit();
- TIME_MEASURE_FINISH(time1);
- time_commit1 += time1;
- // for destruction of batch transaction
- m_write_txn = nullptr;
- delete m_write_batch_txn;
- m_write_batch_txn = nullptr;
- m_batch_active = false;
- memset(&m_wcursors, 0, sizeof(m_wcursors));
+ try
+ {
+ m_write_txn->commit();
+ TIME_MEASURE_FINISH(time1);
+ time_commit1 += time1;
+ cleanup_batch();
+ }
+ catch (const std::exception &e)
+ {
+ cleanup_batch();
+ throw;
+ }
LOG_PRINT_L3("batch transaction: end");
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 3a11ddf0d..98571a7f8 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -264,7 +264,7 @@ public:
);
virtual void set_batch_transactions(bool batch_transactions);
- virtual bool batch_start(uint64_t batch_num_blocks=0);
+ virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0);
virtual void batch_commit();
virtual void batch_stop();
virtual void batch_abort();
@@ -294,8 +294,8 @@ private:
void do_resize(uint64_t size_increase=0);
bool need_resize(uint64_t threshold_size=0) const;
- void check_and_resize_for_batch(uint64_t batch_num_blocks);
- uint64_t get_estimated_batch_size(uint64_t batch_num_blocks) const;
+ void check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes);
+ uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
virtual void add_block( const block& blk
, const size_t& block_size
@@ -368,6 +368,9 @@ private:
// migrate from DB version 0 to 1
void migrate_0_1();
+ void cleanup_batch();
+
+private:
MDB_env* m_env;
MDB_dbi m_blocks;
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index ffdaad4af..0eaf71084 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -77,6 +77,7 @@ target_link_libraries(blockchain_import
cryptonote_core
blockchain_db
p2p
+ version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
@@ -89,8 +90,6 @@ if(ARCH_WIDTH)
PUBLIC -DARCH_WIDTH=${ARCH_WIDTH})
endif()
-add_dependencies(blockchain_import
- version)
set_property(TARGET blockchain_import
PROPERTY
OUTPUT_NAME "monero-blockchain-import")
@@ -104,6 +103,7 @@ target_link_libraries(blockchain_export
cryptonote_core
blockchain_db
p2p
+ version
epee
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
@@ -111,8 +111,6 @@ target_link_libraries(blockchain_export
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
-add_dependencies(blockchain_export
- version)
set_property(TARGET blockchain_export
PROPERTY
OUTPUT_NAME "monero-blockchain-export")
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index ded854ca4..d6302ea1d 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -208,7 +208,8 @@ int check_flush(cryptonote::core &core, std::list<block_complete_entry> &blocks,
}
} // each download block
- core.cleanup_handle_incoming_blocks();
+ if (!core.cleanup_handle_incoming_blocks())
+ return 1;
blocks.clear();
return 0;
@@ -229,11 +230,22 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
return false;
}
+ uint64_t start_height = 1, seek_height;
+ if (opt_resume)
+ start_height = core.get_blockchain_storage().get_current_blockchain_height();
+
+ seek_height = start_height;
BootstrapFile bootstrap;
+ streampos pos;
// BootstrapFile bootstrap(import_file_path);
- uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path);
+ uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path, pos, seek_height);
MINFO("bootstrap file last block number: " << total_source_blocks-1 << " (zero-based height) total blocks: " << total_source_blocks);
+ if (total_source_blocks-1 <= start_height)
+ {
+ return false;
+ }
+
std::cout << ENDL;
std::cout << "Preparing to read blocks..." << ENDL;
std::cout << ENDL;
@@ -258,11 +270,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
block b;
transaction tx;
int quit = 0;
- uint64_t bytes_read = 0;
-
- uint64_t start_height = 1;
- if (opt_resume)
- start_height = core.get_blockchain_storage().get_current_blockchain_height();
+ uint64_t bytes_read;
// Note that a new blockchain will start with block number 0 (total blocks: 1)
// due to genesis block being added at initialization.
@@ -279,18 +287,35 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
bool use_batch = opt_batch && !opt_verify;
- if (use_batch)
- core.get_blockchain_storage().get_db().batch_start(db_batch_size);
-
MINFO("Reading blockchain from bootstrap file...");
std::cout << ENDL;
std::list<block_complete_entry> blocks;
- // Within the loop, we skip to start_height before we start adding.
- // TODO: Not a bottleneck, but we can use what's done in count_blocks() and
- // only do the chunk size reads, skipping the chunk content reads until we're
- // at start_height.
+ // Skip to start_height before we start adding.
+ {
+ bool q2 = false;
+ import_file.seekg(pos);
+ bytes_read = bootstrap.count_bytes(import_file, start_height-seek_height, h, q2);
+ if (q2)
+ {
+ quit = 2;
+ goto quitting;
+ }
+ h = start_height;
+ }
+
+ if (use_batch)
+ {
+ uint64_t bytes, h2;
+ bool q2;
+ pos = import_file.tellg();
+ bytes = bootstrap.count_bytes(import_file, db_batch_size, h2, q2);
+ if (import_file.eof())
+ import_file.clear();
+ import_file.seekg(pos);
+ core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes);
+ }
while (! quit)
{
uint32_t chunk_size;
@@ -316,9 +341,9 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
MWARNING("WARNING: chunk_size " << chunk_size << " > BUFFER_SIZE " << BUFFER_SIZE);
throw std::runtime_error("Aborting: chunk size exceeds buffer size");
}
- if (chunk_size > 100000)
+ if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD)
{
- MINFO("NOTE: chunk_size " << chunk_size << " > 100000");
+ MINFO("NOTE: chunk_size " << chunk_size << " > " << CHUNK_SIZE_WARNING_THRESHOLD);
}
else if (chunk_size == 0) {
MFATAL("ERROR: chunk_size == 0");
@@ -326,18 +351,23 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
}
import_file.read(buffer_block, chunk_size);
if (! import_file) {
- MFATAL("ERROR: unexpected end of file: bytes read before error: "
- << import_file.gcount() << " of chunk_size " << chunk_size);
- return 2;
+ if (import_file.eof())
+ {
+ std::cout << refresh_string;
+ MINFO("End of file reached - file was truncated");
+ quit = 1;
+ break;
+ }
+ else
+ {
+ MFATAL("ERROR: unexpected end of file: bytes read before error: "
+ << import_file.gcount() << " of chunk_size " << chunk_size);
+ return 2;
+ }
}
bytes_read += chunk_size;
MDEBUG("Total bytes read: " << bytes_read);
- if (h + NUM_BLOCKS_PER_CHUNK < start_height + 1)
- {
- h += NUM_BLOCKS_PER_CHUNK;
- continue;
- }
if (h > block_stop)
{
std::cout << refresh_string << "block " << h-1
@@ -394,7 +424,10 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
blocks.push_back({block, txs});
int ret = check_flush(core, blocks, false);
if (ret)
+ {
+ quit = 2; // make sure we don't commit partial block data
break;
+ }
}
else
{
@@ -442,11 +475,16 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
{
if ((h-1) % db_batch_size == 0)
{
+ uint64_t bytes, h2;
+ bool q2;
std::cout << refresh_string;
// zero-based height
std::cout << ENDL << "[- batch commit at height " << h-1 << " -]" << ENDL;
core.get_blockchain_storage().get_db().batch_stop();
- core.get_blockchain_storage().get_db().batch_start(db_batch_size);
+ pos = import_file.tellg();
+ bytes = bootstrap.count_bytes(import_file, db_batch_size, h2, q2);
+ import_file.seekg(pos);
+ core.get_blockchain_storage().get_db().batch_start(db_batch_size, bytes);
std::cout << ENDL;
core.get_blockchain_storage().get_db().show_stats();
}
@@ -463,6 +501,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
}
} // while
+quitting:
import_file.close();
if (opt_verify)
@@ -682,18 +721,12 @@ int main(int argc, char* argv[])
MINFO("bootstrap file path: " << import_file_path);
MINFO("database path: " << m_config_folder);
+ cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
+ cryptonote::core core(&pr);
+
try
{
- // fake_core needed for verification to work when enabled.
- //
- // NOTE: don't need fake_core method of doing things when we're going to call
- // BlockchainDB add_block() directly and have available the 3 block
- // properties to do so. Both ways work, but fake core isn't necessary in that
- // circumstance.
-
- cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects
- cryptonote::core core(&pr);
core.disable_dns_checkpoints(true);
if (!core.init(vm, NULL))
{
@@ -721,23 +754,19 @@ int main(int argc, char* argv[])
import_from_file(core, import_file_path, block_stop);
+ // ensure db closed
+ // - transactions properly checked and handled
+ // - disk sync if needed
+ //
+ core.deinit();
}
catch (const DB_ERROR& e)
{
std::cout << std::string("Error loading blockchain db: ") + e.what() + " -- shutting down now" << ENDL;
+ core.deinit();
return 1;
}
- // destructors called at exit:
- //
- // ensure db closed
- // - transactions properly checked and handled
- // - disk sync if needed
- //
- // fake_core object's destructor is called when it goes out of scope. For an
- // LMDB fake_core, it calls Blockchain::deinit() on its object, which in turn
- // calls delete on its BlockchainDB derived class' object, which closes its
- // files.
return 0;
CATCH_ENTRY("Import error", 1);
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index af934bf29..6fb5e1131 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -34,6 +34,7 @@
// bounds checking is done before writing to buffer, but buffer size
// should be a sensible maximum
#define BUFFER_SIZE 1000000
+#define CHUNK_SIZE_WARNING_THRESHOLD 500000
#define NUM_BLOCKS_PER_CHUNK 1
#define BLOCKCHAIN_RAW "blockchain.raw"
diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp
index d5bb37d93..a004d3547 100644
--- a/src/blockchain_utilities/bootstrap_file.cpp
+++ b/src/blockchain_utilities/bootstrap_file.cpp
@@ -375,39 +375,15 @@ uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file)
return full_header_size;
}
-uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
+uint64_t BootstrapFile::count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit)
{
- boost::filesystem::path raw_file_path(import_file_path);
- boost::system::error_code ec;
- if (!boost::filesystem::exists(raw_file_path, ec))
- {
- MFATAL("bootstrap file not found: " << raw_file_path);
- throw std::runtime_error("Aborting");
- }
- std::ifstream import_file;
- import_file.open(import_file_path, std::ios_base::binary | std::ifstream::in);
-
- uint64_t h = 0;
- if (import_file.fail())
- {
- MFATAL("import_file.open() fail");
- throw std::runtime_error("Aborting");
- }
-
- uint64_t full_header_size; // 4 byte magic + length of header structures
- full_header_size = seek_to_first_chunk(import_file);
-
- MINFO("Scanning blockchain from bootstrap file...");
- block b;
- bool quit = false;
uint64_t bytes_read = 0;
- int progress_interval = 10;
-
+ uint32_t chunk_size;
+ char buf1[sizeof(chunk_size)];
std::string str1;
- char buf1[2048];
- while (! quit)
+ h = 0;
+ while (1)
{
- uint32_t chunk_size;
import_file.read(buf1, sizeof(chunk_size));
if (!import_file) {
std::cout << refresh_string;
@@ -415,15 +391,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
quit = true;
break;
}
- h += NUM_BLOCKS_PER_CHUNK;
- if ((h-1) % progress_interval == 0)
- {
- std::cout << "\r" << "block height: " << h-1 <<
- " " <<
- std::flush;
- }
bytes_read += sizeof(chunk_size);
-
str1.assign(buf1, sizeof(chunk_size));
if (! ::serialization::parse_binary(str1, chunk_size))
throw std::runtime_error("Error in deserialization of chunk_size");
@@ -436,10 +404,10 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
<< " height: " << h-1);
throw std::runtime_error("Aborting: chunk size exceeds buffer size");
}
- if (chunk_size > 100000)
+ if (chunk_size > CHUNK_SIZE_WARNING_THRESHOLD)
{
std::cout << refresh_string;
- MDEBUG("NOTE: chunk_size " << chunk_size << " > 100000" << " height: "
+ MDEBUG("NOTE: chunk_size " << chunk_size << " > " << CHUNK_SIZE_WARNING_THRESHOLD << " << height: "
<< h-1);
}
else if (chunk_size <= 0) {
@@ -456,6 +424,64 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
throw std::runtime_error("Aborting");
}
bytes_read += chunk_size;
+ h += NUM_BLOCKS_PER_CHUNK;
+ if (h >= blocks)
+ break;
+ }
+ return bytes_read;
+}
+
+uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
+{
+ streampos dummy_pos;
+ uint64_t dummy_height = 0;
+ return count_blocks(import_file_path, dummy_pos, dummy_height);
+}
+
+// If seek_height is non-zero on entry, return a stream position <= this height when finished.
+// And return the actual height corresponding to this position. Allows the caller to locate its
+// starting position without having to reread the entire file again.
+uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, streampos &start_pos, uint64_t& seek_height)
+{
+ boost::filesystem::path raw_file_path(import_file_path);
+ boost::system::error_code ec;
+ if (!boost::filesystem::exists(raw_file_path, ec))
+ {
+ MFATAL("bootstrap file not found: " << raw_file_path);
+ throw std::runtime_error("Aborting");
+ }
+ std::ifstream import_file;
+ import_file.open(import_file_path, std::ios_base::binary | std::ifstream::in);
+
+ uint64_t start_height = seek_height;
+ uint64_t h = 0;
+ if (import_file.fail())
+ {
+ MFATAL("import_file.open() fail");
+ throw std::runtime_error("Aborting");
+ }
+
+ uint64_t full_header_size; // 4 byte magic + length of header structures
+ full_header_size = seek_to_first_chunk(import_file);
+
+ MINFO("Scanning blockchain from bootstrap file...");
+ bool quit = false;
+ uint64_t bytes_read = 0, blocks;
+ int progress_interval = 10;
+
+ while (! quit)
+ {
+ if (start_height && h + progress_interval >= start_height - 1)
+ {
+ start_height = 0;
+ start_pos = import_file.tellg();
+ seek_height = h;
+ }
+ bytes_read += count_bytes(import_file, progress_interval, blocks, quit);
+ h += blocks;
+ std::cout << "\r" << "block height: " << h-1 <<
+ " " <<
+ std::flush;
// std::cout << refresh_string;
MDEBUG("Number bytes scanned: " << bytes_read);
diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h
index 1a646b54b..c3969a357 100644
--- a/src/blockchain_utilities/bootstrap_file.h
+++ b/src/blockchain_utilities/bootstrap_file.h
@@ -56,6 +56,8 @@ class BootstrapFile
{
public:
+ uint64_t count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit);
+ uint64_t count_blocks(const std::string& dir_path, streampos& start_pos, uint64_t& seek_height);
uint64_t count_blocks(const std::string& dir_path);
uint64_t seek_to_first_chunk(std::ifstream& import_file);
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index a15f53e67..15fa042cf 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt
new file mode 100644
index 000000000..bc7a27e36
--- /dev/null
+++ b/src/checkpoints/CMakeLists.txt
@@ -0,0 +1,60 @@
+# Copyright (c) 2014-2017, The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+if(APPLE)
+ find_library(IOKIT_LIBRARY IOKit)
+ mark_as_advanced(IOKIT_LIBRARY)
+ list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY})
+endif()
+
+set(checkpoints_sources
+ checkpoints.cpp)
+
+set(checkpoints_headers)
+
+set(checkpoints_private_headers
+ checkpoints.h)
+
+monero_private_headers(checkpoints
+ ${checkpoints_private_headers})
+monero_add_library(checkpoints
+ ${checkpoints_sources}
+ ${checkpoints_headers}
+ ${checkpoints_private_headers})
+target_link_libraries(checkpoints
+ PUBLIC
+ common
+ cncrypto
+ ${Boost_DATE_TIME_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ PRIVATE
+ ${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_basic/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 103a4a33e..bea392db0 100644
--- a/src/cryptonote_basic/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -36,6 +36,7 @@ using namespace epee;
#include "common/dns_utils.h"
#include "include_base_utils.h"
+#include "storages/portable_storage_template_helper.h" // epee json include
#include <sstream>
#include <random>
@@ -51,7 +52,7 @@ namespace cryptonote
//---------------------------------------------------------------------------
bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str)
{
- crypto::hash h = null_hash;
+ crypto::hash h = crypto::null_hash;
bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse checkpoint hash string into binary representation!");
@@ -135,8 +136,14 @@ namespace cryptonote
return true;
}
- bool checkpoints::init_default_checkpoints()
+ bool checkpoints::init_default_checkpoints(bool testnet)
{
+ if (testnet)
+ {
+ // just use the genesis block on testnet
+ ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b");
+ return true;
+ }
ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148");
ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381");
ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d");
@@ -167,6 +174,8 @@ namespace cryptonote
ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903");
ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2");
ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f");
+ ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0");
+ ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c");
return true;
diff --git a/src/cryptonote_basic/checkpoints.h b/src/checkpoints/checkpoints.h
index 3a74d8a69..a643c5790 100644
--- a/src/cryptonote_basic/checkpoints.h
+++ b/src/checkpoints/checkpoints.h
@@ -31,9 +31,9 @@
#pragma once
#include <map>
#include <vector>
-#include "cryptonote_basic_impl.h"
#include "misc_log_ex.h"
-#include "storages/portable_storage_template_helper.h" // epee json include
+#include "crypto/hash.h"
+#include "serialization/keyvalue_serialization.h"
#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false);
#define JSON_HASH_FILE_NAME "checkpoints.json"
@@ -148,10 +148,11 @@ namespace cryptonote
/**
* @brief loads the default main chain checkpoints
+ * @param testnet whether to load testnet checkpoints or mainnet
*
* @return true unless adding a checkpoint fails
*/
- bool init_default_checkpoints();
+ bool init_default_checkpoints(bool testnet);
/**
* @brief load new checkpoints
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 55b8ad3e6..50887e35c 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -37,8 +37,7 @@ set(common_sources
i18n.cpp
password.cpp
perf_timer.cpp
- task_region.cpp
- thread_group.cpp
+ threadpool.cpp
updates.cpp)
if (STACK_TRACE)
@@ -48,6 +47,7 @@ endif()
set(common_headers)
set(common_private_headers
+ apply_permutation.h
base58.h
boost_serialization_helper.h
command_line.h
@@ -66,8 +66,7 @@ set(common_private_headers
password.h
perf_timer.h
stack_trace.h
- task_region.h
- thread_group.h
+ threadpool.h
updates.h)
monero_private_headers(common
diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h
new file mode 100644
index 000000000..4fd952686
--- /dev/null
+++ b/src/common/apply_permutation.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Most of this file is originally copyright (c) 2017 Raymond Chen, Microsoft
+// This algorithm is adapted from Raymond Chen's code:
+// https://blogs.msdn.microsoft.com/oldnewthing/20170109-00/?p=95145
+
+#include <vector>
+#include <functional>
+#include "misc_log_ex.h"
+
+namespace tools
+{
+
+template<typename F>
+void apply_permutation(std::vector<size_t> permutation, const F &swap)
+{
+ //sanity check
+ for (size_t n = 0; n < permutation.size(); ++n)
+ CHECK_AND_ASSERT_THROW_MES(std::find(permutation.begin(), permutation.end(), n) != permutation.end(), "Bad permutation");
+
+ for (size_t i = 0; i < permutation.size(); ++i)
+ {
+ size_t current = i;
+ while (i != permutation[current])
+ {
+ size_t next = permutation[current];
+ swap(current, next);
+ permutation[current] = current;
+ current = next;
+ }
+ permutation[current] = current;
+ }
+}
+
+template<typename T>
+void apply_permutation(const std::vector<size_t> &permutation, std::vector<T> &v)
+{
+ CHECK_AND_ASSERT_THROW_MES(permutation.size() == v.size(), "Mismatched vector sizes");
+ apply_permutation(permutation, [&v](size_t i0, size_t i1){ std::swap(v[i0], v[i1]); });
+}
+
+}
diff --git a/src/common/base58.cpp b/src/common/base58.cpp
index 64cb7c0de..941373443 100644
--- a/src/common/base58.cpp
+++ b/src/common/base58.cpp
@@ -111,13 +111,13 @@ namespace tools
uint64_t res = 0;
switch (9 - size)
{
- case 1: res |= *data++;
- case 2: res <<= 8; res |= *data++;
- case 3: res <<= 8; res |= *data++;
- case 4: res <<= 8; res |= *data++;
- case 5: res <<= 8; res |= *data++;
- case 6: res <<= 8; res |= *data++;
- case 7: res <<= 8; res |= *data++;
+ case 1: res |= *data++; /* FALLTHRU */
+ case 2: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 3: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 4: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 5: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 6: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 7: res <<= 8; res |= *data++; /* FALLTHRU */
case 8: res <<= 8; res |= *data; break;
default: assert(false);
}
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index ad66b2009..666b3267f 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -32,7 +32,6 @@
#include <boost/algorithm/string/compare.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <unordered_set>
-#include "blockchain_db/db_types.h"
#include "common/i18n.h"
#include "cryptonote_config.h"
#include "string_tools.h"
@@ -96,22 +95,6 @@ namespace command_line
, "checkpoints from DNS server will be enforced"
, false
};
- std::string arg_db_type_description = "Specify database type, available: " + cryptonote::blockchain_db_types(", ");
- const command_line::arg_descriptor<std::string> arg_db_type = {
- "db-type"
- , arg_db_type_description.c_str()
- , DEFAULT_DB_TYPE
- };
- const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
- "db-sync-mode"
- , "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[nblocks_per_sync]."
- , "fast:async:1000"
- };
- const arg_descriptor<bool> arg_db_salvage = {
- "db-salvage"
- , "Try to salvage a blockchain database if it seems corrupted"
- , false
- };
const command_line::arg_descriptor<uint64_t> arg_fast_block_sync = {
"fast-block-sync"
, "Sync up most of the way by using embedded, known block hashes."
@@ -137,4 +120,9 @@ namespace command_line
, "Check for new versions of monero: [disabled|notify|download|update]"
, "notify"
};
+ const arg_descriptor<bool> arg_fluffy_blocks = {
+ "fluffy-blocks"
+ , "Relay blocks as fluffy blocks where possible (automatic on testnet)"
+ , false
+ };
}
diff --git a/src/common/command_line.h b/src/common/command_line.h
index 03ba35a5b..d4231acd0 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -212,12 +212,10 @@ namespace command_line
extern const arg_descriptor<int> arg_test_dbg_lock_sleep;
extern const arg_descriptor<bool, false> arg_testnet_on;
extern const arg_descriptor<bool> arg_dns_checkpoints;
- extern const arg_descriptor<std::string> arg_db_type;
- extern const arg_descriptor<std::string> arg_db_sync_mode;
- extern const arg_descriptor<bool, false> arg_db_salvage;
extern const arg_descriptor<uint64_t> arg_fast_block_sync;
extern const arg_descriptor<uint64_t> arg_prep_blocks_threads;
extern const arg_descriptor<uint64_t> arg_show_time_stats;
extern const arg_descriptor<size_t> arg_block_sync_size;
extern const arg_descriptor<std::string> arg_check_updates;
+ extern const arg_descriptor<bool> arg_fluffy_blocks;
}
diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h
index 5d67251b1..f33e185b5 100644
--- a/src/common/common_fwd.h
+++ b/src/common/common_fwd.h
@@ -36,6 +36,5 @@ namespace tools
struct login;
class password_container;
class t_http_connection;
- class task_region;
- class thread_group;
+ class threadpool;
}
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index e7ff11c5c..9c306505e 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -27,8 +27,6 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/dns_utils.h"
-#include "common/i18n.h"
-#include "cryptonote_basic/cryptonote_basic_impl.h"
// check local first (in the event of static or in-source compilation of libunbound)
#include "unbound.h"
@@ -326,8 +324,6 @@ bool DNSResolver::check_address_syntax(const char *addr) const
namespace dns_utils
{
-const char *tr(const char *str) { return i18n_translate(str, "tools::dns_utils"); }
-
//-----------------------------------------------------------------------
// TODO: parse the string in a less stupid way, probably with regex
std::string address_from_txt_record(const std::string& s)
diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h
new file mode 100644
index 000000000..ddd456dd2
--- /dev/null
+++ b/src/common/sfinae_helpers.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+// the loose definitions of types in this file are, well, loose.
+//
+// these helpers aren't here for absolute type certainty at compile-time,
+// but rather to help with templated functions telling types apart.
+
+namespace sfinae
+{
+
+ typedef char true_type;
+
+ struct false_type { true_type a[2]; };
+
+ template <typename T>
+ struct is_not_container
+ {
+ private:
+
+ // does not have const iterator
+ template <typename C> static false_type c_iter(typename C::const_iterator*);
+ template <typename C> static true_type c_iter(...);
+
+ // does not have value_type
+ template <typename C> static false_type v_type(typename C::value_type*);
+ template <typename C> static true_type v_type(...);
+
+ // does not have key_type
+ template <typename C> static false_type k_type(typename C::key_type*);
+ template <typename C> static true_type k_type(...);
+
+ // does not have mapped_type
+ template <typename C> static false_type m_type(typename C::mapped_type*);
+ template <typename C> static true_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type)
+ )
+ || std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+ template <typename T>
+ struct is_vector_like
+ {
+ private:
+
+ // has const iterator
+ template <typename C> static true_type c_iter(typename C::const_iterator*);
+ template <typename C> static false_type c_iter(...);
+
+ // has value_type
+ template <typename C> static true_type v_type(typename C::value_type*);
+ template <typename C> static false_type v_type(...);
+
+ // does not have key_type
+ template <typename C> static false_type k_type(typename C::key_type*);
+ template <typename C> static true_type k_type(...);
+
+ // does not have mapped_type
+ template <typename C> static false_type m_type(typename C::mapped_type*);
+ template <typename C> static true_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type) &&
+ !std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+ template <typename T>
+ struct is_map_like
+ {
+ private:
+
+ // has const iterator
+ template <typename C> static true_type c_iter(typename C::const_iterator*);
+ template <typename C> static false_type c_iter(...);
+
+ // has value_type
+ template <typename C> static true_type v_type(typename C::value_type*);
+ template <typename C> static false_type v_type(...);
+
+ // has key_type
+ template <typename C> static true_type k_type(typename C::key_type*);
+ template <typename C> static false_type k_type(...);
+
+ // has mapped_type
+ template <typename C> static true_type m_type(typename C::mapped_type*);
+ template <typename C> static false_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type) &&
+ !std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+} // namespace sfinae
diff --git a/src/common/task_region.cpp b/src/common/task_region.cpp
deleted file mode 100644
index 9b4620c6e..000000000
--- a/src/common/task_region.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2014-2017, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "common/task_region.h"
-
-#include <boost/thread/locks.hpp>
-#include <cassert>
-
-/* `mark_completed` and `wait` can throw in the lock call, but its difficult to
-recover from either. An exception in `wait` means the post condition of joining
-all threads cannot be achieved, and an exception in `mark_completed` means
-certain deadlock. `noexcept` qualifier will force a call to `std::terminate` if
-locking throws an exception, which should only happen if a recursive lock
-attempt is made (which is not possible since no external function is called
-while holding the lock). */
-
-namespace tools
-{
-void task_region_handle::state::mark_completed(id task_id) noexcept {
- assert(task_id != 0 && (task_id & (task_id - 1)) == 0); // power of 2 check
- if (pending.fetch_and(~task_id) == task_id) {
- // synchronize with wait call, but do not need to hold
- boost::unique_lock<boost::mutex>{sync_on_complete};
- all_complete.notify_all();
- }
-}
-
-void task_region_handle::state::abort() noexcept {
- state* current = this;
- while (current) {
- current->ready = 0;
- current = current->next.get();
- }
-}
-
-void task_region_handle::state::wait() noexcept {
- state* current = this;
- while (current) {
- {
- boost::unique_lock<boost::mutex> lock{current->sync_on_complete};
- current->all_complete.wait(lock, [current] { return current->pending == 0; });
- }
- current = current->next.get();
- }
-}
-
-void task_region_handle::state::wait(thread_group& threads) noexcept {
- state* current = this;
- while (current) {
- while (current->pending != 0) {
- if (!threads.try_run_one()) {
- current->wait();
- return;
- }
- }
- current = current->next.get();
- }
-}
-
-void task_region_handle::create_state() {
- st = std::make_shared<state>(std::move(st));
- next_id = 1;
-}
-
-void task_region_handle::do_wait() noexcept {
- assert(st);
- const std::shared_ptr<state> temp = std::move(st);
- temp->wait(threads);
-}
-}
diff --git a/src/common/task_region.h b/src/common/task_region.h
deleted file mode 100644
index 30972cce3..000000000
--- a/src/common/task_region.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2014-2017, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#pragma once
-
-#include <atomic>
-#include <boost/thread/condition_variable.hpp>
-#include <boost/thread/mutex.hpp>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "common/thread_group.h"
-
-namespace tools
-{
-
-/*! A model of the fork-join concept. `run(...)` "forks" (i.e. spawns new
-tasks), and `~task_region_handle()` or `wait()` "joins" the spawned tasks.
-`wait` will block until all tasks have completed, while `~task_region_handle()`
-blocks until all tasks have completed or aborted.
-
-Do _NOT_ give this object to separate thread of execution (which includes
-`task_region_handle::run(...)`) because joining on a different thread is
-undesireable (potential deadlock).
-
-This class cannot be constructed directly, use the function
-`task_region(...)` instead.
-*/
-class task_region_handle
-{
- struct state
- {
- using id = unsigned;
-
- explicit state(std::shared_ptr<state> next_src) noexcept
- : next(std::move(next_src))
- , ready(0)
- , pending(0)
- , sync_on_complete()
- , all_complete() {
- }
-
- state(const state&) = default;
- state(state&&) = default;
- ~state() = default;
- state& operator=(const state&) = default;
- state& operator=(state&&) = default;
-
- void track_id(id task_id) noexcept {
- pending |= task_id;
- ready |= task_id;
- }
-
- //! \return True only once whether a given id can execute
- bool can_run(id task_id) noexcept {
- return (ready.fetch_and(~task_id) & task_id);
- }
-
- //! Mark id as completed, and synchronize with waiting threads
- void mark_completed(id task_id) noexcept;
-
- //! Tell all unstarted functions in region to return immediately
- void abort() noexcept;
-
- //! Blocks until all functions in region have aborted or completed.
- void wait() noexcept;
-
- //! Same as `wait()`, except `this_thread` runs tasks while waiting.
- void wait(thread_group& threads) noexcept;
-
- private:
- /* This implementation is a bit pessimistic, it ensures that all copies
- of a wrapped task can only be executed once. `thread_group` should never
- do this, but some variable needs to track whether an abort should be done
- anyway... */
- std::shared_ptr<state> next;
- std::atomic<id> ready; //!< Tracks whether a task has been invoked
- std::atomic<id> pending; //!< Tracks when a task has completed or aborted
- boost::mutex sync_on_complete;
- boost::condition_variable all_complete;
- };
-
- template<typename F>
- struct wrapper
- {
- wrapper(state::id id_src, std::shared_ptr<state> st_src, F f_src)
- : task_id(id_src), st(std::move(st_src)), f(std::move(f_src)) {
- }
-
- wrapper(const wrapper&) = default;
- wrapper(wrapper&&) = default;
- wrapper& operator=(const wrapper&) = default;
- wrapper& operator=(wrapper&&) = default;
-
- void operator()() {
- if (st) {
- if (st->can_run(task_id)) {
- f();
- }
- st->mark_completed(task_id);
- }
- }
-
- private:
- const state::id task_id;
- std::shared_ptr<state> st;
- F f;
- };
-
-public:
- friend struct task_region_;
-
- task_region_handle() = delete;
- task_region_handle(const task_region_handle&) = delete;
- task_region_handle(task_region_handle&&) = delete;
-
- //! Cancels unstarted pending tasks, and waits for them to respond.
- ~task_region_handle() noexcept {
- if (st) {
- st->abort();
- st->wait(threads);
- }
- }
-
- task_region_handle& operator=(const task_region_handle&) = delete;
- task_region_handle& operator=(task_region_handle&&) = delete;
-
- /*! If the group has no threads, `f` is immediately run before returning.
- Otherwise, `f` is dispatched to the thread_group associated with `this`
- region. If `f` is dispatched to another thread, and it throws, the process
- will immediately terminate. See std::packaged_task for getting exceptions on
- functions executed on other threads. */
- template<typename F>
- void run(F&& f) {
- if (threads.count() == 0) {
- f();
- } else {
- if (!st || next_id == 0) {
- create_state();
- }
- const state::id this_id = next_id;
- next_id <<= 1;
-
- st->track_id(this_id);
- threads.dispatch(wrapper<F>{this_id, st, std::move(f)});
- }
- }
-
- //! Wait until all functions provided to `run` have completed.
- void wait() noexcept {
- if (st) {
- do_wait();
- }
- }
-
-private:
- explicit task_region_handle(thread_group& threads_src)
- : st(nullptr), threads(threads_src), next_id(0) {
- }
-
- void create_state();
- void do_wait() noexcept;
-
- std::shared_ptr<state> st;
- thread_group& threads;
- state::id next_id;
-};
-
-/*! Function for creating a `task_region_handle`, which automatically calls
-`task_region_handle::wait()` before returning. If a `thread_group` is not
-provided, one is created with an optimal number of threads. The callback `f`
-must have the signature `void(task_region_handle&)`. */
-struct task_region_ {
- template<typename F>
- void operator()(thread_group& threads, F&& f) const {
- static_assert(
- std::is_same<void, typename std::result_of<F(task_region_handle&)>::type>::value,
- "f cannot have a return value"
- );
- task_region_handle region{threads};
- f(region);
- region.wait();
- }
-
- template<typename F>
- void operator()(thread_group&& threads, F&& f) const {
- (*this)(threads, std::forward<F>(f));
- }
-
- template<typename F>
- void operator()(F&& f) const {
- thread_group threads;
- (*this)(threads, std::forward<F>(f));
- }
-};
-
-constexpr const task_region_ task_region{};
-}
diff --git a/src/common/thread_group.cpp b/src/common/thread_group.cpp
deleted file mode 100644
index 691a27a25..000000000
--- a/src/common/thread_group.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2014-2017, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "common/thread_group.h"
-
-#include <boost/thread/locks.hpp>
-#include <cassert>
-#include <limits>
-#include <stdexcept>
-
-#include "cryptonote_config.h"
-#include "common/util.h"
-
-namespace tools
-{
-std::size_t thread_group::optimal() {
- static_assert(
- std::numeric_limits<unsigned>::max() <= std::numeric_limits<std::size_t>::max(),
- "unexpected truncation"
- );
- const std::size_t hardware = get_max_concurrency();
- return hardware ? (hardware - 1) : 0;
-}
-
-std::size_t thread_group::optimal_with_max(std::size_t count) {
- return count ? std::min(count - 1, optimal()) : 0;
-}
-
-thread_group::thread_group(std::size_t count) : internal() {
- if (count) {
- internal.emplace(count);
- }
-}
-
-thread_group::data::data(std::size_t count)
- : threads()
- , head{nullptr}
- , last(std::addressof(head))
- , mutex()
- , has_work()
- , stop(false) {
- threads.reserve(count);
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
- while (count--) {
- threads.push_back(boost::thread(attrs, boost::bind(&thread_group::data::run, this)));
- }
-}
-
-thread_group::data::~data() noexcept {
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- stop = true;
- }
- has_work.notify_all();
- for (auto& worker : threads) {
- try {
- worker.join();
- }
- catch(...) {}
- }
-}
-
-std::unique_ptr<thread_group::data::work> thread_group::data::get_next() noexcept {
- std::unique_ptr<work> rc = std::move(head.ptr);
- if (rc != nullptr) {
- head.ptr = std::move(rc->next.ptr);
- if (head.ptr == nullptr) {
- last = std::addressof(head);
- }
- }
- return rc;
-}
-
-bool thread_group::data::try_run_one() noexcept {
- /* This function and `run()` can both throw when acquiring the lock, or in
- dispatched function. It is tough to recover from either, particularly the
- lock case. These functions are marked as noexcept so that if either call
- throws, the entire process is terminated. Users of the `dispatch` call are
- expected to make their functions noexcept, or use std::packaged_task to copy
- exceptions so that the process will continue in all but the most pessimistic
- cases (std::bad_alloc). This was the existing behavior;
- `asio::io_service::run` propogates errors from dispatched calls, and uncaught
- exceptions on threads result in process termination. */
- std::unique_ptr<work> next = nullptr;
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- next = get_next();
- }
- if (next) {
- assert(next->f);
- next->f();
- return true;
- }
- return false;
-}
-
-void thread_group::data::run() noexcept {
- // see `try_run_one()` source for additional information
- while (true) {
- std::unique_ptr<work> next = nullptr;
- {
- boost::unique_lock<boost::mutex> lock(mutex);
- has_work.wait(lock, [this] { return head.ptr != nullptr || stop; });
- if (stop) {
- return;
- }
- next = get_next();
- }
- assert(next != nullptr);
- assert(next->f);
- next->f();
- }
-}
-
-void thread_group::data::dispatch(std::function<void()> f) {
- std::unique_ptr<work> latest(new work{std::move(f), node{nullptr}});
- node* const latest_node = std::addressof(latest->next);
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- assert(last != nullptr);
- assert(last->ptr == nullptr);
-
- last->ptr = std::move(latest);
- last = latest_node;
- }
- has_work.notify_one();
-}
-}
diff --git a/src/common/thread_group.h b/src/common/thread_group.h
deleted file mode 100644
index 48fd4cd56..000000000
--- a/src/common/thread_group.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2014-2017, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#pragma once
-
-#include <boost/optional/optional.hpp>
-#include <boost/thread/condition_variable.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/thread.hpp>
-#include <cstddef>
-#include <functional>
-#include <thread>
-#include <utility>
-#include <vector>
-
-namespace tools
-{
-//! Manages zero or more threads for work dispatching.
-class thread_group
-{
-public:
-
- //! \return `get_max_concurrency() ? get_max_concurrency() - 1 : 0`
- static std::size_t optimal();
-
- //! \return `count ? min(count - 1, optimal()) : 0`
- static std::size_t optimal_with_max(std::size_t count);
-
- //! Create an optimal number of threads.
- explicit thread_group() : thread_group(optimal()) {}
-
- //! Create exactly `count` threads.
- explicit thread_group(std::size_t count);
-
- thread_group(thread_group const&) = delete;
- thread_group(thread_group&&) = delete;
-
- //! Joins threads, but does not necessarily run all dispatched functions.
- ~thread_group() = default;
-
- thread_group& operator=(thread_group const&) = delete;
- thread_group& operator=(thread_group&&) = delete;
-
- //! \return Number of threads owned by `this` group.
- std::size_t count() const noexcept {
- if (internal) {
- return internal->count();
- }
- return 0;
- }
-
- //! \return True iff a function was available and executed (on `this_thread`).
- bool try_run_one() noexcept {
- if (internal) {
- return internal->try_run_one();
- }
- return false;
- }
-
- /*! `f` is invoked immediately if `count() == 0`, otherwise execution of `f`
- is queued for next available thread. If `f` is queued, any exception leaving
- that function will result in process termination. Use std::packaged_task if
- exceptions need to be handled. */
- template<typename F>
- void dispatch(F&& f) {
- if (internal) {
- internal->dispatch(std::forward<F>(f));
- }
- else {
- f();
- }
- }
-
-private:
- class data {
- public:
- data(std::size_t count);
- ~data() noexcept;
-
- std::size_t count() const noexcept {
- return threads.size();
- }
-
- bool try_run_one() noexcept;
- void dispatch(std::function<void()> f);
-
- private:
- struct work;
-
- struct node {
- std::unique_ptr<work> ptr;
- };
-
- struct work {
- std::function<void()> f;
- node next;
- };
-
- //! Requires lock on `mutex`.
- std::unique_ptr<work> get_next() noexcept;
-
- //! Blocks until destructor is invoked, only call from thread.
- void run() noexcept;
-
- private:
- std::vector<boost::thread> threads;
- node head;
- node* last;
- boost::condition_variable has_work;
- boost::mutex mutex;
- bool stop;
- };
-
-private:
- // optionally construct elements, without separate heap allocation
- boost::optional<data> internal;
-};
-
-}
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
new file mode 100644
index 000000000..41d0c25e0
--- /dev/null
+++ b/src/common/threadpool.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "common/threadpool.h"
+
+#include <cassert>
+#include <limits>
+#include <stdexcept>
+
+#include "cryptonote_config.h"
+#include "common/util.h"
+
+namespace tools
+{
+threadpool::threadpool() : running(true), active(0) {
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+ max = tools::get_max_concurrency() * 2;
+ size_t i = max;
+ while(i--) {
+ threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this)));
+ }
+}
+
+threadpool::~threadpool() {
+ {
+ const boost::unique_lock<boost::mutex> lock(mutex);
+ running = false;
+ has_work.notify_all();
+ }
+ for (size_t i = 0; i<threads.size(); i++) {
+ threads[i].join();
+ }
+}
+
+void threadpool::submit(waiter *obj, std::function<void()> f) {
+ entry e = {obj, f};
+ boost::unique_lock<boost::mutex> lock(mutex);
+ if (active == max && !queue.empty()) {
+ // if all available threads are already running
+ // and there's work waiting, just run in current thread
+ lock.unlock();
+ f();
+ } else {
+ if (obj)
+ obj->inc();
+ queue.push_back(e);
+ has_work.notify_one();
+ }
+}
+
+int threadpool::get_max_concurrency() {
+ return max / 2;
+}
+
+void threadpool::waiter::wait() {
+ boost::unique_lock<boost::mutex> lock(mt);
+ while(num) cv.wait(lock);
+}
+
+void threadpool::waiter::inc() {
+ const boost::unique_lock<boost::mutex> lock(mt);
+ num++;
+}
+
+void threadpool::waiter::dec() {
+ const boost::unique_lock<boost::mutex> lock(mt);
+ num--;
+ if (!num)
+ cv.notify_one();
+}
+
+void threadpool::run() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ while (running) {
+ entry e;
+ while(queue.empty() && running)
+ has_work.wait(lock);
+ if (!running) break;
+
+ active++;
+ e = queue.front();
+ queue.pop_front();
+ lock.unlock();
+ e.f();
+
+ if (e.wo)
+ e.wo->dec();
+ lock.lock();
+ active--;
+ }
+}
+}
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
new file mode 100644
index 000000000..1d56d7605
--- /dev/null
+++ b/src/common/threadpool.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#pragma once
+
+#include <boost/thread/condition_variable.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <cstddef>
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace tools
+{
+//! A global thread pool
+class threadpool
+{
+public:
+ static threadpool& getInstance() {
+ static threadpool instance;
+ return instance;
+ }
+
+ // The waiter lets the caller know when all of its
+ // tasks are completed.
+ class waiter {
+ boost::mutex mt;
+ boost::condition_variable cv;
+ int num;
+ public:
+ void inc();
+ void dec();
+ void wait(); //! Wait for a set of tasks to finish.
+ waiter() : num(0){}
+ ~waiter() { wait(); }
+ };
+
+ // Submit a task to the pool. The waiter pointer may be
+ // NULL if the caller doesn't care to wait for the
+ // task to finish.
+ void submit(waiter *waiter, std::function<void()> f);
+
+ int get_max_concurrency();
+
+ private:
+ threadpool();
+ ~threadpool();
+ typedef struct entry {
+ waiter *wo;
+ std::function<void()> f;
+ } entry;
+ std::deque<entry> queue;
+ boost::condition_variable has_work;
+ boost::mutex mutex;
+ std::vector<boost::thread> threads;
+ int active;
+ int max;
+ bool running;
+ void run();
+};
+
+}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 046961b06..74a6babf1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -39,11 +39,13 @@ using namespace epee;
#include "net/http_client.h" // epee::net_utils::...
#ifdef WIN32
-#include <windows.h>
-#include <shlobj.h>
-#include <strsafe.h>
+ #include <windows.h>
+ #include <shlobj.h>
+ #include <strsafe.h>
#else
-#include <sys/utsname.h>
+ #include <sys/file.h>
+ #include <sys/utsname.h>
+ #include <sys/stat.h>
#endif
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
@@ -53,7 +55,12 @@ namespace tools
{
std::function<void(int)> signal_handler::m_handler;
- std::unique_ptr<std::FILE, tools::close_file> create_private_file(const std::string& name)
+ private_file::private_file() noexcept : m_handle(), m_filename() {}
+
+ private_file::private_file(std::FILE* handle, std::string&& filename) noexcept
+ : m_handle(handle), m_filename(std::move(filename)) {}
+
+ private_file private_file::create(std::string name)
{
#ifdef WIN32
struct close_handle
@@ -70,17 +77,17 @@ namespace tools
const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
process.reset(temp);
if (fail)
- return nullptr;
+ return {};
}
DWORD sid_size = 0;
GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- return nullptr;
+ return {};
std::unique_ptr<char[]> sid{new char[sid_size]};
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
- return nullptr;
+ return {};
const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
const DWORD daclSize =
@@ -88,17 +95,17 @@ namespace tools
const std::unique_ptr<char[]> dacl{new char[daclSize]};
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
- return nullptr;
+ return {};
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
- return nullptr;
+ return {};
SECURITY_DESCRIPTOR descriptor{};
if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
- return nullptr;
+ return {};
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
- return nullptr;
+ return {};
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
std::unique_ptr<void, close_handle> file{
@@ -106,7 +113,7 @@ namespace tools
name.c_str(),
GENERIC_WRITE, FILE_SHARE_READ,
std::addressof(attributes),
- CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY,
+ CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
nullptr
)
};
@@ -121,22 +128,49 @@ namespace tools
{
_close(fd);
}
- return {real_file, tools::close_file{}};
+ return {real_file, std::move(name)};
}
}
#else
- const int fd = open(name.c_str(), (O_RDWR | O_EXCL | O_CREAT), S_IRUSR);
- if (0 <= fd)
+ const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
+ if (0 <= fdr)
{
- std::FILE* file = fdopen(fd, "w");
- if (!file)
+ struct stat rstats = {};
+ if (fstat(fdr, std::addressof(rstats)) != 0)
{
- close(fd);
+ close(fdr);
+ return {};
+ }
+ fchmod(fdr, (S_IRUSR | S_IWUSR));
+ const int fdw = open(name.c_str(), O_RDWR);
+ fchmod(fdr, rstats.st_mode);
+ close(fdr);
+
+ if (0 <= fdw)
+ {
+ struct stat wstats = {};
+ if (fstat(fdw, std::addressof(wstats)) == 0 &&
+ rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino &&
+ flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0)
+ {
+ std::FILE* file = fdopen(fdw, "w");
+ if (file) return {file, std::move(name)};
+ }
+ close(fdw);
}
- return {file, tools::close_file{}};
}
#endif
- return nullptr;
+ return {};
+ }
+
+ private_file::~private_file() noexcept
+ {
+ try
+ {
+ boost::system::error_code ec{};
+ boost::filesystem::remove(filename(), ec);
+ }
+ catch (...) {}
}
#ifdef WIN32
diff --git a/src/common/util.h b/src/common/util.h
index 2452bc9d5..48bdbbc28 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -60,8 +60,30 @@ namespace tools
}
};
- //! \return File only readable by owner. nullptr if `filename` exists.
- std::unique_ptr<std::FILE, close_file> create_private_file(const std::string& filename);
+ //! A file restricted to process owner AND process. Deletes file on destruction.
+ class private_file {
+ std::unique_ptr<std::FILE, close_file> m_handle;
+ std::string m_filename;
+
+ private_file(std::FILE* handle, std::string&& filename) noexcept;
+ public:
+
+ //! `handle() == nullptr && filename.empty()`.
+ private_file() noexcept;
+
+ /*! \return File only readable by owner and only used by this process
+ OR `private_file{}` on error. */
+ static private_file create(std::string filename);
+
+ private_file(private_file&&) = default;
+ private_file& operator=(private_file&&) = default;
+
+ //! Deletes `filename()` and closes `handle()`.
+ ~private_file() noexcept;
+
+ std::FILE* handle() const noexcept { return m_handle.get(); }
+ const std::string& filename() const noexcept { return m_filename; }
+ };
/*! \brief Returns the default data directory.
*
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index e99b6651f..94f924296 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -31,12 +31,16 @@
#pragma once
#include <cstddef>
+#include <iostream>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
+#include <boost/utility/value_init.hpp>
#include <vector>
#include "common/pod-class.h"
#include "generic-ops.h"
+#include "hex.h"
+#include "span.h"
#include "hash.h"
namespace crypto {
@@ -248,6 +252,24 @@ namespace crypto {
const signature *sig) {
return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig);
}
+
+ inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+
+ const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>();
}
CRYPTO_MAKE_HASHABLE(public_key)
diff --git a/src/crypto/hash.h b/src/crypto/hash.h
index 22991e513..610b4502f 100644
--- a/src/crypto/hash.h
+++ b/src/crypto/hash.h
@@ -31,9 +31,13 @@
#pragma once
#include <stddef.h>
+#include <iostream>
+#include <boost/utility/value_init.hpp>
#include "common/pod-class.h"
#include "generic-ops.h"
+#include "hex.h"
+#include "span.h"
namespace crypto {
@@ -75,6 +79,15 @@ namespace crypto {
tree_hash(reinterpret_cast<const char (*)[HASH_SIZE]>(hashes), count, reinterpret_cast<char *>(&root_hash));
}
+ inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+
+ const static crypto::hash null_hash = boost::value_initialized<crypto::hash>();
+ const static crypto::hash8 null_hash8 = boost::value_initialized<crypto::hash8>();
}
CRYPTO_MAKE_HASHABLE(hash)
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 1503b277e..750be69f1 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -34,7 +34,6 @@ endif()
set(cryptonote_basic_sources
account.cpp
- checkpoints.cpp
cryptonote_basic_impl.cpp
cryptonote_format_utils.cpp
difficulty.cpp
@@ -46,7 +45,6 @@ set(cryptonote_basic_headers)
set(cryptonote_basic_private_headers
account.h
account_boost_serialization.h
- checkpoints.h
connection_context.h
cryptonote_basic.h
cryptonote_basic_impl.h
@@ -60,7 +58,7 @@ set(cryptonote_basic_private_headers
verification_context.h)
monero_private_headers(cryptonote_basic
- ${crypto_private_headers})
+ ${cryptonote_basic_private_headers})
monero_add_library(cryptonote_basic
${cryptonote_basic_sources}
${cryptonote_basic_headers}
@@ -69,6 +67,7 @@ target_link_libraries(cryptonote_basic
PUBLIC
common
cncrypto
+ checkpoints
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h
index 3283543e2..da4b6512e 100644
--- a/src/cryptonote_basic/connection_context.h
+++ b/src/cryptonote_basic/connection_context.h
@@ -40,7 +40,7 @@ namespace cryptonote
struct cryptonote_connection_context: public epee::net_utils::connection_context_base
{
cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0),
- m_last_known_hash(cryptonote::null_hash) {}
+ m_last_known_hash(crypto::null_hash) {}
enum state
{
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index c4adf1fcb..eb03d33b9 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -53,11 +53,6 @@
namespace cryptonote
{
-
- const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash);
- const static crypto::hash8 null_hash8 = AUTO_VAL_INIT(null_hash8);
- const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey);
-
typedef std::vector<crypto::signature> ring_signature;
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h
index 7a2259b32..5523846d6 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.h
+++ b/src/cryptonote_basic/cryptonote_basic_impl.h
@@ -33,8 +33,6 @@
#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
-#include "hex.h"
-#include "span.h"
namespace cryptonote {
@@ -136,26 +134,3 @@ namespace cryptonote {
bool parse_hash256(const std::string str_hash, crypto::hash& hash);
-namespace crypto {
- inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
- inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) {
- epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
- }
-}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 745dfb72e..fc979f288 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -632,7 +632,7 @@ namespace cryptonote
// prunable rct
if (t.rct_signatures.type == rct::RCTTypeNull)
{
- hashes[2] = cryptonote::null_hash;
+ hashes[2] = crypto::null_hash;
}
else
{
@@ -869,4 +869,21 @@ namespace cryptonote
block_hashes_calculated = block_hashes_calculated_count;
block_hashes_cached = block_hashes_cached_count;
}
+ //---------------------------------------------------------------
+ crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ {
+ crypto::hash hash;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
+ sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
+ return key;
+ }
+ //---------------------------------------------------------------
+ crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ {
+ crypto::hash hash;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
+ sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
+ return key;
+ }
+
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index d8ccf8eec..00080fb98 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -212,6 +212,8 @@ namespace cryptonote
bool is_valid_decomposed_amount(uint64_t amount);
void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached);
+ crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase);
+ crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 3c5811d61..b620e3426 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -858,19 +858,6 @@ namespace cryptonote
const boost::filesystem::path& power_supply_path = iter->path();
if (boost::filesystem::is_directory(power_supply_path))
{
- std::ifstream power_supply_present_stream((power_supply_path / "present").string());
- if (power_supply_present_stream.fail())
- {
- LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present");
- continue;
- }
-
- if (power_supply_present_stream.get() != '1')
- {
- LOG_PRINT_L4("Power supply not present at " << power_supply_path);
- continue;
- }
-
boost::filesystem::path power_supply_type_path = power_supply_path / "type";
if (boost::filesystem::is_regular_file(power_supply_type_path))
{
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 8ff01cf8b..a143c307f 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -91,7 +91,6 @@
#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing
#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading
#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading
-#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block
#define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day
#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week
@@ -150,6 +149,7 @@ namespace config
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
uint16_t const P2P_DEFAULT_PORT = 18080;
uint16_t const RPC_DEFAULT_PORT = 18081;
+ uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082;
boost::uuids::uuid const NETWORK_ID = { {
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10
} }; // Bender's nightmare
@@ -162,6 +162,7 @@ namespace config
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54;
uint16_t const P2P_DEFAULT_PORT = 28080;
uint16_t const RPC_DEFAULT_PORT = 28081;
+ uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082;
boost::uuids::uuid const NETWORK_ID = { {
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11
} }; // Bender's daydream
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 7c43323d4..169a38f0a 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -48,13 +48,14 @@ else()
endif()
monero_private_headers(cryptonote_core
- ${crypto_private_headers})
+ ${cryptonote_core_private_headers})
monero_add_library(cryptonote_core
${cryptonote_core_sources}
${cryptonote_core_headers}
${cryptonote_core_private_headers})
target_link_libraries(cryptonote_core
PUBLIC
+ version
common
cncrypto
blockchain_db
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index c1faa703f..0b09d503c 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -45,10 +45,10 @@
#include "profile_tools.h"
#include "file_io_utils.h"
#include "common/int-util.h"
+#include "common/threadpool.h"
#include "common/boost_serialization_helper.h"
#include "warnings.h"
#include "crypto/hash.h"
-#include "cryptonote_basic/checkpoints.h"
#include "cryptonote_core.h"
#include "ringct/rctSigs.h"
#include "common/perf_timer.h"
@@ -751,7 +751,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_timestamps = timestamps;
m_difficulties = difficulties;
}
- size_t target = get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+ size_t target = get_difficulty_target();
return next_difficulty(timestamps, difficulties, target);
}
//------------------------------------------------------------------
@@ -1571,6 +1571,98 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A
output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey;
}
+
+uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
+{
+ uint64_t num_outs = m_db->get_num_outputs(amount);
+ // ensure we don't include outputs that aren't yet eligible to be used
+ // outpouts are sorted by height
+ while (num_outs > 0)
+ {
+ const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
+ const uint64_t height = m_db->get_tx_block_height(toi.first);
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ break;
+ --num_outs;
+ }
+
+ return num_outs;
+}
+
+std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
+{
+ uint64_t num_outs = get_num_mature_outputs(amount);
+
+ std::vector<uint64_t> indices;
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_outs <= count)
+ {
+ for (uint64_t i = 0; i < num_outs; i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if tx is unlocked, add output to indices
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (indices.size() < count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
+ uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
+ double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
+ uint64_t i = (uint64_t)(frac*num_outs);
+ // just in case rounding up to 1 occurs after sqrt
+ if (i == num_outs)
+ --i;
+
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+
+ return indices;
+}
+
+crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
+{
+ output_data_t data = m_db->get_output_key(amount, global_index);
+ return data.pubkey;
+}
+
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
@@ -1585,80 +1677,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// from BlockchainDB where <n> is req.outs_count (number of mixins).
for (uint64_t amount : req.amounts)
{
- auto num_outs = m_db->get_num_outputs(amount);
- // ensure we don't include outputs that aren't yet eligible to be used
- // outpouts are sorted by height
- while (num_outs > 0)
- {
- const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
- const uint64_t height = m_db->get_tx_block_height(toi.first);
- if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
- break;
- --num_outs;
- }
-
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
- std::unordered_set<uint64_t> seen_indices;
-
- // if there aren't enough outputs to mix with (or just enough),
- // use all of them. Eventually this should become impossible.
- if (num_outs <= req.outs_count)
- {
- for (uint64_t i = 0; i < num_outs; i++)
- {
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
-
- // if tx is unlocked, add output to result_outs
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
+ std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
- }
- }
- else
+ for (auto i : indices)
{
- // while we still need more mixins
- while (result_outs.outs.size() < req.outs_count)
- {
- // if we've gone through every possible output, we've gotten all we can
- if (seen_indices.size() == num_outs)
- {
- break;
- }
-
- // get a random output index from the DB. If we've already seen it,
- // return to the top of the loop and try again, otherwise add it to the
- // list of output indices we've seen.
-
- // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
- uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
- double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
- uint64_t i = (uint64_t)(frac*num_outs);
- // just in case rounding up to 1 occurs after sqrt
- if (i == num_outs)
- --i;
-
- if (seen_indices.count(i))
- {
- continue;
- }
- seen_indices.emplace(i);
-
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
- // if the output's transaction is unlocked, add the output's index to
- // our list.
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
- }
+ oe.global_amount_index = i;
+ oe.out_key = get_output_key(amount, i);
}
}
return true;
@@ -1816,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
return true;
}
//------------------------------------------------------------------
+void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const
+{
+ const auto o_data = m_db->get_output_key(amount, index);
+ key = o_data.pubkey;
+ mask = o_data.commitment;
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, index);
+ unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
+}
+//------------------------------------------------------------------
// This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync.
@@ -2025,28 +2064,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const
// 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.
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// if we can't find the split point, return false
- if(!find_blockchain_supplement(qblock_ids, resp.start_height))
+ if(!find_blockchain_supplement(qblock_ids, start_height))
{
return false;
}
m_db->block_txn_start(true);
- resp.total_height = get_current_blockchain_height();
+ current_height = get_current_blockchain_height();
size_t count = 0;
- for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
+ for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
- resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i));
+ hashes.push_back(m_db->get_block_hash_from_height(i));
}
- resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
m_db->block_txn_stop();
return true;
}
+
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height);
+ resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
+ return result;
+}
//------------------------------------------------------------------
//FIXME: change argument to std::vector, low priority
// find split point between ours and foreign blockchain (or start at
@@ -2333,6 +2383,26 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
+ // from v7, sorted outs
+ if (m_hardfork->get_current_version() >= 7) {
+ const crypto::public_key *last_key = NULL;
+ for (size_t n = 0; n < tx.vout.size(); ++n)
+ {
+ const tx_out &o = tx.vout[n];
+ if (o.target.type() == typeid(txout_to_key))
+ {
+ const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target);
+ if (last_key && memcmp(&out_to_key.key, last_key, sizeof(*last_key)) >= 0)
+ {
+ MERROR_VER("transaction has unsorted outputs");
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ last_key = &out_to_key.key;
+ }
+ }
+ }
+
return true;
}
//------------------------------------------------------------------
@@ -2501,6 +2571,25 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
+ // from v7, sorted ins
+ if (hf_version >= 7) {
+ const crypto::key_image *last_key_image = NULL;
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ const txin_v &txin = tx.vin[n];
+ if (txin.type() == typeid(txin_to_key))
+ {
+ const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
+ if (last_key_image && memcmp(&in_to_key.k_image, last_key_image, sizeof(*last_key_image)) >= 0)
+ {
+ MERROR_VER("transaction has unsorted inputs");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+ last_key_image = &in_to_key.k_image;
+ }
+ }
+ }
auto it = m_check_txin_table.find(tx_prefix_hash);
if(it == m_check_txin_table.end())
{
@@ -2513,33 +2602,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
- int threads = tools::get_max_concurrency();
-
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- bool ioservice_active = false;
-
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- if(threads > 1)
- {
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
- ioservice_active = true;
- }
-
-#define KILL_IOSERVICE() \
- if(ioservice_active) \
- { \
- work.reset(); \
- while (!ioservice.stopped()) ioservice.poll(); \
- threadpool.join_all(); \
- ioservice.stop(); \
- ioservice_active = false; \
- }
-
- epee::misc_utils::auto_scope_leave_caller ioservice_killer = epee::misc_utils::create_scope_leave_handler([&]() { KILL_IOSERVICE(); });
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ int threads = tpool.get_max_concurrency();
for (const auto& txin : tx.vin)
{
@@ -2600,7 +2665,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
// ND: Speedup
// 1. Thread ring signature verification if possible.
- ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
}
else
{
@@ -2623,8 +2688,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
sig_index++;
}
-
- KILL_IOSERVICE();
+ if (tx.version == 1 && threads > 1)
+ waiter.wait();
if (tx.version == 1)
{
@@ -3249,7 +3314,7 @@ leave:
// XXX old code adds miner tx here
- int tx_index = 0;
+ size_t tx_index = 0;
// Iterate over the block's transaction hashes, grabbing each
// from the tx_pool and validating them. Each is then added
// to txs. Keys spent in each are added to <keys> by the double spend check.
@@ -3331,7 +3396,7 @@ leave:
{
// ND: if fast_check is enabled for blocks, there is no need to check
// the transaction inputs, but do some sanity checks anyway.
- if (memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0)
+ if (tx_index >= m_blocks_txs_check.size() || memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0)
{
MERROR_VER("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
//TODO: why is this done? make sure that keeping invalid blocks makes sense.
@@ -3582,12 +3647,23 @@ void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block>
//------------------------------------------------------------------
bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
{
+ bool success = false;
+
MTRACE("Blockchain::" << __func__);
CRITICAL_REGION_BEGIN(m_blockchain_lock);
TIME_MEASURE_START(t1);
- m_db->batch_stop();
- if (m_sync_counter > 0)
+ try
+ {
+ m_db->batch_stop();
+ success = true;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Exception in cleanup_handle_incoming_blocks: " << e.what());
+ }
+
+ if (success && m_sync_counter > 0)
{
if (force_sync)
{
@@ -3622,7 +3698,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
CRITICAL_REGION_END();
m_tx_pool.unlock();
- return true;
+ return success;
}
//------------------------------------------------------------------
@@ -3655,6 +3731,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
MTRACE("Blockchain::" << __func__);
TIME_MEASURE_START(prepare);
bool stop_batch;
+ uint64_t bytes = 0;
// Order of locking must be:
// m_incoming_tx_lock (optional)
@@ -3676,7 +3753,15 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
if(blocks_entry.size() == 0)
return false;
- while (!(stop_batch = m_db->batch_start(blocks_entry.size()))) {
+ for (const auto &entry : blocks_entry)
+ {
+ bytes += entry.block.size();
+ for (const auto &tx_blob : entry.txs)
+ {
+ bytes += tx_blob.size();
+ }
+ }
+ while (!(stop_batch = m_db->batch_start(blocks_entry.size(), bytes))) {
m_blockchain_lock.unlock();
m_tx_pool.unlock();
epee::misc_utils::sleep_no_w(1000);
@@ -3688,7 +3773,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
return true;
bool blocks_exist = false;
- uint64_t threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ uint64_t threads = tpool.get_max_concurrency();
if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1)
{
@@ -3697,15 +3783,12 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
threads = m_max_prepare_blocks_threads;
uint64_t height = m_db->height();
- std::vector<boost::thread *> thread_list;
int batches = blocks_entry.size() / threads;
int extra = blocks_entry.size() % threads;
MDEBUG("block_batches: " << batches);
std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads);
std::vector < std::vector < block >> blocks(threads);
auto it = blocks_entry.begin();
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
for (uint64_t i = 0; i < threads; i++)
{
@@ -3764,19 +3847,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
{
m_blocks_longhash_table.clear();
uint64_t thread_height = height;
+ tools::threadpool::waiter waiter;
for (uint64_t i = 0; i < threads; i++)
{
- thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i]))));
+ tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])));
thread_height += blocks[i].size();
}
- for (size_t j = 0; j < thread_list.size(); j++)
- {
- thread_list[j]->join();
- delete thread_list[j];
- }
-
- thread_list.clear();
+ waiter.wait();
if (m_cancel)
return false;
@@ -3900,30 +3978,20 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
// [output] stores all transactions for each tx_out_index::hash found
std::vector<std::unordered_map<crypto::hash, cryptonote::transaction>> transactions(amounts.size());
- threads = tools::get_max_concurrency();
+ threads = tpool.get_max_concurrency();
if (!m_db->can_thread_bulk_indices())
threads = 1;
if (threads > 1)
{
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
-
- for (uint64_t i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool::waiter waiter;
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
- ioservice.dispatch(boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])));
}
-
- work.reset();
- threadpool.join_all();
- ioservice.stop();
+ waiter.wait();
}
else
{
@@ -4075,6 +4143,11 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
}
+uint64_t Blockchain::get_difficulty_target() const
+{
+ return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+}
+
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const
{
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff);
@@ -4118,7 +4191,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94";
+static const char expected_block_hashes_hash[] = "d3ca80d50661684cde0e715d46d7c19704d2e216b21ed088af9fd4ef37ed4d65";
void Blockchain::load_compiled_in_block_hashes()
{
if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0)
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b8ea657b4..00b40d0ad 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -51,7 +51,7 @@
#include "cryptonote_tx_utils.h"
#include "cryptonote_basic/verification_context.h"
#include "crypto/hash.h"
-#include "cryptonote_basic/checkpoints.h"
+#include "checkpoints/checkpoints.h"
#include "cryptonote_basic/hardfork.h"
#include "blockchain_db/blockchain_db.h"
@@ -374,6 +374,22 @@ namespace cryptonote
* BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
*
* @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
+ * @param hashes the hashes to be returned, return-by-reference
+ * @param start_height the start height, return-by-reference
+ * @param current_height the current blockchain height, return-by-reference
+ *
+ * @return true if a block found in common, else false
+ */
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
+
+ /**
+ * @brief get recent block hashes for a foreign chain
+ *
+ * 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.
+ *
+ * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
* @param resp return-by-reference the split height and subsequent blocks' hashes
*
* @return true if a block found in common, else false
@@ -427,6 +443,35 @@ namespace cryptonote
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
/**
+ * @brief get number of outputs of an amount past the minimum spendable age
+ *
+ * @param amount the output amount
+ *
+ * @return the number of mature outputs
+ */
+ uint64_t get_num_mature_outputs(uint64_t amount) const;
+
+ /**
+ * @brief get random outputs (indices) for an amount
+ *
+ * @param amount the amount
+ * @param count the number of random outputs to choose
+ *
+ * @return the outputs' amount-global indices
+ */
+ std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
+
+ /**
+ * @brief get the public key for an output
+ *
+ * @param amount the output amount
+ * @param global_index the output amount-global index
+ *
+ * @return the public key
+ */
+ crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
+
+ /**
* @brief gets random outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -458,6 +503,17 @@ namespace cryptonote
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
/**
+ * @brief gets an output's key and unlocked state
+ *
+ * @param amount in - the output amount
+ * @param index in - the output global amount index
+ * @param mask out - the output's RingCT mask
+ * @param key out - the output's key
+ * @param unlocked out - the output's unlocked state
+ */
+ void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
+
+ /**
* @brief gets random ringct outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -775,6 +831,13 @@ namespace cryptonote
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const;
/**
+ * @brief get difficulty target based on chain and hardfork version
+ *
+ * @return difficulty target
+ */
+ uint64_t get_difficulty_target() const;
+
+ /**
* @brief remove transactions from the transaction pool (if present)
*
* @param txids a list of hashes of transactions to be removed
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index c406dd0b4..57347fdbc 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -37,7 +37,7 @@ using namespace epee;
#include "common/util.h"
#include "common/updates.h"
#include "common/download.h"
-#include "common/task_region.h"
+#include "common/threadpool.h"
#include "warnings.h"
#include "crypto/crypto.h"
#include "cryptonote_config.h"
@@ -45,7 +45,7 @@ using namespace epee;
#include "misc_language.h"
#include <csignal>
#include <p2p/net_node.h>
-#include "cryptonote_basic/checkpoints.h"
+#include "checkpoints/checkpoints.h"
#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
#include "ringct/rctSigs.h"
@@ -74,7 +74,7 @@ namespace cryptonote
m_last_dns_checkpoints_update(0),
m_last_json_checkpoints_update(0),
m_disable_dns_checkpoints(false),
- m_threadpool(tools::thread_group::optimal()),
+ m_threadpool(tools::threadpool::getInstance()),
m_update_download(0)
{
m_checkpoints_updating.clear();
@@ -156,20 +156,19 @@ namespace cryptonote
command_line::add_arg(desc, command_line::arg_testnet_on);
command_line::add_arg(desc, command_line::arg_dns_checkpoints);
- command_line::add_arg(desc, command_line::arg_db_type);
command_line::add_arg(desc, command_line::arg_prep_blocks_threads);
command_line::add_arg(desc, command_line::arg_fast_block_sync);
- command_line::add_arg(desc, command_line::arg_db_sync_mode);
- command_line::add_arg(desc, command_line::arg_db_salvage);
command_line::add_arg(desc, command_line::arg_show_time_stats);
command_line::add_arg(desc, command_line::arg_block_sync_size);
command_line::add_arg(desc, command_line::arg_check_updates);
+ command_line::add_arg(desc, command_line::arg_fluffy_blocks);
// we now also need some of net_node's options (p2p bind arg, for separate data dir)
command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false);
miner::init_options(desc);
+ BlockchainDB::init_options(desc);
}
//-----------------------------------------------------------------------------------------------
bool core::handle_command_line(const boost::program_options::variables_map& vm)
@@ -184,7 +183,7 @@ namespace cryptonote
if (!m_testnet && !m_fakechain)
{
cryptonote::checkpoints checkpoints;
- if (!checkpoints.init_default_checkpoints())
+ if (!checkpoints.init_default_checkpoints(m_testnet))
{
throw std::runtime_error("Failed to initialize checkpoints");
}
@@ -199,6 +198,7 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height));
+ m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks);
if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true)
test_drop_download();
@@ -211,10 +211,9 @@ namespace cryptonote
return m_blockchain_storage.get_current_blockchain_height();
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const
+ void core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) const
{
top_id = m_blockchain_storage.get_tail_id(height);
- return true;
}
//-----------------------------------------------------------------------------------------------
bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const
@@ -279,9 +278,9 @@ namespace cryptonote
m_config_folder_mempool = m_config_folder_mempool + "/" + m_port;
}
- std::string db_type = command_line::get_arg(vm, command_line::arg_db_type);
- std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode);
- bool db_salvage = command_line::get_arg(vm, command_line::arg_db_salvage) != 0;
+ std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
+ std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
+ bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0;
bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads);
std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates);
@@ -585,42 +584,59 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_txs(const std::list<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
+ TRY_ENTRY();
+
struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; };
std::vector<result> results(tx_blobs.size());
tvc.resize(tx_blobs.size());
- tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
- for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- region.run([&, i, it] {
- results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
- });
- }
- });
- tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
- for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- if (!results[i].res)
- continue;
- if(m_mempool.have_tx(results[i].hash))
+ tools::threadpool::waiter waiter;
+ std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ m_threadpool.submit(&waiter, [&, i, it] {
+ try
{
- LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
}
- else if(m_blockchain_storage.have_tx(results[i].hash))
+ catch (const std::exception &e)
{
- LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what());
+ results[i].res = false;
}
- else
- {
- region.run([&, i, it] {
+ });
+ }
+ waiter.wait();
+ it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ if (!results[i].res)
+ continue;
+ if(m_mempool.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ }
+ else if(m_blockchain_storage.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ }
+ else
+ {
+ m_threadpool.submit(&waiter, [&, i, it] {
+ try
+ {
results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
- });
- }
+ }
+ catch (const std::exception &e)
+ {
+ MERROR_VER("Exception in handle_incoming_tx_post: " << e.what());
+ results[i].res = false;
+ }
+ });
}
- });
+ }
+ waiter.wait();
bool ok = true;
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res)
{
@@ -638,6 +654,8 @@ namespace cryptonote
MDEBUG("tx added: " << results[i].hash);
}
return ok;
+
+ CATCH_ENTRY_L0("core::handle_incoming_txs()", false);
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
@@ -790,6 +808,13 @@ namespace cryptonote
return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4;
}
//-----------------------------------------------------------------------------------------------
+ bool core::are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const
+ {
+ spent.clear();
+
+ return m_mempool.check_for_key_images(key_im, spent);
+ }
+ //-----------------------------------------------------------------------------------------------
std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count)
{
uint64_t emission_amount = 0;
@@ -1015,7 +1040,6 @@ namespace cryptonote
{
cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>();
NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg);
- arg.hop = 0;
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::list<crypto::hash> missed_txs;
std::list<cryptonote::blobdata> txs;
@@ -1064,17 +1088,20 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::cleanup_handle_incoming_blocks(bool force_sync)
{
+ bool success = false;
try {
- m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
+ success = m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
}
catch (...) {}
m_incoming_tx_lock.unlock();
- return true;
+ return success;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate)
{
+ TRY_ENTRY();
+
// load json & DNS checkpoints every 10min/hour respectively,
// and verify them with respect to what blocks we already have
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
@@ -1098,6 +1125,8 @@ namespace cryptonote
if(update_miner_blocktemplate && bvc.m_added_to_main_chain)
update_miner_block_template();
return true;
+
+ CATCH_ENTRY_L0("core::handle_incoming_block()", false);
}
//-----------------------------------------------------------------------------------------------
// Used by the RPC server to check the size of an incoming
@@ -1175,6 +1204,11 @@ namespace cryptonote
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_short_chain_history(std::list<crypto::hash>& ids) const
{
return m_blockchain_storage.get_short_chain_history(ids);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index f565afd87..1aed86b25 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -40,7 +40,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
-#include "common/thread_group.h"
+#include "common/threadpool.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "cryptonote_basic/miner.h"
@@ -299,10 +299,8 @@ namespace cryptonote
*
* @param height return-by-reference height of the block
* @param top_id return-by-reference hash of the block
- *
- * @return true
*/
- bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const;
+ void get_blockchain_top(uint64_t& height, crypto::hash& top_id) const;
/**
* @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const
@@ -463,6 +461,13 @@ namespace cryptonote
bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @copydoc tx_memory_pool::get_pool_for_rpc
+ *
+ * @note see tx_memory_pool::get_pool_for_rpc
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
* @copydoc tx_memory_pool::get_transactions_count
*
* @note see tx_memory_pool::get_transactions_count
@@ -708,6 +713,16 @@ namespace cryptonote
bool are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
/**
+ * @brief check if multiple key images are spent in the transaction pool
+ *
+ * @param key_im list of key images to check
+ * @param spent return-by-reference result for each image checked
+ *
+ * @return true
+ */
+ bool are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
+
+ /**
* @brief get the number of blocks to sync in one go
*
* @return the number of blocks to sync in one go
@@ -728,6 +743,13 @@ namespace cryptonote
*/
bool get_testnet() const { return m_testnet; };
+ /**
+ * @brief get whether fluffy blocks are enabled
+ *
+ * @return whether fluffy blocks are enabled
+ */
+ bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
+
private:
/**
@@ -933,7 +955,7 @@ namespace cryptonote
std::unordered_set<crypto::hash> bad_semantics_txes[2];
boost::mutex bad_semantics_txes_lock;
- tools::thread_group m_threadpool;
+ tools::threadpool& m_threadpool;
enum {
UPDATES_DISABLED,
@@ -945,6 +967,8 @@ namespace cryptonote
tools::download_async_handle m_update_download;
size_t m_last_update_length;
boost::mutex m_update_mutex;
+
+ bool m_fluffy_blocks_enabled;
};
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 94f069827..19ed57aba 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -31,6 +31,7 @@
#include "include_base_utils.h"
using namespace epee;
+#include "common/apply_permutation.h"
#include "cryptonote_tx_utils.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/miner.h"
@@ -156,7 +157,7 @@ namespace cryptonote
return destinations[0].addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry> sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
{
std::vector<rct::key> amount_keys;
tx.set_null();
@@ -263,14 +264,25 @@ namespace cryptonote
tx.vin.push_back(input_to_key);
}
- // "Shuffle" outs
- std::vector<tx_destination_entry> shuffled_dsts(destinations);
- std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](unsigned int i) { return crypto::rand<unsigned int>() % i; });
+ // sort ins by their key image
+ std::vector<size_t> ins_order(sources.size());
+ for (size_t n = 0; n < sources.size(); ++n)
+ ins_order[n] = n;
+ std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) {
+ const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]);
+ const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]);
+ return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) < 0;
+ });
+ tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) {
+ std::swap(tx.vin[i0], tx.vin[i1]);
+ std::swap(in_contexts[i0], in_contexts[i1]);
+ std::swap(sources[i0], sources[i1]);
+ });
uint64_t summary_outs_money = 0;
//fill outputs
size_t output_index = 0;
- for(const tx_destination_entry& dst_entr: shuffled_dsts)
+ for(const tx_destination_entry& dst_entr: destinations)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
@@ -297,6 +309,23 @@ namespace cryptonote
summary_outs_money += dst_entr.amount;
}
+#if 0
+ // sort outs by their public key
+ std::vector<size_t> outs_order(tx.vout.size());
+ for (size_t n = 0; n < tx.vout.size(); ++n)
+ outs_order[n] = n;
+ std::sort(outs_order.begin(), outs_order.end(), [&](size_t i0, size_t i1) {
+ const txout_to_key &tk0 = boost::get<txout_to_key>(tx.vout[i0].target);
+ const txout_to_key &tk1 = boost::get<txout_to_key>(tx.vout[i1].target);
+ return memcmp(&tk0.key, &tk1.key, sizeof(tk0.key)) < 0;
+ });
+ tools::apply_permutation(outs_order, [&] (size_t i0, size_t i1) {
+ std::swap(tx.vout[i0], tx.vout[i1]);
+ if (!amount_keys.empty())
+ std::swap(amount_keys[i0], amount_keys[i1]);
+ });
+#endif
+
//check money
if(summary_outs_money > summary_inputs_money )
{
@@ -484,8 +513,9 @@ namespace cryptonote
std::string genesis_coinbase_tx_hex = config::GENESIS_TX;
blobdata tx_bl;
- string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl);
- bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
+ bool r = string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl);
+ CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
+ r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 7aa7c280d..69254fb5f 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -71,7 +71,7 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys);
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry> sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
bool generate_genesis_block(
block& bl
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 265a4ca3e..9071c330c 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -80,7 +80,7 @@ namespace cryptonote
uint64_t get_transaction_size_limit(uint8_t version)
{
- return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
// This class is meant to create a batch when none currently exists.
@@ -92,7 +92,7 @@ namespace cryptonote
LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false) {
m_batch = m_blockchain.get_db().batch_start();
}
- ~LockedTXN() { if (m_batch) { m_blockchain.get_db().batch_stop(); } }
+ ~LockedTXN() { try { if (m_batch) { m_blockchain.get_db().batch_stop(); } } catch (const std::exception &e) { MWARNING("LockedTXN dtor filtering exception: " << e.what()); } }
private:
Blockchain &m_blockchain;
bool m_batch;
@@ -202,6 +202,9 @@ namespace cryptonote
return false;
}
+ // assume failure during verification steps until success is certain
+ tvc.m_verifivation_failed = true;
+
time_t receive_time = time(nullptr);
crypto::hash max_used_block_id = null_hash;
@@ -246,6 +249,7 @@ namespace cryptonote
{
LOG_PRINT_L1("tx used wrong inputs, rejected");
tvc.m_verifivation_failed = true;
+ tvc.m_invalid_input = true;
return false;
}
}else
@@ -285,9 +289,6 @@ namespace cryptonote
tvc.m_should_be_relayed = true;
}
- // assume failure during verification steps until success is certain
- tvc.m_verifivation_failed = true;
-
tvc.m_verifivation_failed = false;
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
@@ -298,7 +299,8 @@ namespace cryptonote
{
crypto::hash h = null_hash;
size_t blob_size = 0;
- get_transaction_hash(tx, h, blob_size);
+ if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0)
+ return false;
return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version);
}
//---------------------------------------------------------------------------------
@@ -684,6 +686,65 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+ m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
+ cryptonote::rpc::tx_in_pool txi;
+ txi.tx_hash = txid;
+ transaction tx;
+ if (!parse_and_validate_tx_from_blob(*bd, tx))
+ {
+ MERROR("Failed to parse tx from txpool");
+ // continue
+ return true;
+ }
+ txi.tx = tx;
+ txi.blob_size = meta.blob_size;
+ txi.fee = meta.fee;
+ txi.kept_by_block = meta.kept_by_block;
+ txi.max_used_block_height = meta.max_used_block_height;
+ txi.max_used_block_hash = meta.max_used_block_id;
+ txi.last_failed_block_height = meta.last_failed_height;
+ txi.last_failed_block_hash = meta.last_failed_id;
+ txi.receive_time = meta.receive_time;
+ txi.relayed = meta.relayed;
+ txi.last_relayed_time = meta.last_relayed_time;
+ txi.do_not_relay = meta.do_not_relay;
+ tx_infos.push_back(txi);
+ return true;
+ }, true);
+
+ for (const key_images_container::value_type& kee : m_spent_key_images) {
+ std::vector<crypto::hash> tx_hashes;
+ const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
+ for (const crypto::hash& tx_id_hash : kei_image_set)
+ {
+ tx_hashes.push_back(tx_id_hash);
+ }
+
+ const crypto::key_image& k_image = kee.first;
+ key_image_infos[k_image] = tx_hashes;
+ }
+ return true;
+ }
+ //---------------------------------------------------------------------------------
+ bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+
+ spent.clear();
+
+ for (const auto& image : key_images)
+ {
+ spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true);
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------------
bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -863,6 +924,9 @@ namespace cryptonote
std::unordered_set<crypto::key_image> k_images;
LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
+
+ LockedTXN lock(m_blockchain);
+
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
{
@@ -1028,12 +1092,13 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.clear();
m_spent_key_images.clear();
- return m_blockchain.for_all_txpool_txes([this](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
+ std::vector<crypto::hash> remove;
+ bool r = m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
cryptonote::transaction tx;
if (!parse_and_validate_tx_from_blob(*bd, tx))
{
- MERROR("Failed to parse tx from txpool");
- return false;
+ MWARNING("Failed to parse tx from txpool, removing");
+ remove.push_back(txid);
}
if (!insert_key_images(tx, meta.kept_by_block))
{
@@ -1043,6 +1108,25 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid);
return true;
}, true);
+ if (!r)
+ return false;
+ if (!remove.empty())
+ {
+ LockedTXN lock(m_blockchain);
+ for (const auto &txid: remove)
+ {
+ try
+ {
+ m_blockchain.remove_txpool_tx(txid);
+ }
+ catch (const std::exception &e)
+ {
+ MWARNING("Failed to remove corrupt transaction: " << txid);
+ // ignore error
+ }
+ }
+ }
+ return true;
}
//---------------------------------------------------------------------------------
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 6414620c9..3e4ccb338 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -46,6 +46,7 @@
#include "blockchain_db/blockchain_db.h"
#include "crypto/hash.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/message_data_structs.h"
namespace cryptonote
{
@@ -269,6 +270,28 @@ namespace cryptonote
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @brief get information about all transactions and key images in the pool
+ *
+ * see documentation on tx_in_pool and key_images_with_tx_hashes for more details
+ *
+ * @param tx_infos [out] the transactions' information
+ * @param key_image_infos [out] the spent key images' information
+ *
+ * @return true
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
+ * @brief check for presence of key images in the pool
+ *
+ * @param key_images [in] vector of key images to check
+ * @param spent [out] vector of bool to return
+ *
+ * @return true
+ */
+ bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const;
+
+ /**
* @brief get a specific transaction from the pool
*
* @param h the hash of the transaction to get
diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt
index 4ce380a48..347e48eee 100644
--- a/src/cryptonote_protocol/CMakeLists.txt
+++ b/src/cryptonote_protocol/CMakeLists.txt
@@ -39,5 +39,3 @@ target_link_libraries(cryptonote_protocol
p2p
PRIVATE
${EXTRA_LIBRARIES})
-add_dependencies(cryptonote_protocol
- version)
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index 02a8e3ec2..64dd1fb50 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -340,7 +340,7 @@ size_t block_queue::get_num_filled_spans() const
crypto::hash block_queue::get_last_known_hash(const boost::uuids::uuid &connection_id) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
- crypto::hash hash = cryptonote::null_hash;
+ crypto::hash hash = crypto::null_hash;
uint64_t highest_height = 0;
for (const auto &span: blocks)
{
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 71e205c23..1804cc101 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -128,12 +128,10 @@ namespace cryptonote
{
block_complete_entry b;
uint64_t current_blockchain_height;
- uint32_t hop;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(b)
KV_SERIALIZE(current_blockchain_height)
- KV_SERIALIZE(hop)
END_KV_SERIALIZE_MAP()
};
};
@@ -254,12 +252,10 @@ namespace cryptonote
{
block_complete_entry b;
uint64_t current_blockchain_height;
- uint32_t hop;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(b)
KV_SERIALIZE(current_blockchain_height)
- KV_SERIALIZE(hop)
END_KV_SERIALIZE_MAP()
};
};
@@ -276,13 +272,11 @@ namespace cryptonote
crypto::hash block_hash;
uint64_t current_blockchain_height;
std::vector<size_t> missing_tx_indices;
- uint32_t hop;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash)
KV_SERIALIZE(current_blockchain_height)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices)
- KV_SERIALIZE(hop)
END_KV_SERIALIZE_MAP()
};
};
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index e762cf9c8..58e5fc380 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -336,7 +336,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)");
+ MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (" << arg.b.txs.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks
@@ -364,7 +364,12 @@ namespace cryptonote
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block
- m_core.cleanup_handle_incoming_blocks(true);
+ if (!m_core.cleanup_handle_incoming_blocks(true))
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ m_core.resume_mine();
+ return 1;
+ }
m_core.resume_mine();
if(bvc.m_verifivation_failed)
{
@@ -374,7 +379,6 @@ namespace cryptonote
}
if(bvc.m_added_to_main_chain)
{
- ++arg.hop;
//TODO: Add here announce protocol usage
relay_block(arg, context);
}else if(bvc.m_marked_as_orphaned)
@@ -392,7 +396,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", hop " << arg.hop << ", " << arg.b.txs.size() << " txes)");
+ MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks
@@ -602,7 +606,6 @@ namespace cryptonote
MDEBUG(" tx " << new_block.tx_hashes[txidx]);
NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req;
missing_tx_req.block_hash = get_block_hash(new_block);
- missing_tx_req.hop = arg.hop;
missing_tx_req.current_blockchain_height = arg.current_blockchain_height;
missing_tx_req.missing_tx_indices = std::move(need_tx_indices);
@@ -623,7 +626,12 @@ namespace cryptonote
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block
- m_core.cleanup_handle_incoming_blocks(true);
+ if (!m_core.cleanup_handle_incoming_blocks(true))
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ m_core.resume_mine();
+ return 1;
+ }
m_core.resume_mine();
if( bvc.m_verifivation_failed )
@@ -634,10 +642,8 @@ namespace cryptonote
}
if( bvc.m_added_to_main_chain )
{
- ++arg.hop;
//TODO: Add here announce protocol usage
NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg);
- reg_arg.hop = arg.hop;
reg_arg.current_blockchain_height = arg.current_blockchain_height;
reg_arg.b = b;
relay_block(reg_arg, context);
@@ -690,7 +696,6 @@ namespace cryptonote
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response;
fluffy_response.b.block = t_serializable_object_to_blob(b);
fluffy_response.current_blockchain_height = arg.current_blockchain_height;
- fluffy_response.hop = arg.hop;
for(auto& tx_idx: arg.missing_tx_indices)
{
if(tx_idx < b.tx_hashes.size())
@@ -926,10 +931,12 @@ namespace cryptonote
}
// get the last parsed block, which should be the highest one
- if(m_core.have_block(cryptonote::get_block_hash(b)))
+ const crypto::hash last_block_hash = cryptonote::get_block_hash(b);
+ if(m_core.have_block(last_block_hash))
{
const uint64_t subchain_height = start_height + arg.blocks.size();
LOG_DEBUG_CC(context, "These are old blocks, ignoring: blocks " << start_height << " - " << (subchain_height-1) << ", blockchain height " << m_core.get_current_blockchain_height());
+ m_block_queue.remove_spans(context.m_connection_id, start_height);
goto skip;
}
@@ -943,7 +950,7 @@ namespace cryptonote
MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1e3) << " kB/s, size now " << (m_block_queue.get_data_size() + blocks_size) / 1048576.f << " MB");
m_block_queue.add_blocks(start_height, arg.blocks, context.m_connection_id, rate, blocks_size);
- context.m_last_known_hash = cryptonote::get_blob_hash(arg.blocks.back().block);
+ context.m_last_known_hash = last_block_hash;
if (!m_core.get_test_drop_download() || !m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing
return 1;
@@ -1055,7 +1062,11 @@ skip:
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- m_core.cleanup_handle_incoming_blocks();
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return 1;
+ }
// in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
@@ -1080,7 +1091,12 @@ skip:
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- m_core.cleanup_handle_incoming_blocks();
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return 1;
+ }
+
// in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
@@ -1094,7 +1110,12 @@ skip:
}))
LOG_ERROR_CCONTEXT("span connection id not found");
- m_core.cleanup_handle_incoming_blocks();
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return 1;
+ }
+
// in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it
m_block_queue.remove_spans(span_connection_id, start_height);
return 1;
@@ -1107,7 +1128,11 @@ skip:
MCINFO("sync-info", "Block process time (" << blocks.size() << " blocks, " << num_txs << " txs): " << block_process_time_full + transactions_process_time_full << " (" << transactions_process_time_full << "/" << block_process_time_full << ") ms");
- m_core.cleanup_handle_incoming_blocks();
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return 1;
+ }
m_block_queue.remove_spans(span_connection_id, start_height);
@@ -1459,7 +1484,7 @@ skip:
if (!start_from_current_chain)
{
// we'll want to start off from where we are on that peer, which may not be added yet
- if (context.m_last_known_hash != cryptonote::null_hash && r.block_ids.front() != context.m_last_known_hash)
+ if (context.m_last_known_hash != crypto::null_hash && r.block_ids.front() != context.m_last_known_hash)
r.block_ids.push_front(context.m_last_known_hash);
}
@@ -1576,7 +1601,6 @@ skip:
bool t_cryptonote_protocol_handler<t_core>::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)
{
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg);
- fluffy_arg.hop = arg.hop;
fluffy_arg.current_blockchain_height = arg.current_blockchain_height;
std::list<blobdata> fluffy_txs;
fluffy_arg.b = arg.b;
@@ -1593,7 +1617,7 @@ skip:
{
if (peer_id && exclude_context.m_connection_id != context.m_connection_id)
{
- if(m_core.get_testnet() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
+ if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
{
LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK");
fluffyConnections.push_back(context.m_connection_id);
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 795442a2d..d0fc1d846 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -92,14 +92,17 @@ target_link_libraries(daemon
p2p
cryptonote_protocol
daemonizer
+ serialization
+ daemon_rpc_server
+ version
${Boost_CHRONO_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
+ ${ZMQ_LIB}
${EXTRA_LIBRARIES})
-add_dependencies(daemon version)
set_property(TARGET daemon
PROPERTY
OUTPUT_NAME "monerod")
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 8eb3db195..7fa58f9d8 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -46,6 +46,11 @@ namespace daemon_args
, "Specify log file"
, ""
};
+ const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = {
+ "max-log-file-size"
+ , "Specify maximum log file size [B]"
+ , MAX_LOG_FILE_SIZE
+ };
const command_line::arg_descriptor<std::string> arg_log_level = {
"log-level"
, ""
@@ -64,6 +69,25 @@ namespace daemon_args
, "Max number of threads to use for a parallel job"
, 0
};
+
+ const command_line::arg_descriptor<std::string> arg_zmq_rpc_bind_ip = {
+ "zmq-rpc-bind-ip"
+ , "IP for ZMQ RPC server to listen on"
+ , "127.0.0.1"
+ };
+
+ const command_line::arg_descriptor<std::string> arg_zmq_rpc_bind_port = {
+ "zmq-rpc-bind-port"
+ , "Port for ZMQ RPC server to listen on"
+ , std::to_string(config::ZMQ_RPC_DEFAULT_PORT)
+ };
+
+ const command_line::arg_descriptor<std::string> arg_zmq_testnet_rpc_bind_port = {
+ "zmq-testnet-rpc-bind-port"
+ , "Port for testnet ZMQ RPC server to listen on"
+ , std::to_string(config::testnet::ZMQ_RPC_DEFAULT_PORT)
+ };
+
} // namespace daemon_args
#endif // DAEMON_COMMAND_LINE_ARGS_H
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index f301ef14a..6443d9be0 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -36,7 +36,7 @@
#pragma once
-#include <boost/optional/optional_fwd.hpp>
+#include <boost/optional/optional.hpp>
#include "daemon/rpc_command_executor.h"
#include "common/common_fwd.h"
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 12f7c5fa4..b9f503c6b 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -231,7 +231,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"print_coinbase_tx_sum"
, std::bind(&t_command_parser_executor::print_coinbase_tx_sum, &m_parser, p::_1)
- , "Print sum of coinbase transactions (start height, block count)"
+ , "Print sum of coinbase transactions <start height> [block count]"
);
m_command_lookup.set_handler(
"alt_chain_info"
@@ -241,7 +241,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"bc_dyn_stats"
, std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1)
- , "Print information about current blockchain dynamic state"
+ , "Print information about current blockchain dynamic state, bc_dyn_stats <last n blocks>"
);
m_command_lookup.set_handler(
"update"
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 683eaf4ff..faa620c54 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -32,6 +32,8 @@
#include <stdexcept>
#include "misc_log_ex.h"
#include "daemon/daemon.h"
+#include "rpc/daemon_handler.h"
+#include "rpc/zmq_server.h"
#include "common/password.h"
#include "common/util.h"
@@ -85,7 +87,18 @@ t_daemon::t_daemon(
boost::program_options::variables_map const & vm
)
: mp_internals{new t_internals{vm}}
-{}
+{
+ bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+ if (testnet)
+ {
+ zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_testnet_rpc_bind_port);
+ }
+ else
+ {
+ zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port);
+ }
+ zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip);
+}
t_daemon::~t_daemon() = default;
@@ -133,6 +146,30 @@ bool t_daemon::run(bool interactive)
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
}
+ cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get());
+ cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler);
+
+ if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port))
+ {
+ LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
+
+ if (interactive)
+ {
+ rpc_commands->stop_handling();
+ }
+
+ mp_internals->rpc.stop();
+
+ return false;
+ }
+
+ MINFO("Starting ZMQ server...");
+ zmq_server.run();
+
+ MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ".");
+
mp_internals->p2p.run(); // blocks until p2p goes down
if (rpc_commands)
@@ -140,6 +177,8 @@ bool t_daemon::run(bool interactive)
rpc_commands->stop_handling();
}
+ zmq_server.stop();
+
mp_internals->rpc.stop();
mp_internals->core.get().get_miner().stop();
MGINFO("Node stopped.");
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index 2b9f18669..8c7547f62 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -43,6 +43,8 @@ private:
void stop_p2p();
private:
std::unique_ptr<t_internals> mp_internals;
+ std::string zmq_rpc_bind_address;
+ std::string zmq_rpc_bind_port;
public:
t_daemon(
boost::program_options::variables_map const & vm
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 456eeee64..acc23b9f9 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -88,7 +88,11 @@ int main(int argc, char const * argv[])
bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log");
command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string());
command_line::add_arg(core_settings, daemon_args::arg_log_level);
+ command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size);
command_line::add_arg(core_settings, daemon_args::arg_max_concurrency);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_testnet_rpc_bind_port);
daemonizer::init_options(hidden_options, visible_options);
daemonize::t_executor::init_options(core_settings);
@@ -142,7 +146,7 @@ int main(int argc, char const * argv[])
epee::debug::g_test_dbg_lock_sleep() = command_line::get_arg(vm, command_line::arg_test_dbg_lock_sleep);
- std::string db_type = command_line::get_arg(vm, command_line::arg_db_type);
+ std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
// verify that blockchaindb type is valid
if(!cryptonote::blockchain_valid_db_type(db_type))
@@ -204,7 +208,7 @@ int main(int argc, char const * argv[])
if (! vm["log-file"].defaulted())
log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file);
log_file_path = bf::absolute(log_file_path, relative_path_base);
- mlog_configure(log_file_path.string(), true);
+ mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size));
// Set log level
if (!vm["log-level"].defaulted())
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index eefba8449..3cd32ec2f 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -420,16 +420,17 @@ bool t_rpc_command_executor::show_status() {
}
std::time_t uptime = std::time(nullptr) - ires.start_time;
+ uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height;
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
% (unsigned long long)ires.height
- % (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height)
+ % (unsigned long long)net_height
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : "mainnet")
% (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
- % get_fork_extra_info(hfres.earliest_height, ires.height, ires.target)
+ % get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
% (unsigned)ires.outgoing_connections_count
% (unsigned)ires.incoming_connections_count
@@ -699,6 +700,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) {
std::string fail_message = "Problem fetching transaction";
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(transaction_hash));
+ req.decode_as_json = false;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/gettransactions", fail_message.c_str()))
@@ -781,7 +783,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) {
if (1 == res.spent_status.size())
{
// first as hex
- tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent");
+ tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent") << (res.spent_status.front() == cryptonote::COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_POOL ? " (in pool)" : "");
}
else
{
diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt
index e6d49fd61..7a82c12d9 100644
--- a/src/debug_utilities/CMakeLists.txt
+++ b/src/debug_utilities/CMakeLists.txt
@@ -37,14 +37,11 @@ monero_add_executable(cn_deserialize
target_link_libraries(cn_deserialize
LINK_PRIVATE
cryptonote_core
- common
blockchain_db
p2p
epee
${CMAKE_THREAD_LIBS_INIT})
-add_dependencies(cn_deserialize
- version)
set_property(TARGET cn_deserialize
PROPERTY
OUTPUT_NAME "monero-utils-deserialize")
@@ -66,8 +63,6 @@ target_link_libraries(object_sizes
epee
${CMAKE_THREAD_LIBS_INIT})
-add_dependencies(object_sizes
- version)
set_property(TARGET object_sizes
PROPERTY
OUTPUT_NAME "monero-utils-object-sizes")
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index 0704940b5..123b0a272 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -39,6 +39,7 @@ monero_add_library(p2p ${P2P})
target_link_libraries(p2p
PUBLIC
epee
+ version
${UPNP_LIBRARIES}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
@@ -47,5 +48,3 @@ target_link_libraries(p2p
${Boost_THREAD_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
-add_dependencies(p2p
- version)
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 22ae5ec26..8bbaa9138 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -105,7 +105,7 @@ namespace nodetool
bool init(const boost::program_options::variables_map& vm);
bool deinit();
bool send_stop_signal();
- uint32_t get_this_peer_port(){return m_listenning_port;}
+ uint32_t get_this_peer_port(){return m_listening_port;}
t_payload_net_handler& get_payload_object();
template <class Archive, class t_version_type>
@@ -218,6 +218,8 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
+ void add_upnp_port_mapping(uint32_t port);
+ void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb);
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
@@ -287,7 +289,7 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
- uint32_t m_listenning_port;
+ uint32_t m_listening_port;
uint32_t m_external_port;
uint32_t m_ip_address;
bool m_allow_local_ip;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 889cfaf9d..f1ca50f76 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -573,55 +573,15 @@ namespace nodetool
res = m_net_server.init_server(m_port, m_bind_ip);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
- m_listenning_port = m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listenning_port);
+ m_listening_port = m_net_server.get_binded_port();
+ MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listening_port);
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
- // Add UPnP port mapping
- if(m_no_igd == false) {
- MDEBUG("Attempting to add IGD port mapping.");
- int result;
-#if MINIUPNPC_API_VERSION > 13
- // default according to miniupnpc.h
- unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
-#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
-#endif
- UPNPUrls urls;
- IGDdatas igdData;
- char lanAddress[64];
- result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
- freeUPNPDevlist(deviceList);
- if (result != 0) {
- if (result == 1) {
- std::ostringstream portString;
- portString << m_listenning_port;
-
- // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly
- UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
-
- int portMappingResult;
- portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0");
- if (portMappingResult != 0) {
- LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult));
- } else {
- MLOG_GREEN(el::Level::Info, "Added IGD port mapping.");
- }
- } else if (result == 2) {
- MWARNING("IGD was found but reported as not connected.");
- } else if (result == 3) {
- MWARNING("UPnP device was found but not recognized as IGD.");
- } else {
- MWARNING("UPNP_GetValidIGD returned an unknown result code.");
- }
+ // add UPnP port mapping
+ if(!m_no_igd)
+ add_upnp_port_mapping(m_listening_port);
- FreeUPNPUrls(&urls);
- } else {
- MINFO("No IGD was found.");
- }
- }
return res;
}
//-----------------------------------------------------------------------------------
@@ -688,6 +648,9 @@ namespace nodetool
kill();
m_peerlist.deinit();
m_net_server.deinit_server();
+ // remove UPnP port mapping
+ if(!m_no_igd)
+ delete_upnp_port_mapping(m_listening_port);
return store_config();
}
//-----------------------------------------------------------------------------------
@@ -1127,6 +1090,8 @@ namespace nodetool
if (use_white_list) {
local_peers_count = m_peerlist.get_white_peers_count();
+ if (!local_peers_count)
+ return false;
max_random_index = std::min<uint64_t>(local_peers_count -1, 20);
random_index = get_random_index_with_fixed_probability(max_random_index);
} else {
@@ -1389,7 +1354,7 @@ namespace nodetool
node_data.local_time = local_time;
node_data.peer_id = m_config.m_peer_id;
if(!m_hide_my_port)
- node_data.my_port = m_external_port ? m_external_port : m_listenning_port;
+ node_data.my_port = m_external_port ? m_external_port : m_listening_port;
else
node_data.my_port = 0;
node_data.network_id = m_network_id;
@@ -1951,8 +1916,13 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::gray_peerlist_housekeeping()
{
+ if (!m_exclusive_peers.empty()) return true;
+
peerlist_entry pe = AUTO_VAL_INIT(pe);
+ if (m_net_server.is_stop_signal_sent())
+ return false;
+
if (!m_peerlist.get_random_gray_peer(pe)) {
return false;
}
@@ -1973,4 +1943,93 @@ namespace nodetool
return true;
}
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ {
+ MDEBUG("Attempting to add IGD port mapping.");
+ int result;
+#if MINIUPNPC_API_VERSION > 13
+ // default according to miniupnpc.h
+ unsigned char ttl = 2;
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+#else
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+#endif
+ UPNPUrls urls;
+ IGDdatas igdData;
+ char lanAddress[64];
+ result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
+ freeUPNPDevlist(deviceList);
+ if (result != 0) {
+ if (result == 1) {
+ std::ostringstream portString;
+ portString << port;
+
+ // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly
+ UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
+
+ int portMappingResult;
+ portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0");
+ if (portMappingResult != 0) {
+ LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult));
+ } else {
+ MLOG_GREEN(el::Level::Info, "Added IGD port mapping.");
+ }
+ } else if (result == 2) {
+ MWARNING("IGD was found but reported as not connected.");
+ } else if (result == 3) {
+ MWARNING("UPnP device was found but not recognized as IGD.");
+ } else {
+ MWARNING("UPNP_GetValidIGD returned an unknown result code.");
+ }
+
+ FreeUPNPUrls(&urls);
+ } else {
+ MINFO("No IGD was found.");
+ }
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ MDEBUG("Attempting to delete IGD port mapping.");
+ int result;
+#if MINIUPNPC_API_VERSION > 13
+ // default according to miniupnpc.h
+ unsigned char ttl = 2;
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+#else
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+#endif
+ UPNPUrls urls;
+ IGDdatas igdData;
+ char lanAddress[64];
+ result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
+ freeUPNPDevlist(deviceList);
+ if (result != 0) {
+ if (result == 1) {
+ std::ostringstream portString;
+ portString << port;
+
+ int portMappingResult;
+ portMappingResult = UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
+ if (portMappingResult != 0) {
+ LOG_ERROR("UPNP_DeletePortMapping failed, error: " << strupnperror(portMappingResult));
+ } else {
+ MLOG_GREEN(el::Level::Info, "Deleted IGD port mapping.");
+ }
+ } else if (result == 2) {
+ MWARNING("IGD was found but reported as not connected.");
+ } else if (result == 3) {
+ MWARNING("UPnP device was found but not recognized as IGD.");
+ } else {
+ MWARNING("UPNP_GetValidIGD returned an unknown result code.");
+ }
+
+ FreeUPNPUrls(&urls);
+ } else {
+ MINFO("No IGD was found.");
+ }
+ }
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index a30a05422..8372445aa 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -190,35 +190,16 @@ namespace nodetool
if (ver < 6)
return;
- if(ver < 3)
- return;
CRITICAL_REGION_LOCAL(m_peerlist_lock);
- if(ver < 4)
- {
- //loading data from old storage
- peers_indexed_old pio;
- a & pio;
- peers_indexed_from_old(pio, m_peers_white);
- return;
- }
#if 0
// trouble loading more than one peer, can't find why
a & m_peers_white;
a & m_peers_gray;
+ a & m_peers_anchor;
#else
serialize_peers(a, m_peers_white, peerlist_entry(), ver);
serialize_peers(a, m_peers_gray, peerlist_entry(), ver);
-#endif
-
- if(ver < 5) {
- return;
- }
-
-#if 0
- // trouble loading more than one peer, can't find why
- a & m_peers_anchor;
-#else
serialize_peers(a, m_peers_anchor, anchor_peerlist_entry(), ver);
#endif
}
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 43c5ea5f0..6ea2d48fd 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -59,6 +59,8 @@ namespace boost
{
a & na.m_ip;
a & na.m_port;
+ if (!typename Archive::is_saving())
+ na.init_ids();
}
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 8efd6a07c..946325367 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -30,8 +30,7 @@
#include "misc_log_ex.h"
#include "common/perf_timer.h"
-#include "common/task_region.h"
-#include "common/thread_group.h"
+#include "common/threadpool.h"
#include "common/util.h"
#include "rctSigs.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -731,17 +730,16 @@ namespace rct {
try
{
if (semantics) {
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
std::deque<bool> results(rv.outPk.size(), false);
- tools::thread_group threadpool(tools::thread_group::optimal_with_max(rv.outPk.size()));
-
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- DP("range proofs verified?");
- for (size_t i = 0; i < rv.outPk.size(); i++) {
- region.run([&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
- }
- });
+ DP("range proofs verified?");
+ for (size_t i = 0; i < rv.outPk.size(); i++) {
+ tpool.submit(&waiter, [&, i] {
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < rv.outPk.size(); ++i) {
if (!results[i]) {
@@ -794,7 +792,8 @@ namespace rct {
const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
std::deque<bool> results(threads);
- tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads));
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
if (semantics) {
key sumOutpks = identity();
@@ -819,13 +818,12 @@ namespace rct {
results.clear();
results.resize(rv.outPk.size());
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- for (size_t i = 0; i < rv.outPk.size(); i++) {
- region.run([&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
- }
- });
+ for (size_t i = 0; i < rv.outPk.size(); i++) {
+ tpool.submit(&waiter, [&, i] {
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
@@ -839,13 +837,12 @@ namespace rct {
results.clear();
results.resize(rv.mixRing.size());
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
- region.run([&, i] {
+ for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
+ tpool.submit(&waiter, [&, i] {
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
- });
- }
- });
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index f6037d7e3..23bb6aaae 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -30,20 +30,61 @@ set(rpc_sources
core_rpc_server.cpp
rpc_args.cpp)
+set(daemon_messages_sources
+ message.cpp
+ daemon_messages.cpp)
+
+set(daemon_rpc_server_sources
+ daemon_handler.cpp
+ zmq_server.cpp)
+
+
set(rpc_headers
rpc_args.h)
-set(rpc_private_headers
+set(daemon_rpc_server_headers)
+
+
+set(rpc_daemon_private_headers
core_rpc_server.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
+set(daemon_messages_private_headers
+ message.h
+ daemon_messages.h)
+
+set(daemon_rpc_server_private_headers
+ message.h
+ daemon_messages.h
+ daemon_handler.h
+ rpc_handler.h
+ zmq_server.h)
+
+
monero_private_headers(rpc
${rpc_private_headers})
+
+monero_private_headers(daemon_rpc_server
+ ${daemon_rpc_server_private_headers})
+
+
monero_add_library(rpc
${rpc_sources}
${rpc_headers}
${rpc_private_headers})
+
+monero_add_library(daemon_messages
+ ${daemon_messages_sources}
+ ${daemon_messages_headers}
+ ${daemon_messages_private_headers})
+
+monero_add_library(daemon_rpc_server
+ ${daemon_rpc_server_sources}
+ ${daemon_rpc_server_headers}
+ ${daemon_rpc_server_private_headers})
+
+
target_link_libraries(rpc
PUBLIC
common
@@ -54,5 +95,25 @@ target_link_libraries(rpc
${Boost_THREAD_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
-add_dependencies(rpc
- version)
+
+target_link_libraries(daemon_messages
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ serialization
+ ${EXTRA_LIBRARIES})
+
+target_link_libraries(daemon_rpc_server
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ daemon_messages
+ serialization
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${ZMQ_LIB}
+ ${EXTRA_LIBRARIES})
+target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
+target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 3fbe174b9..9730cdf46 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -50,6 +50,16 @@ using namespace epee;
#define MAX_RESTRICTED_FAKE_OUTS_COUNT 40
#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500
+namespace
+{
+ void add_reason(std::string &reasons, const char *reason)
+ {
+ if (!reasons.empty())
+ reasons += ", ";
+ reasons += reason;
+ }
+}
+
namespace cryptonote
{
@@ -128,16 +138,12 @@ namespace cryptonote
{
CHECK_CORE_BUSY();
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.top_block_hash = string_tools::pod_to_hex(top_hash);
res.target_height = m_core.get_target_blockchain_height();
res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block();
- res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+ res.target = m_core.get_blockchain_storage().get_difficulty_target();
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
res.tx_pool_size = m_core.get_pool_transactions_count();
res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count();
@@ -478,18 +484,30 @@ namespace cryptonote
bool r = m_core.get_pool_transactions(pool_txs);
if(r)
{
- for (std::list<transaction>::const_iterator i = pool_txs.begin(); i != pool_txs.end(); ++i)
+ // sort to match original request
+ std::list<transaction> sorted_txs;
+ std::list<cryptonote::transaction>::const_iterator i;
+ for (const crypto::hash &h: vh)
{
- crypto::hash tx_hash = get_transaction_hash(*i);
- std::list<crypto::hash>::iterator mi = std::find(missed_txs.begin(), missed_txs.end(), tx_hash);
- if (mi != missed_txs.end())
+ if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
{
- pool_tx_hashes.insert(tx_hash);
- missed_txs.erase(mi);
- txs.push_back(*i);
+ // core returns the ones it finds in the right order
+ if (get_transaction_hash(txs.front()) != h)
+ {
+ res.status = "Failed: tx hash mismatch";
+ return true;
+ }
+ sorted_txs.push_back(std::move(txs.front()));
+ txs.pop_front();
+ }
+ else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](cryptonote::transaction &tx) { return h == cryptonote::get_transaction_hash(tx); })) != pool_txs.end())
+ {
+ sorted_txs.push_back(*i);
+ missed_txs.remove(h);
++found_in_pool;
}
}
+ txs = sorted_txs;
}
LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool");
}
@@ -510,11 +528,12 @@ namespace cryptonote
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
- e.block_height = std::numeric_limits<uint64_t>::max();
+ e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
}
else
{
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
+ e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
}
// fill up old style responses too, in case an old wallet asks
@@ -623,31 +642,33 @@ namespace cryptonote
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed)
{
- if (tvc.m_verifivation_failed)
- {
- LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
- }
- else
- {
- LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
- }
res.status = "Failed";
+ res.reason = "";
if ((res.low_mixin = tvc.m_low_mixin))
- res.reason = "ring size too small";
+ add_reason(res.reason, "ring size too small");
if ((res.double_spend = tvc.m_double_spend))
- res.reason = "double spend";
+ add_reason(res.reason, "double spend");
if ((res.invalid_input = tvc.m_invalid_input))
- res.reason = "invalid input";
+ add_reason(res.reason, "invalid input");
if ((res.invalid_output = tvc.m_invalid_output))
- res.reason = "invalid output";
+ add_reason(res.reason, "invalid output");
if ((res.too_big = tvc.m_too_big))
- res.reason = "too big";
+ add_reason(res.reason, "too big");
if ((res.overspend = tvc.m_overspend))
- res.reason = "overspend";
+ add_reason(res.reason, "overspend");
if ((res.fee_too_low = tvc.m_fee_too_low))
- res.reason = "fee too low";
+ add_reason(res.reason, "fee too low");
if ((res.not_rct = tvc.m_not_rct))
- res.reason = "tx is not ringct";
+ add_reason(res.reason, "tx is not ringct");
+ const std::string punctuation = res.reason.empty() ? "" : ": ";
+ if (tvc.m_verifivation_failed)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed" << punctuation << res.reason);
+ }
+ else
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx" << punctuation << res.reason);
+ }
return true;
}
@@ -948,7 +969,7 @@ namespace cryptonote
LOG_ERROR("Failed to find tx pub key in blockblob");
return false;
}
- res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
if(res.reserved_offset + req.reserve_size > block_blob.size())
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1048,13 +1069,7 @@ namespace cryptonote
}
uint64_t last_block_height;
crypto::hash last_block_hash;
- bool have_last_block_hash = m_core.get_blockchain_top(last_block_height, last_block_hash);
- if (!have_last_block_hash)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't get last block hash.";
- return false;
- }
+ m_core.get_blockchain_top(last_block_height, last_block_hash);
block last_block;
bool have_last_block = m_core.get_block_by_hash(last_block_hash, last_block);
if (!have_last_block)
@@ -1287,11 +1302,7 @@ namespace cryptonote
}
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.top_block_hash = string_tools::pod_to_hex(top_hash);
res.target_height = m_core.get_target_blockchain_height();
@@ -1750,11 +1761,7 @@ namespace cryptonote
}
crypto::hash top_hash;
- if (!m_core.get_blockchain_top(res.height, top_hash))
- {
- res.status = "Failed";
- return false;
- }
+ m_core.get_blockchain_top(res.height, top_hash);
++res.height; // turn top block height into blockchain height
res.target_height = m_core.get_target_blockchain_height();
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 3b10d08ae..73a308a72 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -127,7 +127,7 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
- MAP_JON_RPC_WE_IF("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG, !m_restricted)
+ MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG)
END_JSON_RPC_MAP()
END_URI_MAP2()
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index c7e968319..ecf546bb0 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -215,6 +215,7 @@ namespace cryptonote
std::string as_json;
bool in_pool;
uint64_t block_height;
+ uint64_t block_timestamp;
std::vector<uint64_t> output_indices;
BEGIN_KV_SERIALIZE_MAP()
@@ -223,6 +224,7 @@ namespace cryptonote
KV_SERIALIZE(as_json)
KV_SERIALIZE(in_pool)
KV_SERIALIZE(block_height)
+ KV_SERIALIZE(block_timestamp)
KV_SERIALIZE(output_indices)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
new file mode 100644
index 000000000..9a6c61b10
--- /dev/null
+++ b/src/rpc/daemon_handler.cpp
@@ -0,0 +1,887 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "daemon_handler.h"
+
+// likely included by daemon_handler.h's includes,
+// but including here for clarity
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_protocol/blobdatatype.h"
+#include "ringct/rctSigs.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res)
+ {
+ res.height = m_core.get_current_blockchain_height();
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res)
+ {
+ std::list<std::pair<blobdata, std::list<blobdata> > > blocks;
+
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::find_blockchain_supplement() returned false";
+ return;
+ }
+
+ res.blocks.resize(blocks.size());
+ res.output_indices.resize(blocks.size());
+
+ //TODO: really need to switch uses of std::list to std::vector unless
+ // it's a huge performance concern
+
+ auto it = blocks.begin();
+
+ uint64_t block_count = 0;
+ while (it != blocks.end())
+ {
+ cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count];
+
+ if (!parse_and_validate_block_from_blob(it->first, bwt.block))
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "failed retrieving a requested block";
+ return;
+ }
+
+ if (it->second.size() != bwt.block.tx_hashes.size())
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "incorrect number of transactions retrieved for block";
+ return;
+ }
+ std::list<transaction> txs;
+ for (const auto& blob : it->second)
+ {
+ txs.resize(txs.size() + 1);
+ if (!parse_and_validate_tx_from_blob(blob, txs.back()))
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "failed retrieving a requested transaction";
+ return;
+ }
+ }
+
+ cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count];
+
+ // miner tx output indices
+ {
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+ indices.push_back(tx_indices);
+ }
+
+ // assume each block returned is returned with all its transactions
+ // in the correct order.
+ auto tx_it = txs.begin();
+ for (const crypto::hash& h : bwt.block.tx_hashes)
+ {
+ bwt.transactions.emplace(h, *tx_it);
+ tx_it++;
+
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(h, tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+
+ indices.push_back(tx_indices);
+ }
+
+ it++;
+ block_count++;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res)
+ {
+ res.start_height = req.start_height;
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Blockchain::find_blockchain_supplement() returned false";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
+ {
+ std::list<cryptonote::transaction> found_txs;
+ std::list<crypto::hash> missed_hashes;
+
+ bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes);
+
+ // TODO: consider fixing core::get_transactions to not hide exceptions
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_transactions() returned false (exception caught there)";
+ return;
+ }
+
+ size_t num_found = found_txs.size();
+
+ // std::list is annoying
+ std::vector<cryptonote::transaction> found_txs_vec
+ {
+ std::make_move_iterator(std::begin(found_txs)),
+ std::make_move_iterator(std::end(found_txs))
+ };
+
+ std::vector<crypto::hash> missed_vec
+ {
+ std::make_move_iterator(std::begin(missed_hashes)),
+ std::make_move_iterator(std::end(missed_hashes))
+ };
+
+ std::vector<uint64_t> heights(num_found);
+ std::vector<bool> in_pool(num_found, false);
+ std::vector<crypto::hash> found_hashes(num_found);
+
+ for (size_t i=0; i < num_found; i++)
+ {
+ found_hashes[i] = get_transaction_hash(found_txs_vec[i]);
+ heights[i] = m_core.get_blockchain_storage().get_db().get_tx_block_height(found_hashes[i]);
+ }
+
+ // if any missing from blockchain, check in tx pool
+ if (!missed_vec.empty())
+ {
+ std::list<cryptonote::transaction> pool_txs;
+
+ m_core.get_pool_transactions(pool_txs);
+
+ for (const auto& tx : pool_txs)
+ {
+ crypto::hash h = get_transaction_hash(tx);
+
+ auto itr = std::find(missed_vec.begin(), missed_vec.end(), h);
+
+ if (itr != missed_vec.end())
+ {
+ found_hashes.push_back(h);
+ found_txs_vec.push_back(tx);
+ heights.push_back(std::numeric_limits<uint64_t>::max());
+ in_pool.push_back(true);
+ missed_vec.erase(itr);
+ }
+ }
+ }
+
+ for (size_t i=0; i < found_hashes.size(); i++)
+ {
+ cryptonote::rpc::transaction_info info;
+ info.height = heights[i];
+ info.in_pool = in_pool[i];
+ info.transaction = std::move(found_txs_vec[i]);
+
+ res.txs.emplace(found_hashes[i], std::move(info));
+ }
+
+ res.missed_hashes = std::move(missed_vec);
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res)
+ {
+ res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT);
+
+ std::vector<bool> chain_spent_status;
+ std::vector<bool> pool_spent_status;
+
+ m_core.are_key_images_spent(req.key_images, chain_spent_status);
+ m_core.are_key_images_spent_in_pool(req.key_images, pool_spent_status);
+
+ if ((chain_spent_status.size() != req.key_images.size()) || (pool_spent_status.size() != req.key_images.size()))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "tx_pool::have_key_images_as_spent() gave vectors of wrong size(s).";
+ return;
+ }
+
+ for(size_t i=0; i < req.key_images.size(); i++)
+ {
+ if ( chain_spent_status[i] )
+ {
+ res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_BLOCKCHAIN;
+ }
+ else if ( pool_spent_status[i] )
+ {
+ res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_POOL;
+ }
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res)
+ {
+ if (!m_core.get_tx_outputs_gindexs(req.tx_hash, res.output_indices))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+
+ }
+
+ //TODO: handle "restricted" RPC
+ void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res)
+ {
+ auto& chain = m_core.get_blockchain_storage();
+
+ try
+ {
+ for (const uint64_t& amount : req.amounts)
+ {
+ std::vector<uint64_t> indices = chain.get_random_outputs(amount, req.count);
+
+ outputs_for_amount ofa;
+
+ ofa.resize(indices.size());
+
+ for (size_t i = 0; i < indices.size(); i++)
+ {
+ crypto::public_key key = chain.get_output_key(amount, indices[i]);
+ ofa[i].amount_index = indices[i];
+ ofa[i].key = key;
+ }
+
+ amount_with_random_outputs amt;
+ amt.amount = amount;
+ amt.outputs = ofa;
+
+ res.amounts_with_outputs.push_back(amt);
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+ catch (const std::exception& e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ }
+ }
+
+ void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
+ {
+ auto tx_blob = cryptonote::tx_to_blob(req.tx);
+
+ cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
+ tx_verification_context tvc = AUTO_VAL_INIT(tvc);
+
+ if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed)
+ {
+ if (tvc.m_verifivation_failed)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
+ }
+ else
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
+ }
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "";
+
+ if (tvc.m_low_mixin)
+ {
+ res.error_details = "mixin too low";
+ }
+ if (tvc.m_double_spend)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "double spend";
+ }
+ if (tvc.m_invalid_input)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "invalid input";
+ }
+ if (tvc.m_invalid_output)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "invalid output";
+ }
+ if (tvc.m_too_big)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "too big";
+ }
+ if (tvc.m_overspend)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "overspend";
+ }
+ if (tvc.m_fee_too_low)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "fee too low";
+ }
+ if (tvc.m_not_rct)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "tx is not ringct";
+ }
+ if (res.error_details.empty())
+ {
+ res.error_details = "an unknown issue was found with the transaction";
+ }
+
+ return;
+ }
+
+ if(!tvc.m_should_be_relayed || !req.relay)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed");
+ res.error_details = "Not relayed";
+ res.relayed = false;
+ res.status = Message::STATUS_OK;
+
+ return;
+ }
+
+ NOTIFY_NEW_TRANSACTIONS::request r;
+ r.txs.push_back(tx_blob);
+ m_core.get_protocol()->relay_transactions(r, fake_context);
+
+ //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
+ res.status = Message::STATUS_OK;
+ res.relayed = true;
+
+ return;
+ }
+
+ void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res)
+ {
+ account_public_address adr;
+ if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address))
+ {
+ res.error_details = "Failed, wrong address";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
+
+ // if we couldn't detect threads, set it to a ridiculously high number
+ if(concurrency_count == 0)
+ {
+ concurrency_count = 257;
+ }
+
+ // if there are more threads requested than the hardware supports
+ // then we fail and log that.
+ if(req.threads_count > concurrency_count)
+ {
+ res.error_details = "Failed, too many threads relative to CPU cores.";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+
+ if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ {
+ res.error_details = "Failed, mining not started";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+
+ }
+
+ void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res)
+ {
+ res.info.height = m_core.get_current_blockchain_height();
+
+ res.info.target_height = m_core.get_target_blockchain_height();
+
+ if (res.info.height > res.info.target_height)
+ {
+ res.info.target_height = res.info.height;
+ }
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ res.info.difficulty = chain.get_difficulty_for_next_block();
+
+ res.info.target = chain.get_difficulty_target();
+
+ res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase
+
+ res.info.tx_pool_size = m_core.get_pool_transactions_count();
+
+ res.info.alt_blocks_count = chain.get_alternative_blocks_count();
+
+ uint64_t total_conn = m_p2p.get_connections_count();
+ res.info.outgoing_connections_count = m_p2p.get_outgoing_connections_count();
+ res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count;
+
+ res.info.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count();
+
+ res.info.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count();
+
+ res.info.testnet = m_core.get_testnet();
+ res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
+ res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
+ res.info.start_time = (uint64_t)m_core.get_start_time();
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res)
+ {
+ if(!m_core.get_miner().stop())
+ {
+ res.error_details = "Failed, mining not stopped";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res)
+ {
+ const cryptonote::miner& lMiner = m_core.get_miner();
+ res.active = lMiner.is_mining();
+ res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled();
+
+ if ( lMiner.is_mining() ) {
+ res.speed = lMiner.get_speed();
+ res.threads_count = lMiner.get_threads_count();
+ const account_public_address& lMiningAdr = lMiner.get_mining_address();
+ res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr);
+ }
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res)
+ {
+ if (!m_core.get_blockchain_storage().store_blockchain())
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error storing the blockchain";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ }
+ }
+
+ void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res)
+ {
+ if (m_core.get_current_blockchain_height() <= req.height)
+ {
+ res.hash = crypto::null_hash;
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "height given is higher than current chain height";
+ return;
+ }
+
+ res.hash = m_core.get_block_id_by_height(req.height);
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SubmitBlock::Request& req, SubmitBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_tail_id();
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res)
+ {
+ if (!getBlockHeaderByHash(req.hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res)
+ {
+ res.headers.resize(req.heights.size());
+
+ for (size_t i=0; i < req.heights.size(); i++)
+ {
+ const crypto::hash block_hash = m_core.get_block_id_by_height(req.heights[i]);
+
+ if (!getBlockHeaderByHash(block_hash, res.headers[i]))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "A requested block does not exist";
+ return;
+ }
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res)
+ {
+ if (req.level < 0 || req.level > 4)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error: log level not valid";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ mlog_set_log_level(req.level);
+ }
+ }
+
+ void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
+ {
+ bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
+
+ if (!r) res.status = Message::STATUS_FAILED;
+ else res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res)
+ {
+ const Blockchain &blockchain = m_core.get_blockchain_storage();
+ uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version();
+ res.info.version = blockchain.get_current_hard_fork_version();
+ res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting);
+ res.info.state = blockchain.get_hard_fork_state();
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res)
+ {
+ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t> > histogram;
+ try
+ {
+ histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff);
+ }
+ catch (const std::exception &e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ return;
+ }
+
+ res.histogram.clear();
+ res.histogram.reserve(histogram.size());
+ for (const auto &i: histogram)
+ {
+ if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0))
+ res.histogram.emplace_back(output_amount_count{i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)});
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res)
+ {
+ try
+ {
+ for (const auto& i : req.outputs)
+ {
+ crypto::public_key key;
+ rct::key mask;
+ bool unlocked;
+ m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked);
+ res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked});
+ }
+ }
+ catch (const std::exception& e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res)
+ {
+ res.version = DAEMON_RPC_VERSION_ZMQ;
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
+ {
+ res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks);
+ res.status = Message::STATUS_OK;
+ }
+
+ bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
+ {
+ block b;
+
+ if (!m_core.get_block_by_hash(hash_in, b))
+ {
+ return false;
+ }
+
+ header.hash = hash_in;
+ header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height;
+
+ header.major_version = b.major_version;
+ header.minor_version = b.minor_version;
+ header.timestamp = b.timestamp;
+ header.nonce = b.nonce;
+ header.prev_id = b.prev_id;
+
+ header.depth = m_core.get_current_blockchain_height() - header.height - 1;
+
+ header.reward = 0;
+ for (const auto& out : b.miner_tx.vout)
+ {
+ header.reward += out.amount;
+ }
+
+ header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height);
+
+ return true;
+ }
+
+ std::string DaemonHandler::handle(const std::string& request)
+ {
+ MDEBUG("Handling RPC request: " << request);
+
+ Message* resp_message = NULL;
+
+ try
+ {
+ FullMessage req_full(request, true);
+
+ rapidjson::Value& req_json = req_full.getMessage();
+
+ const std::string request_type = req_full.getRequestType();
+
+ // create correct Message subclass and call handle() on it
+ REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle);
+
+ // if none of the request types matches
+ if (resp_message == NULL)
+ {
+ return BAD_REQUEST(request_type, req_full.getID());
+ }
+
+ FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID());
+
+ const std::string response = resp_full.getJson();
+ delete resp_message;
+ resp_message = NULL;
+
+ MDEBUG("Returning RPC response: " << response);
+
+ return response;
+ }
+ catch (const std::exception& e)
+ {
+ if (resp_message)
+ {
+ delete resp_message;
+ }
+
+ return BAD_JSON(e.what());
+ }
+ }
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
new file mode 100644
index 000000000..0d356bad2
--- /dev/null
+++ b/src/rpc/daemon_handler.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "daemon_messages.h"
+#include "daemon_rpc_version.h"
+#include "rpc_handler.h"
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_protocol/cryptonote_protocol_handler.h"
+#include "p2p/net_node.h"
+
+namespace
+{
+ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > t_p2p;
+} // anonymous namespace
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+class DaemonHandler : public RpcHandler
+{
+ public:
+
+ DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { }
+
+ ~DaemonHandler() { }
+
+ void handle(const GetHeight::Request& req, GetHeight::Response& res);
+
+ void handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res);
+
+ void handle(const GetHashesFast::Request& req, GetHashesFast::Response& res);
+
+ void handle(const GetTransactions::Request& req, GetTransactions::Response& res);
+
+ void handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res);
+
+ void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res);
+
+ void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res);
+
+ void handle(const SendRawTx::Request& req, SendRawTx::Response& res);
+
+ void handle(const StartMining::Request& req, StartMining::Response& res);
+
+ void handle(const GetInfo::Request& req, GetInfo::Response& res);
+
+ void handle(const StopMining::Request& req, StopMining::Response& res);
+
+ void handle(const MiningStatus::Request& req, MiningStatus::Response& res);
+
+ void handle(const SaveBC::Request& req, SaveBC::Response& res);
+
+ void handle(const GetBlockHash::Request& req, GetBlockHash::Response& res);
+
+ void handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res);
+
+ void handle(const SubmitBlock::Request& req, SubmitBlock::Response& res);
+
+ void handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res);
+
+ void handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res);
+
+ void handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res);
+
+ void handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res);
+
+ void handle(const GetBlock::Request& req, GetBlock::Response& res);
+
+ void handle(const GetPeerList::Request& req, GetPeerList::Response& res);
+
+ void handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res);
+
+ void handle(const SetLogLevel::Request& req, SetLogLevel::Response& res);
+
+ void handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res);
+
+ void handle(const GetConnections::Request& req, GetConnections::Response& res);
+
+ void handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res);
+
+ void handle(const StopDaemon::Request& req, StopDaemon::Response& res);
+
+ void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res);
+
+ void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res);
+
+ void handle(const HardForkInfo::Request& req, HardForkInfo::Response& res);
+
+ void handle(const GetBans::Request& req, GetBans::Response& res);
+
+ void handle(const SetBans::Request& req, SetBans::Response& res);
+
+ void handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res);
+
+ void handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res);
+
+ void handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res);
+
+ void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res);
+
+ void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res);
+
+ std::string handle(const std::string& request);
+
+ private:
+
+ bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response);
+
+ cryptonote::core& m_core;
+ t_p2p& m_p2p;
+};
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
new file mode 100644
index 000000000..640058df9
--- /dev/null
+++ b/src/rpc/daemon_messages.cpp
@@ -0,0 +1,900 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "daemon_messages.h"
+#include "serialization/json_object.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* const GetHeight::name = "get_height";
+const char* const GetBlocksFast::name = "get_blocks_fast";
+const char* const GetHashesFast::name = "get_hashes_fast";
+const char* const GetTransactions::name = "get_transactions";
+const char* const KeyImagesSpent::name = "key_images_spent";
+const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
+const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts";
+const char* const SendRawTx::name = "send_raw_tx";
+const char* const StartMining::name = "start_mining";
+const char* const StopMining::name = "stop_mining";
+const char* const MiningStatus::name = "mining_status";
+const char* const GetInfo::name = "get_info";
+const char* const SaveBC::name = "save_bc";
+const char* const GetBlockHash::name = "get_block_hash";
+const char* const GetLastBlockHeader::name = "get_last_block_header";
+const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash";
+const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height";
+const char* const GetBlockHeadersByHeight::name = "get_block_headers_by_height";
+const char* const GetPeerList::name = "get_peer_list";
+const char* const SetLogLevel::name = "set_log_level";
+const char* const GetTransactionPool::name = "get_transaction_pool";
+const char* const HardForkInfo::name = "hard_fork_info";
+const char* const GetOutputHistogram::name = "get_output_histogram";
+const char* const GetOutputKeys::name = "get_output_keys";
+const char* const GetRPCVersion::name = "get_rpc_version";
+const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate";
+
+
+
+
+rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetHeight::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("height", height, al);
+
+ return val;
+}
+
+void GetHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+
+rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("prune", prune, al);
+
+ return val;
+}
+
+void GetBlocksFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, block_ids, block_ids);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, prune, prune);
+}
+
+rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetBlocksFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, blocks, blocks);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes);
+ val.AddMember("start_height", start_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+}
+
+rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hashes, hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+}
+
+
+rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes);
+
+ return val;
+}
+
+void GetTransactions::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes);
+}
+
+rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, txs, txs);
+ INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes);
+
+ return val;
+}
+
+void GetTransactions::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, txs, txs);
+ GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes);
+}
+
+
+rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void KeyImagesSpent::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status);
+
+ return val;
+}
+
+void KeyImagesSpent::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, spent_status, spent_status);
+}
+
+
+rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash);
+}
+
+rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, count, count);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, count, count);
+}
+
+rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs);
+}
+
+
+rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx, tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
+
+ return val;
+}
+
+void SendRawTx::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx, tx);
+ GET_FROM_JSON_OBJECT(val, relay, relay);
+}
+
+rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed);
+
+ return val;
+}
+
+
+void SendRawTx::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, relayed, relayed);
+}
+
+rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address);
+ INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining);
+ INSERT_INTO_JSON_OBJECT(val, doc, ignore_battery, ignore_battery);
+
+ return val;
+}
+
+void StartMining::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, miner_address, miner_address);
+ GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
+ GET_FROM_JSON_OBJECT(val, do_background_mining, do_background_mining);
+ GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery);
+}
+
+rapidjson::Value StartMining::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StartMining::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StopMining::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StopMining::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void MiningStatus::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, active, active);
+ INSERT_INTO_JSON_OBJECT(val, doc, speed, speed);
+ INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, address, address);
+ INSERT_INTO_JSON_OBJECT(val, doc, is_background_mining_enabled, is_background_mining_enabled);
+
+ return val;
+}
+
+void MiningStatus::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, active, active);
+ GET_FROM_JSON_OBJECT(val, speed, speed);
+ GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
+ GET_FROM_JSON_OBJECT(val, address, address);
+ GET_FROM_JSON_OBJECT(val, is_background_mining_enabled, is_background_mining_enabled);
+}
+
+
+rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetInfo::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, info, info);
+
+ return val;
+}
+
+void GetInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, info, info);
+}
+
+
+rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+
+rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, heights, heights);
+
+ return val;
+}
+
+void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, heights, heights);
+}
+
+rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, headers, headers);
+
+ return val;
+}
+
+void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, headers, headers);
+}
+
+
+rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetPeerList::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list);
+ INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list);
+
+ return val;
+}
+
+void GetPeerList::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, white_list, white_list);
+ GET_FROM_JSON_OBJECT(val, gray_list, gray_list);
+}
+
+
+rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("level", level, al);
+
+ return val;
+}
+
+void SetLogLevel::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, level, level);
+}
+
+rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void SetLogLevel::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetTransactionPool::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void GetTransactionPool::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, transactions, transactions);
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+
+rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void HardForkInfo::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, info, info);
+
+ return val;
+}
+
+void HardForkInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, info, info);
+}
+
+
+rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked);
+ INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff);
+
+ return val;
+}
+
+void GetOutputHistogram::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, min_count, min_count);
+ GET_FROM_JSON_OBJECT(val, max_count, max_count);
+ GET_FROM_JSON_OBJECT(val, unlocked, unlocked);
+ GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff);
+}
+
+rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram);
+
+ return val;
+}
+
+void GetOutputHistogram::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, histogram, histogram);
+}
+
+
+rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs);
+
+ return val;
+}
+
+void GetOutputKeys::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, outputs, outputs);
+}
+
+rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, keys, keys);
+
+ return val;
+}
+
+void GetOutputKeys::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, keys, keys);
+}
+
+
+rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetRPCVersion::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb);
+}
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
new file mode 100644
index 000000000..685f66843
--- /dev/null
+++ b/src/rpc/daemon_messages.h
@@ -0,0 +1,421 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "message.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "rpc/message_data_structs.h"
+#include "rpc/daemon_rpc_version.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+
+#define BEGIN_RPC_MESSAGE_CLASS(classname) \
+class classname \
+{ \
+ public: \
+ static const char* const name;
+
+#define BEGIN_RPC_MESSAGE_REQUEST \
+ class Request : public Message \
+ { \
+ public: \
+ Request() { } \
+ ~Request() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define BEGIN_RPC_MESSAGE_RESPONSE \
+ class Response : public Message \
+ { \
+ public: \
+ Response() { } \
+ ~Response() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define END_RPC_MESSAGE_REQUEST };
+#define END_RPC_MESSAGE_RESPONSE };
+#define END_RPC_MESSAGE_CLASS };
+
+#define COMMA() ,
+
+// NOTE: when using a type with multiple template parameters,
+// replace any comma in the template specifier with the macro
+// above, or the preprocessor will eat the comma in a bad way.
+#define RPC_MESSAGE_MEMBER(type, name) type name;
+
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+BEGIN_RPC_MESSAGE_CLASS(GetHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, block_ids);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(bool, prune);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_with_transactions>, blocks);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(uint64_t, current_height);
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_output_indices>, output_indices);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetHashesFast);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, known_hashes);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, hashes);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(uint64_t, current_height);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetTransactions);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, tx_hashes);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::unordered_map<crypto::hash COMMA() cryptonote::rpc::transaction_info>, txs);
+ RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, missed_hashes);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(KeyImagesSpent);
+ enum STATUS {
+ UNSPENT = 0,
+ SPENT_IN_BLOCKCHAIN = 1,
+ SPENT_IN_POOL = 2,
+ };
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<crypto::key_image>, key_images);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, spent_status);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetTxGlobalOutputIndices);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(crypto::hash, tx_hash);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, output_indices);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetRandomOutputsForAmounts);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts);
+ RPC_MESSAGE_MEMBER(uint64_t, count);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<amount_with_random_outputs>, amounts_with_outputs);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SendRawTx);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(cryptonote::transaction, tx);
+ RPC_MESSAGE_MEMBER(bool, relay);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, relayed);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StartMining);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::string, miner_address);
+ RPC_MESSAGE_MEMBER(uint64_t, threads_count);
+ RPC_MESSAGE_MEMBER(bool, do_background_mining);
+ RPC_MESSAGE_MEMBER(bool, ignore_battery);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(DaemonInfo, info);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopMining);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(MiningStatus);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, active);
+ RPC_MESSAGE_MEMBER(uint64_t, speed);
+ RPC_MESSAGE_MEMBER(uint64_t, threads_count);
+ RPC_MESSAGE_MEMBER(std::string, address);
+ RPC_MESSAGE_MEMBER(bool, is_background_mining_enabled);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SaveBC);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockTemplate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SubmitBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetLastBlockHeader);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersByHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, heights);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::BlockHeaderResponse>, headers);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetPeerList);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<peer>, white_list);
+ RPC_MESSAGE_MEMBER(std::vector<peer>, gray_list);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetLogHashRate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetLogLevel);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(int8_t, level);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetTransactionPool);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::tx_in_pool>, transactions);
+ RPC_MESSAGE_MEMBER(key_images_with_tx_hashes, key_images);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetConnections);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersRange);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopDaemon);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(HardForkInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint8_t, version);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(hard_fork_info, info);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(FlushTransactionPool);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputHistogram);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts);
+ RPC_MESSAGE_MEMBER(uint64_t, min_count);
+ RPC_MESSAGE_MEMBER(uint64_t, max_count);
+ RPC_MESSAGE_MEMBER(bool, unlocked);
+ RPC_MESSAGE_MEMBER(uint64_t, recent_cutoff);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<output_amount_count>, histogram);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputKeys);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<output_amount_and_index>, outputs);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<output_key_mask_unlocked>, keys);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint32_t, version);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, num_grace_blocks);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, estimated_fee_per_kb);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h
new file mode 100644
index 000000000..bb735fe7c
--- /dev/null
+++ b/src/rpc/daemon_rpc_version.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+static const uint32_t DAEMON_RPC_VERSION_ZMQ_MINOR = 0;
+static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 1;
+
+static const uint32_t DAEMON_RPC_VERSION_ZMQ = DAEMON_RPC_VERSION_ZMQ_MINOR + (DAEMON_RPC_VERSION_ZMQ_MAJOR << 16);
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
new file mode 100644
index 000000000..086820180
--- /dev/null
+++ b/src/rpc/message.cpp
@@ -0,0 +1,286 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "message.h"
+#include "daemon_rpc_version.h"
+#include "serialization/json_object.h"
+
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* Message::STATUS_OK = "OK";
+const char* Message::STATUS_RETRY = "Retry";
+const char* Message::STATUS_FAILED = "Failed";
+const char* Message::STATUS_BAD_REQUEST = "Invalid request type";
+const char* Message::STATUS_BAD_JSON = "Malformed json";
+
+rapidjson::Value Message::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("status", rapidjson::StringRef(status.c_str()), al);
+ val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al);
+ INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION_ZMQ);
+
+ return val;
+}
+
+void Message::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, status, status);
+ GET_FROM_JSON_OBJECT(val, error_details, error_details);
+ GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version);
+}
+
+
+FullMessage::FullMessage(const std::string& request, Message* message)
+{
+ doc.SetObject();
+
+ doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator());
+ doc.AddMember("params", message->toJson(doc), doc.GetAllocator());
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator());
+}
+
+FullMessage::FullMessage(Message* message)
+{
+ doc.SetObject();
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", "2.0", doc.GetAllocator());
+
+ if (message->status == Message::STATUS_OK)
+ {
+ doc.AddMember("response", message->toJson(doc), doc.GetAllocator());
+ }
+ else
+ {
+ cryptonote::rpc::error err;
+
+ err.error_str = message->status;
+ err.message = message->error_details;
+
+ INSERT_INTO_JSON_OBJECT(doc, doc, error, err);
+ }
+}
+
+FullMessage::FullMessage(const std::string& json_string, bool request)
+{
+ doc.Parse(json_string.c_str());
+ if (doc.HasParseError())
+ {
+ throw cryptonote::json::PARSE_FAIL();
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "jsonrpc")
+
+ if (request)
+ {
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "params")
+ }
+ else
+ {
+ if (!doc.HasMember("response") && !doc.HasMember("error"))
+ {
+ throw cryptonote::json::MISSING_KEY("error/response");
+ }
+ }
+}
+
+std::string FullMessage::getJson()
+{
+
+ if (!doc.HasMember("id"))
+ {
+ doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator());
+ }
+
+ rapidjson::StringBuffer buf;
+
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
+
+ doc.Accept(writer);
+
+ return std::string(buf.GetString(), buf.GetSize());
+}
+
+std::string FullMessage::getRequestType() const
+{
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
+ return doc["method"].GetString();
+}
+
+rapidjson::Value& FullMessage::getMessage()
+{
+ if (doc.HasMember("params"))
+ {
+ return doc["params"];
+ }
+ else if (doc.HasMember("response"))
+ {
+ return doc["response"];
+ }
+
+ //else
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "error")
+ return doc["error"];
+
+}
+
+rapidjson::Value FullMessage::getMessageCopy()
+{
+ rapidjson::Value& val = getMessage();
+
+ return rapidjson::Value(val, doc.GetAllocator());
+}
+
+rapidjson::Value& FullMessage::getID()
+{
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "id")
+ return doc["id"];
+}
+
+void FullMessage::setID(rapidjson::Value& id)
+{
+ auto itr = doc.FindMember("id");
+ if (itr != doc.MemberEnd())
+ {
+ itr->value = id;
+ }
+ else
+ {
+ doc.AddMember("id", id, doc.GetAllocator());
+ }
+}
+
+cryptonote::rpc::error FullMessage::getError()
+{
+ cryptonote::rpc::error err;
+ err.use = false;
+ if (doc.HasMember("error"))
+ {
+ GET_FROM_JSON_OBJECT(doc, err, error);
+ err.use = true;
+ }
+
+ return err;
+}
+
+FullMessage FullMessage::requestMessage(const std::string& request, Message* message)
+{
+ return FullMessage(request, message);
+}
+
+FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id)
+{
+ auto mes = requestMessage(request, message);
+ mes.setID(id);
+ return mes;
+}
+
+FullMessage FullMessage::responseMessage(Message* message)
+{
+ return FullMessage(message);
+}
+
+FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id)
+{
+ auto mes = responseMessage(message);
+ mes.setID(id);
+ return mes;
+}
+
+FullMessage* FullMessage::timeoutMessage()
+{
+ auto *full_message = new FullMessage();
+
+ auto& doc = full_message->doc;
+ auto& al = full_message->doc.GetAllocator();
+
+ doc.SetObject();
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", "2.0", al);
+
+ cryptonote::rpc::error err;
+
+ err.error_str = "RPC request timed out.";
+ INSERT_INTO_JSON_OBJECT(doc, doc, err, err);
+
+ return full_message;
+}
+
+// convenience functions for bad input
+std::string BAD_REQUEST(const std::string& request)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_REQUEST;
+ fail.error_details = std::string("\"") + request + "\" is not a valid request.";
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail);
+
+ return fail_response.getJson();
+}
+
+std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_REQUEST;
+ fail.error_details = std::string("\"") + request + "\" is not a valid request.";
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail, id);
+
+ return fail_response.getJson();
+}
+
+std::string BAD_JSON(const std::string& error_details)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_JSON;
+ fail.error_details = error_details;
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail);
+
+ return fail_response.getJson();
+}
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message.h b/src/rpc/message.h
new file mode 100644
index 000000000..d1abe3fbe
--- /dev/null
+++ b/src/rpc/message.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "rapidjson/document.h"
+#include "rpc/message_data_structs.h"
+#include <string>
+
+/* I normally hate using macros, but in this case it would be untenably
+ * verbose to not use a macro. This macro saves the trouble of explicitly
+ * writing the below if block for every single RPC call.
+ */
+#define REQ_RESP_TYPES_MACRO( runtime_str, type, reqjson, resp_message_ptr, handler) \
+ \
+ if (runtime_str == type::name) \
+ { \
+ type::Request reqvar; \
+ type::Response *respvar = new type::Response(); \
+ \
+ reqvar.fromJson(reqjson); \
+ \
+ handler(reqvar, *respvar); \
+ \
+ resp_message_ptr = respvar; \
+ }
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ class Message
+ {
+ public:
+ static const char* STATUS_OK;
+ static const char* STATUS_RETRY;
+ static const char* STATUS_FAILED;
+ static const char* STATUS_BAD_REQUEST;
+ static const char* STATUS_BAD_JSON;
+
+ Message() : status(STATUS_OK) { }
+
+ virtual ~Message() { }
+
+ virtual rapidjson::Value toJson(rapidjson::Document& doc) const;
+
+ virtual void fromJson(rapidjson::Value& val);
+
+ std::string status;
+ std::string error_details;
+ uint32_t rpc_version;
+ };
+
+ class FullMessage
+ {
+ public:
+ ~FullMessage() { }
+
+ FullMessage(FullMessage&& rhs) noexcept : doc(std::move(rhs.doc)) { }
+
+ FullMessage(const std::string& json_string, bool request=false);
+
+ std::string getJson();
+
+ std::string getRequestType() const;
+
+ rapidjson::Value& getMessage();
+
+ rapidjson::Value getMessageCopy();
+
+ rapidjson::Value& getID();
+
+ void setID(rapidjson::Value& id);
+
+ cryptonote::rpc::error getError();
+
+ static FullMessage requestMessage(const std::string& request, Message* message);
+ static FullMessage requestMessage(const std::string& request, Message* message, rapidjson::Value& id);
+
+ static FullMessage responseMessage(Message* message);
+ static FullMessage responseMessage(Message* message, rapidjson::Value& id);
+
+ static FullMessage* timeoutMessage();
+ private:
+
+ FullMessage() = default;
+
+ FullMessage(const std::string& request, Message* message);
+ FullMessage(Message* message);
+
+ rapidjson::Document doc;
+ };
+
+
+ // convenience functions for bad input
+ std::string BAD_REQUEST(const std::string& request);
+ std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id);
+
+ std::string BAD_JSON(const std::string& error_details);
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
new file mode 100644
index 000000000..00f1e0caa
--- /dev/null
+++ b/src/rpc/message_data_structs.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "crypto/hash.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "ringct/rctSigs.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ struct block_with_transactions
+ {
+ cryptonote::block block;
+ std::unordered_map<crypto::hash, cryptonote::transaction> transactions;
+ };
+
+ typedef std::vector<uint64_t> tx_output_indices;
+
+ typedef std::vector<tx_output_indices> block_output_indices;
+
+ struct transaction_info
+ {
+ cryptonote::transaction transaction;
+ bool in_pool;
+ uint64_t height;
+ };
+
+ struct output_key_and_amount_index
+ {
+ uint64_t amount_index;
+ crypto::public_key key;
+ };
+
+ typedef std::vector<output_key_and_amount_index> outputs_for_amount;
+
+ struct amount_with_random_outputs
+ {
+ uint64_t amount;
+ outputs_for_amount outputs;
+ };
+
+ struct peer
+ {
+ uint64_t id;
+ uint32_t ip;
+ uint16_t port;
+ uint64_t last_seen;
+ };
+
+ struct tx_in_pool
+ {
+ cryptonote::transaction tx;
+ crypto::hash tx_hash;
+ uint64_t blob_size;
+ uint64_t fee;
+ crypto::hash max_used_block_hash;
+ uint64_t max_used_block_height;
+ bool kept_by_block;
+ crypto::hash last_failed_block_hash;
+ uint64_t last_failed_block_height;
+ uint64_t receive_time;
+ uint64_t last_relayed_time;
+ bool relayed;
+ bool do_not_relay;
+ };
+
+ typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes;
+
+ struct output_amount_count
+ {
+ uint64_t amount;
+ uint64_t total_count;
+ uint64_t unlocked_count;
+ uint64_t recent_count;
+ };
+
+ struct output_amount_and_index
+ {
+ uint64_t amount;
+ uint64_t index;
+ };
+
+ struct output_key_mask_unlocked
+ {
+ crypto::public_key key;
+ rct::key mask;
+ bool unlocked;
+ };
+
+ struct hard_fork_info
+ {
+ uint8_t version;
+ bool enabled;
+ uint32_t window;
+ uint32_t votes;
+ uint32_t threshold;
+ uint8_t voting;
+ uint32_t state;
+ uint64_t earliest_height;
+ };
+
+ //required by JSON-RPC 2.0 spec
+ struct error
+ {
+ // not really using code, maybe later.
+ error() : use(false), code(1) { }
+
+ bool use; // do not serialize
+
+ int32_t code;
+
+ // not required by spec, but int error codes aren't perfect
+ std::string error_str;
+
+ std::string message;
+
+ //TODO: data member? not required, may want later.
+ };
+
+ struct BlockHeaderResponse
+ {
+ uint64_t major_version;
+ uint64_t minor_version;
+ uint64_t timestamp;
+ crypto::hash prev_id;
+ uint32_t nonce;
+ uint64_t height;
+ uint64_t depth;
+ crypto::hash hash;
+ uint64_t difficulty;
+ uint64_t reward;
+ };
+
+ struct DaemonInfo
+ {
+ uint64_t height;
+ uint64_t target_height;
+ uint64_t difficulty;
+ uint64_t target;
+ uint64_t tx_count;
+ uint64_t tx_pool_size;
+ uint64_t alt_blocks_count;
+ uint64_t outgoing_connections_count;
+ uint64_t incoming_connections_count;
+ uint64_t white_peerlist_size;
+ uint64_t grey_peerlist_size;
+ bool testnet;
+ crypto::hash top_block_hash;
+ uint64_t cumulative_difficulty;
+ uint64_t block_size_limit;
+ uint64_t start_time;
+ };
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h
new file mode 100644
index 000000000..d75180199
--- /dev/null
+++ b/src/rpc/rpc_handler.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <string>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+
+class RpcHandler
+{
+ public:
+
+ virtual std::string handle(const std::string& request) = 0;
+
+ RpcHandler() { }
+
+ virtual ~RpcHandler() { }
+};
+
+
+} // rpc
+
+} // cryptonote
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
new file mode 100644
index 000000000..afdff2328
--- /dev/null
+++ b/src/rpc/zmq_server.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "zmq_server.h"
+#include <boost/chrono/chrono.hpp>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ZmqServer::ZmqServer(RpcHandler& h) :
+ handler(h),
+ stop_signal(false),
+ running(false),
+ context(DEFAULT_NUM_ZMQ_THREADS) // TODO: make this configurable
+{
+}
+
+ZmqServer::~ZmqServer()
+{
+}
+
+void ZmqServer::serve()
+{
+
+ while (1)
+ {
+ try
+ {
+ zmq::message_t message;
+
+ if (!rep_socket)
+ {
+ throw std::runtime_error("ZMQ RPC server reply socket is null");
+ }
+ while (rep_socket->recv(&message))
+ {
+ std::string message_string(reinterpret_cast<const char *>(message.data()), message.size());
+
+ MDEBUG(std::string("Received RPC request: \"") + message_string + "\"");
+
+ std::string response = handler.handle(message_string);
+
+ zmq::message_t reply(response.size());
+ memcpy((void *) reply.data(), response.c_str(), response.size());
+
+ rep_socket->send(reply);
+ MDEBUG(std::string("Sent RPC reply: \"") + response + "\"");
+
+ }
+ }
+ catch (const boost::thread_interrupted& e)
+ {
+ MDEBUG("ZMQ Server thread interrupted.");
+ }
+ catch (const zmq::error_t& e)
+ {
+ MERROR(std::string("ZMQ error: ") + e.what());
+ }
+ boost::this_thread::interruption_point();
+ }
+}
+
+bool ZmqServer::addIPCSocket(std::string address, std::string port)
+{
+ MERROR("ZmqServer::addIPCSocket not yet implemented!");
+ return false;
+}
+
+bool ZmqServer::addTCPSocket(std::string address, std::string port)
+{
+ try
+ {
+ std::string addr_prefix("tcp://");
+
+ rep_socket.reset(new zmq::socket_t(context, ZMQ_REP));
+
+ rep_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS);
+
+ std::string bind_address = addr_prefix + address + std::string(":") + port;
+ rep_socket->bind(bind_address.c_str());
+ }
+ catch (const std::exception& e)
+ {
+ MERROR(std::string("Error creating ZMQ Socket: ") + e.what());
+ return false;
+ }
+ return true;
+}
+
+void ZmqServer::run()
+{
+ running = true;
+ run_thread = boost::thread(boost::bind(&ZmqServer::serve, this));
+}
+
+void ZmqServer::stop()
+{
+ if (!running) return;
+
+ stop_signal = true;
+
+ run_thread.interrupt();
+ run_thread.join();
+
+ running = false;
+
+ return;
+}
+
+
+} // namespace cryptonote
+
+} // namespace rpc
diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h
new file mode 100644
index 000000000..c278ff759
--- /dev/null
+++ b/src/rpc/zmq_server.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <boost/thread/thread.hpp>
+#include <zmq.hpp>
+#include <string>
+#include <memory>
+
+#include "common/command_line.h"
+
+#include "rpc_handler.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1;
+static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000;
+
+class ZmqServer
+{
+ public:
+
+ ZmqServer(RpcHandler& h);
+
+ ~ZmqServer();
+
+ static void init_options(boost::program_options::options_description& desc);
+
+ void serve();
+
+ bool addIPCSocket(std::string address, std::string port);
+ bool addTCPSocket(std::string address, std::string port);
+
+ void run();
+ void stop();
+
+ private:
+ RpcHandler& handler;
+
+ volatile bool stop_signal;
+ volatile bool running;
+
+ zmq::context_t context;
+
+ boost::thread run_thread;
+
+ std::unique_ptr<zmq::socket_t> rep_socket;
+};
+
+
+} // namespace cryptonote
+
+} // namespace rpc
diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt
new file mode 100644
index 000000000..e4aa0b6a2
--- /dev/null
+++ b/src/serialization/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (c) 2016, The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set(serialization_sources
+ json_object.cpp)
+
+set(serialization_headers)
+
+set(serialization_private_headers
+ json_object.h)
+
+monero_private_headers(serialization
+ ${serialization_private_headers})
+monero_add_library(serialization
+ ${serialization_sources}
+ ${serialization_headers}
+ ${serialization_private_headers})
+target_link_libraries(serialization
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${EXTRA_LIBRARIES})
+add_dependencies(serialization
+ version)
+
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
new file mode 100644
index 000000000..e35389f9c
--- /dev/null
+++ b/src/serialization/json_object.cpp
@@ -0,0 +1,1167 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "json_object.h"
+
+#include <limits>
+#include <type_traits>
+#include "string_tools.h"
+
+namespace cryptonote
+{
+
+namespace json
+{
+
+namespace
+{
+ template<typename Source, typename Destination>
+ constexpr bool precision_loss()
+ {
+ return
+ std::numeric_limits<Destination>::is_signed != std::numeric_limits<Source>::is_signed ||
+ std::numeric_limits<Destination>::min() > std::numeric_limits<Source>::min() ||
+ std::numeric_limits<Destination>::max() < std::numeric_limits<Source>::max();
+ }
+
+ template<typename Source, typename Type>
+ void convert_numeric(Source source, Type& i)
+ {
+ static_assert(
+ (std::is_same<Type, char>() && std::is_same<Source, int>()) ||
+ std::numeric_limits<Source>::is_signed == std::numeric_limits<Type>::is_signed,
+ "comparisons below may have undefined behavior"
+ );
+ if (source < std::numeric_limits<Type>::min())
+ {
+ throw WRONG_TYPE{"numeric underflow"};
+ }
+ if (std::numeric_limits<Type>::max() < source)
+ {
+ throw WRONG_TYPE{"numeric overflow"};
+ }
+ i = Type(source);
+ }
+
+ template<typename Type>
+ void to_int(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsInt())
+ {
+ throw WRONG_TYPE{"integer"};
+ }
+ convert_numeric(val.GetInt(), i);
+ }
+ template<typename Type>
+ void to_int64(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsInt64())
+ {
+ throw WRONG_TYPE{"integer"};
+ }
+ convert_numeric(val.GetInt64(), i);
+ }
+
+ template<typename Type>
+ void to_uint(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsUint())
+ {
+ throw WRONG_TYPE{"unsigned integer"};
+ }
+ convert_numeric(val.GetUint(), i);
+ }
+ template<typename Type>
+ void to_uint64(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsUint64())
+ {
+ throw WRONG_TYPE{"unsigned integer"};
+ }
+ convert_numeric(val.GetUint64(), i);
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i.c_str(), doc.GetAllocator());
+}
+
+void fromJsonValue(const rapidjson::Value& val, std::string& str)
+{
+ if (!val.IsString())
+ {
+ throw WRONG_TYPE("string");
+ }
+
+ str = val.GetString();
+}
+
+void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val)
+{
+ val.SetBool(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, bool& b)
+{
+ if (!val.IsBool())
+ {
+ throw WRONG_TYPE("boolean");
+ }
+ b = val.GetBool();
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned char& i)
+{
+ to_uint(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, char& i)
+{
+ to_int(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, signed char& i)
+{
+ to_int(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned short& i)
+{
+ to_uint(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, short& i)
+{
+ to_int(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const unsigned int i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned int& i)
+{
+ to_uint(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const int i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, int& i)
+{
+ to_int(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val)
+{
+ static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "precision loss");
+ val = rapidjson::Value(std::uint64_t(i));
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned long long& i)
+{
+ to_uint64(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val)
+{
+ static_assert(!precision_loss<long long, std::int64_t>(), "precision loss");
+ val = rapidjson::Value(std::int64_t(i));
+}
+
+void fromJsonValue(const rapidjson::Value& val, long long& i)
+{
+ to_int64(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned long& i)
+{
+ to_uint64(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, long& i)
+{
+ to_int64(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, vin, tx.vin);
+ INSERT_INTO_JSON_OBJECT(val, doc, vout, tx.vout);
+ INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra);
+ INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures);
+ INSERT_INTO_JSON_OBJECT(val, doc, rct_signatures, tx.rct_signatures);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx.version, version);
+ GET_FROM_JSON_OBJECT(val, tx.unlock_time, unlock_time);
+ GET_FROM_JSON_OBJECT(val, tx.vin, vin);
+ GET_FROM_JSON_OBJECT(val, tx.vout, vout);
+ GET_FROM_JSON_OBJECT(val, tx.extra, extra);
+ GET_FROM_JSON_OBJECT(val, tx.signatures, signatures);
+ GET_FROM_JSON_OBJECT(val, tx.rct_signatures, rct_signatures);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, major_version, b.major_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, minor_version, b.minor_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, timestamp, b.timestamp);
+ INSERT_INTO_JSON_OBJECT(val, doc, prev_id, b.prev_id);
+ INSERT_INTO_JSON_OBJECT(val, doc, nonce, b.nonce);
+ INSERT_INTO_JSON_OBJECT(val, doc, miner_tx, b.miner_tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, b.tx_hashes);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, b.major_version, major_version);
+ GET_FROM_JSON_OBJECT(val, b.minor_version, minor_version);
+ GET_FROM_JSON_OBJECT(val, b.timestamp, timestamp);
+ GET_FROM_JSON_OBJECT(val, b.prev_id, prev_id);
+ GET_FROM_JSON_OBJECT(val, b.nonce, nonce);
+ GET_FROM_JSON_OBJECT(val, b.miner_tx, miner_tx);
+ GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ if (txin.type() == typeid(cryptonote::txin_gen))
+ {
+ val.AddMember("type", "txin_gen", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_gen>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_script))
+ {
+ val.AddMember("type", "txin_to_script", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_script>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_scripthash))
+ {
+ val.AddMember("type", "txin_to_scripthash", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_scripthash>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_key))
+ {
+ val.AddMember("type", "txin_to_key", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_key>(txin));
+ }
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "type")
+ OBJECT_HAS_MEMBER_OR_THROW(val, "value")
+ if (val["type"]== "txin_gen")
+ {
+ cryptonote::txin_gen tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"]== "txin_to_script")
+ {
+ cryptonote::txin_to_script tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"] == "txin_to_scripthash")
+ {
+ cryptonote::txin_to_scripthash tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"] == "txin_to_key")
+ {
+ cryptonote::txin_to_key tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, txin.height);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.height, height);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.prev, prev);
+ GET_FROM_JSON_OBJECT(val, txin.prevout, prevout);
+ GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(val, doc, script, txin.script);
+ INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.prev, prev);
+ GET_FROM_JSON_OBJECT(val, txin.prevout, prevout);
+ GET_FROM_JSON_OBJECT(val, txin.script, script);
+ GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets);
+ INSERT_INTO_JSON_OBJECT(val, doc, k_image, txin.k_image);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.amount, amount);
+ GET_FROM_JSON_OBJECT(val, txin.key_offsets, key_offsets);
+ GET_FROM_JSON_OBJECT(val, txin.k_image, k_image);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ if (txout.type() == typeid(cryptonote::txout_to_script))
+ {
+ val.AddMember("type", "txout_to_script", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_script>(txout));
+ }
+ else if (txout.type() == typeid(cryptonote::txout_to_scripthash))
+ {
+ val.AddMember("type", "txout_to_scripthash", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_scripthash>(txout));
+ }
+ else if (txout.type() == typeid(cryptonote::txout_to_key))
+ {
+ val.AddMember("type", "txout_to_key", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_key>(txout));
+ }
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "type")
+ OBJECT_HAS_MEMBER_OR_THROW(val, "value")
+ if (val["type"]== "txout_to_script")
+ {
+ cryptonote::txout_to_script tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+ else if (val["type"] == "txout_to_scripthash")
+ {
+ cryptonote::txout_to_scripthash tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+ else if (val["type"] == "txout_to_key")
+ {
+ cryptonote::txout_to_key tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, keys, txout.keys);
+ INSERT_INTO_JSON_OBJECT(val, doc, script, txout.script);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.keys, keys);
+ GET_FROM_JSON_OBJECT(val, txout.script, script);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, txout.hash);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.hash, hash);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key, txout.key);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.key, key);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, target, txout.target);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.amount, amount);
+ GET_FROM_JSON_OBJECT(val, txout.target, target);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ auto& al = doc.GetAllocator();
+ INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming);
+ INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost);
+ INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip);
+ INSERT_INTO_JSON_OBJECT(val, doc, port, info.port);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, recv_count, info.recv_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, recv_idle_time, info.recv_idle_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, send_count, info.send_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, send_idle_time, info.send_idle_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, live_time, info.live_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, avg_download, info.avg_download);
+ INSERT_INTO_JSON_OBJECT(val, doc, current_download, info.current_download);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, avg_upload, info.avg_upload);
+ INSERT_INTO_JSON_OBJECT(val, doc, current_upload, info.current_upload);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.incoming, incoming);
+ GET_FROM_JSON_OBJECT(val, info.localhost, localhost);
+ GET_FROM_JSON_OBJECT(val, info.local_ip, local_ip);
+
+ GET_FROM_JSON_OBJECT(val, info.ip, ip);
+ GET_FROM_JSON_OBJECT(val, info.port, port);
+
+ GET_FROM_JSON_OBJECT(val, info.peer_id, peer_id);
+
+ GET_FROM_JSON_OBJECT(val, info.recv_count, recv_count);
+ GET_FROM_JSON_OBJECT(val, info.recv_idle_time, recv_idle_time);
+
+ GET_FROM_JSON_OBJECT(val, info.send_count, send_count);
+ GET_FROM_JSON_OBJECT(val, info.send_idle_time, send_idle_time);
+
+ GET_FROM_JSON_OBJECT(val, info.state, state);
+
+ GET_FROM_JSON_OBJECT(val, info.live_time, live_time);
+
+ GET_FROM_JSON_OBJECT(val, info.avg_download, avg_download);
+ GET_FROM_JSON_OBJECT(val, info.current_download, current_download);
+
+ GET_FROM_JSON_OBJECT(val, info.avg_upload, avg_upload);
+ GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(val, doc, txs, blk.txs);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, blk.block, block);
+ GET_FROM_JSON_OBJECT(val, blk.txs, txs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.transactions);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, blk.block, block);
+ GET_FROM_JSON_OBJECT(val, blk.transactions, transactions);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, tx_info.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, in_pool, tx_info.in_pool);
+ INSERT_INTO_JSON_OBJECT(val, doc, transaction, tx_info.transaction);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx_info.height, height);
+ GET_FROM_JSON_OBJECT(val, tx_info.in_pool, in_pool);
+ GET_FROM_JSON_OBJECT(val, tx_info.transaction, transaction);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount_index, out.amount_index);
+ INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount_index, amount_index);
+ GET_FROM_JSON_OBJECT(val, out.key, key);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, out.outputs);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.outputs, outputs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, id, peer.id);
+ INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip);
+ INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, peer.id, id);
+ GET_FROM_JSON_OBJECT(val, peer.ip, ip);
+ GET_FROM_JSON_OBJECT(val, peer.port, port);
+ GET_FROM_JSON_OBJECT(val, peer.last_seen, last_seen);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, kept_by_block, tx.kept_by_block);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_hash, tx.last_failed_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_height, tx.last_failed_block_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, receive_time, tx.receive_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed);
+ INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx.tx, tx);
+ GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size);
+ GET_FROM_JSON_OBJECT(val, tx.fee, fee);
+ GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash);
+ GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height);
+ GET_FROM_JSON_OBJECT(val, tx.kept_by_block, kept_by_block);
+ GET_FROM_JSON_OBJECT(val, tx.last_failed_block_hash, last_failed_block_hash);
+ GET_FROM_JSON_OBJECT(val, tx.last_failed_block_height, last_failed_block_height);
+ GET_FROM_JSON_OBJECT(val, tx.receive_time, receive_time);
+ GET_FROM_JSON_OBJECT(val, tx.last_relayed_time, last_relayed_time);
+ GET_FROM_JSON_OBJECT(val, tx.relayed, relayed);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, info.version);
+ INSERT_INTO_JSON_OBJECT(val, doc, enabled, info.enabled);
+ INSERT_INTO_JSON_OBJECT(val, doc, window, info.window);
+ INSERT_INTO_JSON_OBJECT(val, doc, votes, info.votes);
+ INSERT_INTO_JSON_OBJECT(val, doc, threshold, info.threshold);
+ INSERT_INTO_JSON_OBJECT(val, doc, voting, info.voting);
+ INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
+ INSERT_INTO_JSON_OBJECT(val, doc, earliest_height, info.earliest_height);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.version, version);
+ GET_FROM_JSON_OBJECT(val, info.enabled, enabled);
+ GET_FROM_JSON_OBJECT(val, info.window, window);
+ GET_FROM_JSON_OBJECT(val, info.votes, votes);
+ GET_FROM_JSON_OBJECT(val, info.threshold, threshold);
+ GET_FROM_JSON_OBJECT(val, info.voting, voting);
+ GET_FROM_JSON_OBJECT(val, info.state, state);
+ GET_FROM_JSON_OBJECT(val, info.earliest_height, earliest_height);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, total_count, out.total_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked_count, out.unlocked_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, recent_count, out.recent_count);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.total_count, total_count);
+ GET_FROM_JSON_OBJECT(val, out.unlocked_count, unlocked_count);
+ GET_FROM_JSON_OBJECT(val, out.recent_count, recent_count);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, index, out.index);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.index, index);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, out.mask);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked, out.unlocked);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.key, key);
+ GET_FROM_JSON_OBJECT(val, out.mask, mask);
+ GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, code, err.code);
+ INSERT_INTO_JSON_OBJECT(val, doc, error_str, err.error_str);
+ INSERT_INTO_JSON_OBJECT(val, doc, message, err.message);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, error.code, code);
+ GET_FROM_JSON_OBJECT(val, error.error_str, error_str);
+ GET_FROM_JSON_OBJECT(val, error.message, message);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, major_version, response.major_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, minor_version, response.minor_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, timestamp, response.timestamp);
+ INSERT_INTO_JSON_OBJECT(val, doc, prev_id, response.prev_id);
+ INSERT_INTO_JSON_OBJECT(val, doc, nonce, response.nonce);
+ INSERT_INTO_JSON_OBJECT(val, doc, height, response.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, depth, response.depth);
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, response.hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, difficulty, response.difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, reward, response.reward);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, response.major_version, major_version);
+ GET_FROM_JSON_OBJECT(val, response.minor_version, minor_version);
+ GET_FROM_JSON_OBJECT(val, response.timestamp, timestamp);
+ GET_FROM_JSON_OBJECT(val, response.prev_id, prev_id);
+ GET_FROM_JSON_OBJECT(val, response.nonce, nonce);
+ GET_FROM_JSON_OBJECT(val, response.height, height);
+ GET_FROM_JSON_OBJECT(val, response.depth, depth);
+ GET_FROM_JSON_OBJECT(val, response.hash, hash);
+ GET_FROM_JSON_OBJECT(val, response.difficulty, difficulty);
+ GET_FROM_JSON_OBJECT(val, response.reward, reward);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type);
+ INSERT_INTO_JSON_OBJECT(val, doc, message, sig.message);
+ INSERT_INTO_JSON_OBJECT(val, doc, mixRing, sig.mixRing);
+ INSERT_INTO_JSON_OBJECT(val, doc, pseudoOuts, sig.pseudoOuts);
+ INSERT_INTO_JSON_OBJECT(val, doc, ecdhInfo, sig.ecdhInfo);
+ INSERT_INTO_JSON_OBJECT(val, doc, outPk, sig.outPk);
+ INSERT_INTO_JSON_OBJECT(val, doc, txnFee, sig.txnFee);
+ INSERT_INTO_JSON_OBJECT(val, doc, p, sig.p);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.type, type);
+ GET_FROM_JSON_OBJECT(val, sig.message, message);
+ GET_FROM_JSON_OBJECT(val, sig.mixRing, mixRing);
+ GET_FROM_JSON_OBJECT(val, sig.pseudoOuts, pseudoOuts);
+ GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, ecdhInfo);
+ GET_FROM_JSON_OBJECT(val, sig.outPk, outPk);
+ GET_FROM_JSON_OBJECT(val, sig.txnFee, txnFee);
+ GET_FROM_JSON_OBJECT(val, sig.p, p);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, dest, key.dest);
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, key.mask);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+ GET_FROM_JSON_OBJECT(val, key.dest, dest);
+ GET_FROM_JSON_OBJECT(val, key.mask, mask);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, tuple.mask);
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, tuple.amount);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tuple.mask, mask);
+ GET_FROM_JSON_OBJECT(val, tuple.amount, amount);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs);
+ GET_FROM_JSON_OBJECT(val, sig.MGs, MGs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, asig, sig.asig);
+
+ std::vector<rct::key> keyVector(sig.Ci, std::end(sig.Ci));
+ INSERT_INTO_JSON_OBJECT(val, doc, Ci, keyVector);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.asig, asig);
+
+ std::vector<rct::key> keyVector;
+ cryptonote::json::fromJsonValue(val["Ci"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.Ci[i] = keyVector[i];
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ std::vector<rct::key> keyVector(sig.s0, std::end(sig.s0));
+ INSERT_INTO_JSON_OBJECT(val, doc, s0, sig.s0);
+
+ keyVector.assign(sig.s1, std::end(sig.s1));
+ INSERT_INTO_JSON_OBJECT(val, doc, s1, sig.s1);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "s0")
+ std::vector<rct::key> keyVector;
+ cryptonote::json::fromJsonValue(val["s0"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.s0[i] = keyVector[i];
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "s1")
+ keyVector.clear();
+ cryptonote::json::fromJsonValue(val["s1"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.s1[i] = keyVector[i];
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.ee, ee);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ss, sig.ss);
+ INSERT_INTO_JSON_OBJECT(val, doc, cc, sig.cc);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.ss, ss);
+ GET_FROM_JSON_OBJECT(val, sig.cc, cc);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, info.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, target_height, info.target_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, difficulty, info.difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, target, info.target);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_count, info.tx_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, info.tx_pool_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, info.alt_blocks_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, info.outgoing_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, info.incoming_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet);
+ INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
+ INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.height, height);
+ GET_FROM_JSON_OBJECT(val, info.target_height, target_height);
+ GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty);
+ GET_FROM_JSON_OBJECT(val, info.target, target);
+ GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count);
+ GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size);
+ GET_FROM_JSON_OBJECT(val, info.alt_blocks_count, alt_blocks_count);
+ GET_FROM_JSON_OBJECT(val, info.outgoing_connections_count, outgoing_connections_count);
+ GET_FROM_JSON_OBJECT(val, info.incoming_connections_count, incoming_connections_count);
+ GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, info.testnet, testnet);
+ GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
+ GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
+ GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
+ GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
+}
+
+} // namespace json
+
+} // namespace cryptonote
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
new file mode 100644
index 000000000..7b9519c48
--- /dev/null
+++ b/src/serialization/json_object.h
@@ -0,0 +1,371 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "rapidjson/document.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "rpc/message_data_structs.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "common/sfinae_helpers.h"
+
+#define OBJECT_HAS_MEMBER_OR_THROW(val, key) \
+ do \
+ { \
+ if (!val.HasMember(key)) \
+ { \
+ throw cryptonote::json::MISSING_KEY(key); \
+ } \
+ } while (0);
+
+#define INSERT_INTO_JSON_OBJECT(jsonVal, doc, key, source) \
+ rapidjson::Value key##Val; \
+ cryptonote::json::toJsonValue(doc, source, key##Val); \
+ jsonVal.AddMember(#key, key##Val, doc.GetAllocator());
+
+#define GET_FROM_JSON_OBJECT(source, dst, key) \
+ OBJECT_HAS_MEMBER_OR_THROW(source, #key) \
+ decltype(dst) dstVal##key; \
+ cryptonote::json::fromJsonValue(source[#key], dstVal##key); \
+ dst = dstVal##key;
+
+namespace cryptonote
+{
+
+namespace json
+{
+
+struct JSON_ERROR : public std::exception
+{
+ protected:
+ JSON_ERROR() { }
+ std::string m;
+
+ public:
+ virtual ~JSON_ERROR() { }
+
+ const char* what() const throw()
+ {
+ return m.c_str();
+ }
+};
+
+struct MISSING_KEY : public JSON_ERROR
+{
+ MISSING_KEY(const char* key)
+ {
+ m = std::string("Key \"") + key + "\" missing from object.";
+ }
+};
+
+struct WRONG_TYPE : public JSON_ERROR
+{
+ WRONG_TYPE(const char* type)
+ {
+ m = std::string("Json value has incorrect type, expected: ") + type;
+ }
+};
+
+struct BAD_INPUT : public JSON_ERROR
+{
+ BAD_INPUT()
+ {
+ m = "An item failed to convert from json object to native object";
+ }
+};
+
+struct PARSE_FAIL : public JSON_ERROR
+{
+ PARSE_FAIL()
+ {
+ m = "Failed to parse the json request";
+ }
+};
+
+template<typename Type>
+inline constexpr bool is_to_hex()
+{
+ return std::is_pod<Type>() && !std::is_integral<Type>();
+}
+
+
+// POD to json value
+template <class Type>
+typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value)
+{
+ value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator());
+}
+
+template <class Type>
+typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t)
+{
+ if (!val.IsString())
+ {
+ throw WRONG_TYPE("string");
+ }
+
+ //TODO: handle failure to convert hex string to POD type
+ bool success = epee::string_tools::hex_to_pod(val.GetString(), t);
+
+ if (!success)
+ {
+ throw BAD_INPUT();
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, std::string& str);
+
+void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, bool& b);
+
+// integers overloads for toJsonValue are not needed for standard promotions
+
+void fromJsonValue(const rapidjson::Value& val, unsigned char& i);
+
+void fromJsonValue(const rapidjson::Value& val, signed char& i);
+
+void fromJsonValue(const rapidjson::Value& val, char& i);
+
+void fromJsonValue(const rapidjson::Value& val, unsigned short& i);
+
+void fromJsonValue(const rapidjson::Value& val, short& i);
+
+void toJsonValue(rapidjson::Document& doc, const unsigned i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, unsigned& i);
+
+void toJsonValue(rapidjson::Document& doc, const int, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, int& i);
+
+
+void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, unsigned long long& i);
+
+void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, long long& i);
+
+inline void toJsonValue(rapidjson::Document& doc, const unsigned long i, rapidjson::Value& val) {
+ toJsonValue(doc, static_cast<unsigned long long>(i), val);
+}
+void fromJsonValue(const rapidjson::Value& val, unsigned long& i);
+
+inline void toJsonValue(rapidjson::Document& doc, const long i, rapidjson::Value& val) {
+ toJsonValue(doc, static_cast<long long>(i), val);
+}
+void fromJsonValue(const rapidjson::Value& val, long& i);
+
+// end integers
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key);
+
+void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val);
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map);
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val);
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec);
+
+
+// ideally would like to have the below functions in the .cpp file, but
+// unfortunately because of how templates work they have to be here.
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ auto& al = doc.GetAllocator();
+
+ for (const auto& i : map)
+ {
+ rapidjson::Value k;
+ rapidjson::Value m;
+ toJsonValue(doc, i.first, k);
+ toJsonValue(doc, i.second, m);
+ val.AddMember(k, m, al);
+ }
+}
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ auto itr = val.MemberBegin();
+
+ while (itr != val.MemberEnd())
+ {
+ typename Map::key_type k;
+ typename Map::mapped_type m;
+ fromJsonValue(itr->name, k);
+ fromJsonValue(itr->value, m);
+ map.emplace(k, m);
+ ++itr;
+ }
+}
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val)
+{
+ val.SetArray();
+
+ for (const auto& t : vec)
+ {
+ rapidjson::Value v;
+ toJsonValue(doc, t, v);
+ val.PushBack(v, doc.GetAllocator());
+ }
+}
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec)
+{
+ if (!val.IsArray())
+ {
+ throw WRONG_TYPE("json array");
+ }
+
+ for (rapidjson::SizeType i=0; i < val.Size(); i++)
+ {
+ typename Vec::value_type v;
+ fromJsonValue(val[i], v);
+ vec.push_back(v);
+ }
+}
+
+} // namespace json
+
+} // namespace cryptonote
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 639240820..869f5d10e 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -41,6 +41,7 @@
#pragma once
#include <vector>
+#include <deque>
#include <list>
#include <string>
#include <boost/type_traits/is_integral.hpp>
@@ -198,6 +199,11 @@ inline bool do_serialize(Archive &ar, bool &v)
#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \
::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
+/*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION
+ */
+#define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \
+ ::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive<W>::is_saving())
+
/*! \macro END_SERIALIZE
* \brief self-explanatory
*/
@@ -292,6 +298,17 @@ namespace serialization {
vec.resize(size);
}
+ template <typename T>
+ void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/)
+ {
+ }
+
+ template <typename T>
+ void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/)
+ {
+ vec.resize(size);
+ }
+
/*! \fn do_check_stream_state
*
* \brief self explanatory
diff --git a/src/serialization/vector.h b/src/serialization/vector.h
index 598cfb92e..12fd59558 100644
--- a/src/serialization/vector.h
+++ b/src/serialization/vector.h
@@ -37,6 +37,11 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v);
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::vector<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v);
+
namespace serialization
{
namespace detail
@@ -64,7 +69,7 @@ namespace serialization
}
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::vector<T> &v)
+bool do_serialize_vd(Archive<false> &ar, T &v)
{
size_t cnt;
ar.begin_array(cnt);
@@ -93,7 +98,7 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v)
}
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::vector<T> &v)
+bool do_serialize_vd(Archive<true> &ar, T &v)
{
size_t cnt = v.size();
ar.begin_array(cnt);
@@ -110,3 +115,13 @@ bool do_serialize(Archive<true> &ar, std::vector<T> &v)
ar.end_array();
return true;
}
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt
index 443e9b87e..b56085b8f 100644
--- a/src/simplewallet/CMakeLists.txt
+++ b/src/simplewallet/CMakeLists.txt
@@ -49,14 +49,13 @@ target_link_libraries(simplewallet
common
mnemonics
p2p
+ version
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
-add_dependencies(simplewallet
- version)
set_property(TARGET simplewallet
PROPERTY
OUTPUT_NAME "monero-wallet-cli")
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 479adcafc..937403c66 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -290,7 +290,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
return true;
}
-bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
std::string electrum_words;
@@ -311,7 +311,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st
m_wallet->set_seed_language(mnemonic_language);
}
- success = m_wallet->get_seed(electrum_words);
+ std::string seed_pass;
+ if (encrypted)
+ {
+ auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
+ if (std::cin.eof() || !pwd_container)
+ return true;
+ seed_pass = pwd_container->password();
+ }
+
+ success = m_wallet->get_seed(electrum_words, seed_pass);
}
if (success)
@@ -325,6 +334,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st
return true;
}
+bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ return print_seed(false);
+}
+
+bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ return print_seed(true);
+}
+
bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if (m_wallet->watch_only())
@@ -391,6 +410,61 @@ bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vec
return true;
}
+bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ if (!try_connect_to_daemon())
+ {
+ fail_msg_writer() << tr("Cannot connect to daemon");
+ return true;
+ }
+ const uint64_t per_kb_fee = m_wallet->get_per_kb_fee();
+ const uint64_t typical_size_kb = 13;
+ message_writer() << (boost::format(tr("Current fee is %s monero per kB")) % print_money(per_kb_fee)).str();
+
+ std::vector<uint64_t> fees;
+ for (uint32_t priority = 1; priority <= 4; ++priority)
+ {
+ uint64_t mult = m_wallet->get_fee_multiplier(priority);
+ fees.push_back(per_kb_fee * typical_size_kb * mult);
+ }
+ std::vector<std::pair<uint64_t, uint64_t>> blocks;
+ try
+ {
+ uint64_t base_size = typical_size_kb * 1024;
+ blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees);
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Error: failed to estimate backlog array size: ") << e.what();
+ return true;
+ }
+ if (blocks.size() != 4)
+ {
+ fail_msg_writer() << tr("Error: bad estimated backlog array size");
+ return true;
+ }
+
+ for (uint32_t priority = 1; priority <= 4; ++priority)
+ {
+ uint64_t nblocks_low = blocks[priority - 1].first;
+ uint64_t nblocks_high = blocks[priority - 1].second;
+ if (nblocks_low > 0)
+ {
+ std::string msg;
+ if (priority == m_wallet->get_default_priority() || (m_wallet->get_default_priority() == 0 && priority == 2))
+ msg = tr(" (current)");
+ uint64_t minutes_low = nblocks_low * DIFFICULTY_TARGET_V2 / 60, minutes_high = nblocks_high * DIFFICULTY_TARGET_V2 / 60;
+ if (nblocks_high == nblocks_low)
+ message_writer() << (boost::format(tr("%u block (%u minutes) backlog at priority %u%s")) % nblocks_low % minutes_low % priority % msg).str();
+ else
+ message_writer() << (boost::format(tr("%u to %u block (%u to %u minutes) backlog at priority %u")) % nblocks_low % nblocks_high % minutes_low % minutes_high % priority).str();
+ }
+ else
+ message_writer() << tr("No backlog at priority ") << priority;
+ }
+ return true;
+}
+
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -702,6 +776,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key"));
m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed"));
m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-ring-size <n> - set default ring size (default is 5); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-outputs-count [n] - try to keep at least that many outputs of value at least min-outputs-value; min-outputs-value [n] - try to keep at least min-outputs-count outputs of at least that value; merge-destinations <1|0> - whether to merge multiple payments to the same destination address; confirm-backlog <1|0> - whether to warn if there is transaction backlog"));
+ m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display encrypted Electrum-style mnemonic seed"));
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>"));
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>"));
@@ -713,6 +788,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("Set an arbitrary string note for a txid"));
m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), tr("Get a string note for a txid"));
m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), tr("Show wallet status information"));
+ m_cmd_binder.set_handler("wallet_info", boost::bind(&simple_wallet::wallet_info, this, _1), tr("Show wallet information"));
m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), tr("Sign the contents of a file"));
m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file"));
m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images"));
@@ -722,6 +798,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), tr("Show information about a transfer to/from this address"));
m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change wallet password"));
m_cmd_binder.set_handler("payment_id", boost::bind(&simple_wallet::payment_id, this, _1), tr("Generate a new random full size payment id - these will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids"));
+ m_cmd_binder.set_handler("fee", boost::bind(&simple_wallet::print_fee_info, this, _1), tr("Print information about fee and current transaction backlog"));
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
}
//----------------------------------------------------------------------------------------------------
@@ -970,6 +1047,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("Electrum-style word list failed verification");
return false;
}
+
+ auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none"));
+ if (std::cin.eof() || !pwd_container)
+ return false;
+ std::string seed_pass = pwd_container->password();
+ if (!seed_pass.empty())
+ m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
}
if (!m_generate_from_view_key.empty())
{
@@ -1624,9 +1708,18 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
catch (const std::exception& e)
{
fail_msg_writer() << tr("failed to load wallet: ") << e.what();
- // only suggest removing cache if the password was actually correct
- if (m_wallet && m_wallet->verify_password(password))
- fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file;
+ if (m_wallet)
+ {
+ // only suggest removing cache if the password was actually correct
+ bool password_is_correct = false;
+ try
+ {
+ password_is_correct = m_wallet->verify_password(password);
+ }
+ catch (...) { } // guard against I/O errors
+ if (password_is_correct)
+ fail_msg_writer() << boost::format(tr("You may want to remove the file \"%s\" and try again")) % m_wallet_file;
+ }
return false;
}
success_msg_writer() <<
@@ -2273,7 +2366,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
- size_t fake_outs_count;
+ size_t fake_outs_count = 0;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
@@ -2431,6 +2524,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
break;
default:
LOG_ERROR("Unknown transfer method, using original");
+ /* FALLTHRU */
case TransferOriginal:
ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
break;
@@ -2461,9 +2555,16 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
try
{
- uint64_t nblocks = m_wallet->estimate_backlog(size, fee);
- if (nblocks > 0)
- prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No)")) % nblocks).str();
+ std::vector<std::pair<uint64_t, uint64_t>> nblocks = m_wallet->estimate_backlog(size, size, {fee});
+ if (nblocks.size() != 1)
+ {
+ prompt << "Internal error checking for backlog. " << tr("Is this okay anyway? (Y/Yes/N/No): ");
+ }
+ else
+ {
+ if (nblocks[0].first > 0)
+ prompt << (boost::format(tr("There is currently a %u block backlog at that fee level. Is this okay? (Y/Yes/N/No)")) % nblocks[0].first).str();
+ }
}
catch (const std::exception &e)
{
@@ -2842,7 +2943,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
std::vector<std::string> local_args = args_;
- size_t fake_outs_count;
+ size_t fake_outs_count = 0;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
@@ -3177,7 +3278,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
std::vector<tx_extra_field> tx_extra_fields;
bool has_encrypted_payment_id = false;
- crypto::hash8 payment_id8 = cryptonote::null_hash8;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
{
tx_extra_nonce extra_nonce;
@@ -3410,6 +3511,9 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
catch (const tools::error::tx_rejected& e)
{
fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
+ std::string reason = e.reason();
+ if (!reason.empty())
+ fail_msg_writer() << tr("Reason: ") << reason;
}
catch (const tools::error::tx_sum_overflow& e)
{
@@ -4443,6 +4547,15 @@ bool simple_wallet::status(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::wallet_info(const std::vector<std::string> &args)
+{
+ message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
+ message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ message_writer() << tr("Watch only: ") << (m_wallet->watch_only() ? tr("Yes") : tr("No"));
+ message_writer() << tr("Testnet: ") << (m_wallet->testnet() ? tr("Yes") : tr("No"));
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::sign(const std::vector<std::string> &args)
{
if (args.size() != 1)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index eac4cbc99..3b29e3ca2 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -97,6 +97,7 @@ namespace cryptonote
bool viewkey(const std::vector<std::string> &args = std::vector<std::string>());
bool spendkey(const std::vector<std::string> &args = std::vector<std::string>());
bool seed(const std::vector<std::string> &args = std::vector<std::string>());
+ bool encrypted_seed(const std::vector<std::string> &args = std::vector<std::string>());
/*!
* \brief Sets seed language.
@@ -165,6 +166,7 @@ namespace cryptonote
bool set_tx_note(const std::vector<std::string> &args);
bool get_tx_note(const std::vector<std::string> &args);
bool status(const std::vector<std::string> &args);
+ bool wallet_info(const std::vector<std::string> &args);
bool set_default_priority(const std::vector<std::string> &args);
bool sign(const std::vector<std::string> &args);
bool verify(const std::vector<std::string> &args);
@@ -175,6 +177,7 @@ namespace cryptonote
bool show_transfer(const std::vector<std::string> &args);
bool change_password(const std::vector<std::string>& args);
bool payment_id(const std::vector<std::string> &args);
+ bool print_fee_info(const std::vector<std::string> &args);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);
@@ -184,6 +187,7 @@ namespace cryptonote
bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
std::string get_prompt() const;
+ bool print_seed(bool encrypted);
/*!
* \brief Prints the seed with a nice message
diff --git a/src/version.cmake b/src/version.cmake
index 623d3cf1f..45a97cd20 100644
--- a/src/version.cmake
+++ b/src/version.cmake
@@ -36,7 +36,7 @@ if(RET)
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
set(VERSIONTAG "unknown")
- configure_file("src/version.h.in" "${TO}")
+ configure_file("src/version.cpp.in" "${TO}")
else()
message(STATUS "You are currently on commit ${COMMIT}")
@@ -59,5 +59,5 @@ else()
endif()
endif()
- configure_file("src/version.h.in" "${TO}")
+ configure_file("src/version.cpp.in" "${TO}")
endif()
diff --git a/src/version.cpp.in b/src/version.cpp.in
new file mode 100644
index 000000000..d1444f867
--- /dev/null
+++ b/src/version.cpp.in
@@ -0,0 +1,11 @@
+#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
+#define DEF_MONERO_VERSION "0.11.0.0"
+#define DEF_MONERO_RELEASE_NAME "Helium Hydra"
+#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
+
+#include "version.h"
+
+const char* const MONERO_VERSION_TAG = DEF_MONERO_VERSION_TAG;
+const char* const MONERO_VERSION = DEF_MONERO_VERSION;
+const char* const MONERO_RELEASE_NAME = DEF_MONERO_RELEASE_NAME;
+const char* const MONERO_VERSION_FULL = DEF_MONERO_VERSION_FULL;
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 000000000..d1d06c790
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,6 @@
+#pragma once
+
+extern const char* const MONERO_VERSION_TAG;
+extern const char* const MONERO_VERSION;
+extern const char* const MONERO_RELEASE_NAME;
+extern const char* const MONERO_VERSION_FULL;
diff --git a/src/version.h.in b/src/version.h.in
deleted file mode 100644
index 281b52db4..000000000
--- a/src/version.h.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#define MONERO_VERSION_TAG "@VERSIONTAG@"
-#define MONERO_VERSION "0.11.0.0"
-#define MONERO_RELEASE_NAME "Helium Hydra"
-#define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 639080051..fe87d0de1 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -84,7 +84,6 @@ target_link_libraries(wallet
${Boost_REGEX_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
-add_dependencies(wallet version)
if (NOT BUILD_GUI_DEPS)
set(wallet_rpc_sources
@@ -110,13 +109,13 @@ if (NOT BUILD_GUI_DEPS)
cryptonote_core
cncrypto
common
+ version
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${EXTRA_LIBRARIES})
- add_dependencies(wallet_rpc_server version)
set_property(TARGET wallet_rpc_server
PROPERTY
OUTPUT_NAME "monero-wallet-rpc")
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index 28f835ebd..9605047b7 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -42,7 +42,7 @@ namespace Monero {
AddressBook::~AddressBook() {}
AddressBookImpl::AddressBookImpl(WalletImpl *wallet)
- : m_wallet(wallet) {}
+ : m_wallet(wallet), m_errorCode(Status_Ok) {}
bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &payment_id_str, const std::string &description)
{
@@ -57,7 +57,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
return false;
}
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
bool has_long_pid = (payment_id_str.empty())? false : tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
// Short payment id provided
@@ -106,7 +106,7 @@ void AddressBookImpl::refresh()
for (size_t i = 0; i < rows.size(); ++i) {
tools::wallet2::address_book_row * row = &rows.at(i);
- std::string payment_id = (row->m_payment_id == cryptonote::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
+ std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id);
std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->testnet(),row->m_address);
// convert the zero padded short payment id to integrated address
if (payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) {
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 961bd772a..5105278e4 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -230,13 +230,13 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const
{
std::vector<string> result;
for (const auto &utx: m_unsigned_tx_set.txes) {
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
cryptonote::tx_extra_nonce extra_nonce;
std::vector<cryptonote::tx_extra_field> tx_extra_fields;
cryptonote::parse_tx_extra(utx.extra, tx_extra_fields);
if (cryptonote::find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- crypto::hash8 payment_id8 = cryptonote::null_hash8;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
// We can't decrypt short pid without recipient key.
@@ -244,10 +244,10 @@ std::vector<std::string> UnsignedTransactionImpl::paymentId() const
}
else if (!cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
- payment_id = cryptonote::null_hash;
+ payment_id = crypto::null_hash;
}
}
- if(payment_id != cryptonote::null_hash)
+ if(payment_id != crypto::null_hash)
result.push_back(epee::string_tools::pod_to_hex(payment_id));
else
result.push_back("");
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index c0974f880..9cd72b543 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -303,7 +303,7 @@ WalletImpl::~WalletImpl()
// Pause refresh thread - prevents refresh from starting again
pauseRefresh();
// Close wallet - stores cache and stops ongoing refresh operation
- close();
+ close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
stopRefresh();
delete m_wallet2Callback;
@@ -379,7 +379,32 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
const cryptonote::account_public_address address = m_wallet->get_account().get_keys().m_account_address;
try {
+ // Generate view only wallet
view_wallet->generate(path, password, address, viewkey);
+
+ // Export/Import outputs
+ auto outputs = m_wallet->export_outputs();
+ view_wallet->import_outputs(outputs);
+
+ // Copy scanned blockchain
+ auto bc = m_wallet->export_blockchain();
+ view_wallet->import_blockchain(bc);
+
+ // copy payments
+ auto payments = m_wallet->export_payments();
+ view_wallet->import_payments(payments);
+
+ // copy confirmed outgoing payments
+ std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> out_payments;
+ m_wallet->get_payments_out(out_payments, 0);
+ view_wallet->import_payments_out(out_payments);
+
+ // Export/Import key images
+ // We already know the spent status from the outputs we exported, thus no need to check them again
+ auto key_images = m_wallet->export_key_images();
+ uint64_t spent = 0;
+ uint64_t unspent = 0;
+ view_wallet->import_key_images(key_images,spent,unspent,false);
m_status = Status_Ok;
} catch (const std::exception &e) {
LOG_ERROR("Error creating view only wallet: " << e.what());
@@ -387,6 +412,8 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas
m_errorString = e.what();
return false;
}
+ // Store wallet
+ view_wallet->store();
return true;
}
@@ -539,19 +566,21 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
return m_status == Status_Ok;
}
-bool WalletImpl::close()
+bool WalletImpl::close(bool store)
{
bool result = false;
LOG_PRINT_L1("closing wallet...");
try {
- // Do not store wallet with invalid status
- // Status Critical refers to errors on opening or creating wallets.
- if (status() != Status_Critical)
- m_wallet->store();
- else
- LOG_ERROR("Status_Critical - not storing wallet");
- LOG_PRINT_L1("wallet::store done");
+ if (store) {
+ // Do not store wallet with invalid status
+ // Status Critical refers to errors on opening or creating wallets.
+ if (status() != Status_Critical)
+ m_wallet->store();
+ else
+ LOG_ERROR("Status_Critical - not storing wallet");
+ LOG_PRINT_L1("wallet::store done");
+ }
LOG_PRINT_L1("Calling wallet::stop...");
m_wallet->stop();
LOG_PRINT_L1("wallet::stop done");
@@ -862,6 +891,11 @@ bool WalletImpl::exportKeyImages(const string &filename)
bool WalletImpl::importKeyImages(const string &filename)
{
+ if (!trustedDaemon()) {
+ m_status = Status_Error;
+ m_errorString = tr("Key images can only be imported with a trusted daemon");
+ return false;
+ }
try
{
uint64_t spent = 0, unspent = 0;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 8190c7873..1e3d1e600 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -63,7 +63,7 @@ public:
const std::string &address_string,
const std::string &viewkey_string,
const std::string &spendkey_string = "");
- bool close();
+ bool close(bool store = true);
std::string seed() const;
std::string getSeedLanguage() const;
void setSeedLanguage(const std::string &arg);
@@ -153,7 +153,6 @@ private:
std::string m_password;
TransactionHistoryImpl * m_history;
bool m_trustedDaemon;
- WalletListener * m_walletListener;
Wallet2CallbackImpl * m_wallet2Callback;
AddressBookImpl * m_addressBook;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a23533530..897137d35 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -102,10 +102,12 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
return wallet;
}
-bool WalletManagerImpl::closeWallet(Wallet *wallet)
+bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
- bool result = wallet_->close();
+ if (!wallet_)
+ return false;
+ bool result = wallet_->close(store);
if (!result) {
m_errorString = wallet_->errorString();
} else {
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index aa6ea439e..8455f0f16 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -48,7 +48,7 @@ public:
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "");
- virtual bool closeWallet(Wallet *wallet);
+ virtual bool closeWallet(Wallet *wallet, bool store = true);
bool walletExists(const std::string &path);
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const;
std::vector<std::string> findWallets(const std::string &path);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 09ca8efe1..5e990d215 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -45,6 +45,7 @@ using namespace epee;
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
+#include "common/threadpool.h"
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
@@ -89,14 +90,6 @@ using namespace cryptonote;
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
-#define KILL_IOSERVICE() \
- do { \
- work.reset(); \
- while (!ioservice.stopped()) ioservice.poll(); \
- threadpool.join_all(); \
- ioservice.stop(); \
- } while(0)
-
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
namespace
@@ -296,6 +289,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
return false;
}
restore_deterministic_wallet = true;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
+ if (field_seed_passphrase_found)
+ {
+ if (!field_seed_passphrase.empty())
+ recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
+ }
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
@@ -383,7 +383,11 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
cryptonote::account_public_address address2;
bool has_payment_id;
crypto::hash8 new_payment_id;
- get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address);
+ if (!get_account_integrated_address_from_str(address2, has_payment_id, new_payment_id, testnet, field_address))
+ {
+ tools::fail_msg_writer() << tools::wallet2::tr("failed to parse address: ") << field_address;
+ return false;
+ }
address.m_spend_public_key = address2.m_spend_public_key;
}
wallet->generate(field_filename, field_password, address, viewkey);
@@ -509,6 +513,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit)
{
+ m_checkpoints.init_default_checkpoints(m_testnet);
if(m_http_client.is_connected())
m_http_client.disconnect();
m_is_initialized = true;
@@ -527,7 +532,7 @@ bool wallet2::is_deterministic() const
return keys_deterministic;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_seed(std::string& electrum_words) const
+bool wallet2::get_seed(std::string& electrum_words, const std::string &passphrase) const
{
bool keys_deterministic = is_deterministic();
if (!keys_deterministic)
@@ -541,7 +546,10 @@ bool wallet2::get_seed(std::string& electrum_words) const
return false;
}
- crypto::ElectrumWords::bytes_to_words(get_account().get_keys().m_spend_secret_key, electrum_words, seed_language);
+ 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);
return true;
}
@@ -584,24 +592,24 @@ void wallet2::set_unspent(size_t idx)
td.m_spent_height = 0;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
+void wallet2::check_acc_out_precomp(const crypto::public_key &spend_public_key, const tx_out &o, const crypto::key_derivation &derivation, size_t i, tx_scan_info_t &tx_scan_info) const
{
if (o.target.type() != typeid(txout_to_key))
{
- error = true;
+ tx_scan_info.error = true;
LOG_ERROR("wrong type id in transaction out");
return;
}
- received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i);
- if(received)
+ tx_scan_info.received = is_out_to_acc_precomp(spend_public_key, boost::get<txout_to_key>(o.target), derivation, i);
+ if(tx_scan_info.received)
{
- money_transfered = o.amount; // may be 0 for ringct outputs
+ tx_scan_info.money_transfered = o.amount; // may be 0 for ringct outputs
}
else
{
- money_transfered = 0;
+ tx_scan_info.money_transfered = 0;
}
- error = false;
+ tx_scan_info.error = false;
}
//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key &pub, const crypto::secret_key &sec, unsigned int i, rct::key & mask)
@@ -642,6 +650,22 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a
return true;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs)
+{
+ wallet_generate_key_image_helper(keys, tx_pub_key, i, tx_scan_info.in_ephemeral, tx_scan_info.ki);
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
+ outs.push_back(i);
+ if (tx_scan_info.money_transfered == 0)
+ {
+ tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, i, tx_scan_info.mask);
+ }
+ tx_money_got_in_outs += tx_scan_info.money_transfered;
+ tx_scan_info.amount = tx_scan_info.money_transfered;
+ ++num_vouts_received;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
// In this function, tx (probably) only contains the base information
@@ -680,11 +704,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
bool r = true;
- std::deque<cryptonote::keypair> in_ephemeral(tx.vout.size());
- std::deque<crypto::key_image> ki(tx.vout.size());
- std::deque<uint64_t> amount(tx.vout.size());
- std::deque<rct::key> mask(tx.vout.size());
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ std::unique_ptr<tx_scan_info_t[]> tx_scan_info{new tx_scan_info_t[tx.vout.size()]};
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
@@ -694,117 +716,64 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, received, money_transfered, error);
- if (error)
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[0], derivation, 0, tx_scan_info[0]);
+ if (tx_scan_info[0].error)
{
r = false;
}
else
{
// this assumes that the miner tx pays a single address
- if (received)
+ if (tx_scan_info[0].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, 0, in_ephemeral[0], ki[0]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(0);
- if (money_transfered == 0)
- {
- money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, 0, mask[0]);
- }
- amount[0] = money_transfered;
- tx_money_got_in_outs = money_transfered;
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, 0, tx_scan_info[0], num_vouts_received, tx_money_got_in_outs, outs);
// process the other outs from that tx
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
-
- std::vector<uint64_t> money_transfered(tx.vout.size());
- std::deque<bool> error(tx.vout.size());
- std::deque<bool> received(tx.vout.size());
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
- std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
+ std::ref(tx_scan_info[i])));
}
- KILL_IOSERVICE();
+ waiter.wait();
+
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- if (error[i])
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- if (received[i])
+ if (tx_scan_info[i].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered[i] == 0)
- {
- money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- tx_money_got_in_outs += money_transfered[i];
- amount[i] = money_transfered[i];
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
}
}
- else if (tx.vout.size() > 1 && threads > 1)
+ else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1)
{
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
- std::vector<uint64_t> money_transfered(tx.vout.size());
- std::deque<bool> error(tx.vout.size());
- std::deque<bool> received(tx.vout.size());
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
- std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key),
+ std::cref(tx.vout[i]), std::cref(derivation), i, std::ref(tx_scan_info[i])));
}
- KILL_IOSERVICE();
- tx_money_got_in_outs = 0;
+ waiter.wait();
+
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- if (error[i])
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- if (received[i])
+ if (tx_scan_info[i].received)
{
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered[i] == 0)
- {
- money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- tx_money_got_in_outs += money_transfered[i];
- amount[i] = money_transfered[i];
- ++num_vouts_received;
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -812,31 +781,15 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, received, money_transfered, error);
- if (error)
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, tx.vout[i], derivation, i, tx_scan_info[i]);
+ if (tx_scan_info[i].error)
{
r = false;
break;
}
- else
+ if (tx_scan_info[i].received)
{
- if (received)
- {
- wallet_generate_key_image_helper(keys, tx_pub_key, i, in_ephemeral[i], ki[i]);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- outs.push_back(i);
- if (money_transfered == 0)
- {
- money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
- }
- amount[i] = money_transfered;
- tx_money_got_in_outs += money_transfered;
- ++num_vouts_received;
- }
+ scan_output(keys, tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs);
}
}
}
@@ -858,7 +811,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
- auto kit = m_pub_keys.find(in_ephemeral[o].pub);
+ auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
error::wallet_internal_error, std::string("Unexpected transfer index from public key: ")
+ "got " + (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
@@ -874,14 +827,14 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
td.m_txid = txid;
- td.m_key_image = ki[o];
+ td.m_key_image = tx_scan_info[o].ki;
td.m_key_image_known = !m_watch_only;
td.m_amount = tx.vout[o].amount;
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
{
- td.m_mask = mask[o];
- td.m_amount = amount[o];
+ td.m_mask = tx_scan_info[o].mask;
+ td.m_amount = tx_scan_info[o].amount;
td.m_rct = true;
}
else if (miner_tx && tx.version == 2)
@@ -896,7 +849,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
set_unspent(m_transfers.size()-1);
m_key_images[td.m_key_image] = m_transfers.size()-1;
- m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1;
+ m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
m_callback->on_money_received(height, txid, tx, td.m_amount);
@@ -929,8 +882,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
{
- td.m_mask = mask[o];
- td.m_amount = amount[o];
+ td.m_mask = tx_scan_info[o].mask;
+ td.m_amount = tx_scan_info[o].amount;
td.m_rct = true;
}
else if (miner_tx && tx.version == 2)
@@ -943,7 +896,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
td.m_mask = rct::identity();
td.m_rct = false;
}
- THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys");
+ THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub, error::wallet_internal_error, "Inconsistent public keys");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
@@ -1145,16 +1098,19 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
{
size_t i = 0;
size_t current_multiplier = 1;
- size_t sz = m_blockchain.size();
+ size_t sz = m_blockchain.size() - m_blockchain.offset();
if(!sz)
+ {
+ ids.push_back(m_blockchain.genesis());
return;
+ }
size_t current_back_offset = 1;
- bool genesis_included = false;
+ bool base_included = false;
while(current_back_offset < sz)
{
- ids.push_back(m_blockchain[sz-current_back_offset]);
+ ids.push_back(m_blockchain[m_blockchain.offset() + sz-current_back_offset]);
if(sz-current_back_offset == 0)
- genesis_included = true;
+ base_included = true;
if(i < 10)
{
++current_back_offset;
@@ -1164,8 +1120,10 @@ void wallet2::get_short_chain_history(std::list<crypto::hash>& ids) const
}
++i;
}
- if(!genesis_included)
- ids.push_back(m_blockchain[0]);
+ if(!base_included)
+ ids.push_back(m_blockchain[m_blockchain.offset()]);
+ if(m_blockchain.offset())
+ ids.push_back(m_blockchain.genesis());
}
//----------------------------------------------------------------------------------------------------
void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const
@@ -1250,8 +1208,10 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
size_t tx_o_indices_idx = 0;
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
+ THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::wallet_internal_error, "Index out of bounds of hashchain");
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ int threads = tpool.get_max_concurrency();
if (threads > 1)
{
std::vector<crypto::hash> round_block_hashes(threads);
@@ -1262,23 +1222,16 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
for (size_t b = 0; b < blocks_size; b += threads)
{
size_t round_size = std::min((size_t)threads, blocks_size - b);
-
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (size_t i = 0; i < round_size; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool::waiter waiter;
std::list<block_complete_entry>::const_iterator tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
+ tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i])));
++tmpblocki;
}
- KILL_IOSERVICE();
+ waiter.wait();
tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
@@ -1552,23 +1505,22 @@ void wallet2::update_pool_state(bool refreshed)
{
if (res.txs.size() == txids.size())
{
- size_t n = 0;
- for (const auto &txid: txids)
+ for (const auto &tx_entry: res.txs)
{
- // might have just been put in a block
- if (res.txs[n].in_pool)
+ if (tx_entry.in_pool)
{
cryptonote::transaction tx;
cryptonote::blobdata bd;
crypto::hash tx_hash, tx_prefix_hash;
- if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd))
+ if (epee::string_tools::parse_hexstr_to_binbuff(tx_entry.as_hex, bd))
{
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- if (tx_hash == txid)
+ const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash);
+ if (i != txids.end())
{
- process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
- m_scanned_pool_txs[0].insert(txid);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
@@ -1577,7 +1529,7 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
+ MERROR("Got txid " << tx_hash << " which we did not ask for");
}
}
else
@@ -1587,14 +1539,13 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Failed to parse tx " << txid);
+ LOG_PRINT_L0("Failed to parse transaction from daemon");
}
}
else
{
- LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
+ LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
}
- ++n;
}
}
else
@@ -1698,7 +1649,8 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
size_t try_count = 0;
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
- boost::thread pull_thread;
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
@@ -1736,11 +1688,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
std::list<cryptonote::block_complete_entry> next_blocks;
std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
bool error = false;
- pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
+ tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
blocks_fetched += added_blocks;
- pull_thread.join();
+ waiter.wait();
if(blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
@@ -1762,8 +1714,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- if (pull_thread.joinable())
- pull_thread.join();
+ waiter.wait();
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
@@ -1810,6 +1761,13 @@ bool wallet2::refresh(uint64_t & blocks_fetched, bool& received_money, bool& ok)
void wallet2::detach_blockchain(uint64_t height)
{
LOG_PRINT_L0("Detaching blockchain on height " << height);
+
+ // size 1 2 3 4 5 6 7 8 9
+ // block 0 1 2 3 4 5 6 7 8
+ // C
+ THROW_WALLET_EXCEPTION_IF(height <= m_checkpoints.get_max_height() && m_blockchain.size() > m_checkpoints.get_max_height(),
+ error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
+
size_t transfers_detached = 0;
for (size_t i = 0; i < m_transfers.size(); ++i)
@@ -1840,8 +1798,8 @@ void wallet2::detach_blockchain(uint64_t height)
}
m_transfers.erase(it, m_transfers.end());
- size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height);
- m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end());
+ size_t blocks_detached = m_blockchain.size() - height;
+ m_blockchain.crop(height);
m_local_bc_height -= blocks_detached;
for (auto it = m_payments.begin(); it != m_payments.end(); )
@@ -2585,13 +2543,49 @@ void wallet2::load(const std::string& wallet_, const std::string& password)
check_genesis(genesis_hash);
}
+ trim_hashchain();
+
m_local_bc_height = m_blockchain.size();
}
//----------------------------------------------------------------------------------------------------
+void wallet2::trim_hashchain()
+{
+ uint64_t height = m_checkpoints.get_max_height();
+ if (!m_blockchain.empty() && m_blockchain.size() == m_blockchain.offset())
+ {
+ MINFO("Fixing empty hashchain");
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request> req = AUTO_VAL_INIT(req);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response, std::string> res = AUTO_VAL_INIT(res);
+ m_daemon_rpc_mutex.lock();
+ req.jsonrpc = "2.0";
+ req.id = epee::serialization::storage_entry(0);
+ req.method = "getblockheaderbyheight";
+ req.params.height = m_blockchain.size() - 1;
+ bool r = net_utils::invoke_http_json("/json_rpc", req, res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ if (r && res.result.status == CORE_RPC_STATUS_OK)
+ {
+ crypto::hash hash;
+ epee::string_tools::hex_to_pod(res.result.block_header.hash, hash);
+ m_blockchain.refill(hash);
+ }
+ else
+ {
+ MERROR("Failed to request block header from daemon, hash chain may be unable to sync till the wallet is loaded with a usable daemon");
+ }
+ }
+ if (height > 0 && m_blockchain.size() > height)
+ {
+ --height;
+ MDEBUG("trimming to " << height << ", offset " << m_blockchain.offset());
+ m_blockchain.trim(height);
+ }
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::check_genesis(const crypto::hash& genesis_hash) const {
std::string what("Genesis block mismatch. You probably use wallet without testnet flag with blockchain from test network or vice versa");
- THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain[0], error::wallet_internal_error, what);
+ THROW_WALLET_EXCEPTION_IF(genesis_hash != m_blockchain.genesis(), error::wallet_internal_error, what);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::path() const
@@ -2606,6 +2600,8 @@ void wallet2::store()
//----------------------------------------------------------------------------------------------------
void wallet2::store_to(const std::string &path, const std::string &password)
{
+ trim_hashchain();
+
// if file is the same, we do:
// 1. save wallet to the *.new file
// 2. remove old wallet file
@@ -2668,10 +2664,11 @@ void wallet2::store_to(const std::string &path, const std::string &password)
// if we here, main wallet file is saved and we only need to save keys and address files
if (!same_file) {
prepare_file_names(path);
- store_keys(m_keys_file, password, false);
+ bool r = store_keys(m_keys_file, password, false);
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
// save address to the new file
const std::string address_file = m_wallet_file + ".address.txt";
- bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet));
+ r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet));
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file);
// remove old wallet file
r = boost::filesystem::remove(old_file);
@@ -3113,7 +3110,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
{
std::vector<tx_extra_field> tx_extra_fields;
if(!parse_tx_extra(ptx.tx.extra, tx_extra_fields))
- return cryptonote::null_hash;
+ return crypto::null_hash;
tx_extra_nonce extra_nonce;
crypto::hash payment_id = null_hash;
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
@@ -3128,7 +3125,7 @@ crypto::hash wallet2::get_payment_id(const pending_tx &ptx) const
}
else if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
- payment_id = cryptonote::null_hash;
+ payment_id = crypto::null_hash;
}
}
return payment_id;
@@ -3177,7 +3174,7 @@ void wallet2::commit_tx(pending_tx& ptx)
}
txid = get_transaction_hash(ptx.tx);
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
std::vector<cryptonote::tx_destination_entry> dests;
uint64_t amount_in = 0;
if (store_tx_info())
@@ -3457,12 +3454,15 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
return true;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
+uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
{
static const uint64_t old_multipliers[3] = {1, 2, 3};
static const uint64_t new_multipliers[3] = {1, 20, 166};
static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
+ if (fee_algorithm == -1)
+ fee_algorithm = get_fee_algorithm();
+
// 0 -> default (here, x1 till fee algorithm 2, x4 from it)
if (priority == 0)
priority = m_default_priority;
@@ -5098,6 +5098,9 @@ uint64_t wallet2::get_approximate_blockchain_height() const
const int seconds_per_block = DIFFICULTY_TARGET_V2;
// Calculated blockchain height
uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block;
+ // testnet got some huge rollbacks, so the estimation is way off
+ if (m_testnet && approx_blockchain_height > 105000)
+ approx_blockchain_height -= 105000;
LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height);
return approx_blockchain_height;
}
@@ -5181,10 +5184,9 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
for (size_t i = 0; i < td.m_tx.vout.size(); ++i)
{
- uint64_t money_transfered = 0;
- bool error = false, received = false;
- check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, received, money_transfered, error);
- if (!error && received)
+ tx_scan_info_t tx_scan_info;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, td.m_tx.vout[i], derivation, i, tx_scan_info);
+ if (!tx_scan_info.error && tx_scan_info.received)
return tx_pub_key;
}
}
@@ -5192,7 +5194,7 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
// we found no key yielding an output
THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error,
"Public key yielding at least one output wasn't found in the transaction extra");
- return cryptonote::null_pkey;
+ return crypto::null_pkey;
}
bool wallet2::export_key_images(const std::string filename)
@@ -5330,7 +5332,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent)
+uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent)
{
COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
@@ -5379,34 +5381,236 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
m_transfers[n].m_key_image_known = true;
}
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
- "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
- std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
-
+ if(check_spent)
+ {
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error,
+ "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
+ std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size()));
+ for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
+ {
+ transfer_details &td = m_transfers[n];
+ td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
+ }
+ }
spent = 0;
unspent = 0;
- for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
+ std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
+ std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
+ // was created by sweep_all, so we can't know the spent height and other detailed info.
+ for(size_t i = 0; i < m_transfers.size(); ++i)
{
- transfer_details &td = m_transfers[n];
+ transfer_details &td = m_transfers[i];
uint64_t amount = td.amount();
- td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
if (td.m_spent)
spent += amount;
else
unspent += amount;
- LOG_PRINT_L2("Transfer " << n << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
- << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[n] << ")");
+ LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
+ << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
+
+ if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
+ {
+ bool is_spent_tx_found = false;
+ for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
+ {
+ bool is_spent_tx = false;
+ for(const cryptonote::txin_v& in : it->m_tx.vin)
+ {
+ if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
+ {
+ is_spent_tx = true;
+ break;
+ }
+ }
+ if (is_spent_tx)
+ {
+ is_spent_tx_found = true;
+ spent_txids.insert(it->m_txid);
+ break;
+ }
+ }
+
+ if (!is_spent_tx_found)
+ swept_transfers.push_back(i);
+ }
+ }
+ MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
+
+ if (check_spent)
+ {
+ // query outgoing txes
+ COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req;
+ COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
+ gettxs_req.decode_as_json = false;
+ for (const crypto::hash& spent_txid : spent_txids)
+ gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
+
+ // process each outgoing tx
+ auto spent_txid = spent_txids.begin();
+ for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
+ {
+ THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
+
+ // parse tx
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(e.as_hex, bd), error::wallet_internal_error, "parse_hexstr_to_binbuff failed");
+ cryptonote::transaction spent_tx;
+ crypto::hash spnet_txid_parsed, spent_txid_prefix;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, spent_tx, spnet_txid_parsed, spent_txid_prefix), error::wallet_internal_error, "parse_and_validate_tx_from_blob failed");
+ THROW_WALLET_EXCEPTION_IF(*spent_txid != spnet_txid_parsed, error::wallet_internal_error, "parsed txid mismatch");
+
+ // get received (change) amount
+ uint64_t tx_money_got_in_outs = 0;
+ 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);
+ size_t output_index = 0;
+ for (const cryptonote::tx_out& out : spent_tx.vout)
+ {
+ tx_scan_info_t tx_scan_info;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, tx_scan_info);
+ THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed");
+ if (tx_scan_info.received)
+ {
+ if (tx_scan_info.money_transfered == 0)
+ {
+ rct::key mask;
+ tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask);
+ }
+ tx_money_got_in_outs += tx_scan_info.money_transfered;
+ }
+ ++output_index;
+ }
+
+ // get spent amount
+ uint64_t tx_money_spent_in_ins = 0;
+ for (const cryptonote::txin_v& in : spent_tx.vin)
+ {
+ if (in.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
+ if (it != m_key_images.end())
+ {
+ const transfer_details& td = m_transfers[it->second];
+ uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
+ if (amount > 0)
+ {
+ THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
+ std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
+ std::string(", expected ") + print_money(td.amount()));
+ }
+ amount = td.amount();
+ tx_money_spent_in_ins += amount;
+
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << *spent_txid);
+ set_spent(it->second, e.block_height);
+ if (m_callback)
+ m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx);
+ }
+ }
+
+ // create outgoing payment
+ process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_money_spent_in_ins, tx_money_got_in_outs);
+
+ // erase corresponding incoming payment
+ for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
+ {
+ if (j->second.m_tx_hash == *spent_txid)
+ {
+ m_payments.erase(j);
+ break;
+ }
+ }
+
+ ++spent_txid;
+ }
+
+ for (size_t n : swept_transfers)
+ {
+ const transfer_details& td = m_transfers[n];
+ confirmed_transfer_details pd;
+ pd.m_change = (uint64_t)-1; // cahnge is unknown
+ pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
+ std::string err;
+ pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
+ crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
+ m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
+ }
}
- LOG_PRINT_L1("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
+wallet2::payment_container wallet2::export_payments() const
+{
+ payment_container payments;
+ for (auto const &p : m_payments)
+ {
+ payments.emplace(p);
+ }
+ return payments;
+}
+void wallet2::import_payments(const payment_container &payments)
+{
+ m_payments.clear();
+ for (auto const &p : payments)
+ {
+ m_payments.emplace(p);
+ }
+}
+void wallet2::import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments)
+{
+ m_confirmed_txs.clear();
+ for (auto const &p : confirmed_payments)
+ {
+ m_confirmed_txs.emplace(p);
+ }
+}
+
+std::tuple<size_t,crypto::hash,std::vector<crypto::hash>> wallet2::export_blockchain() const
+{
+ std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> bc;
+ std::get<0>(bc) = m_blockchain.offset();
+ std::get<1>(bc) = m_blockchain.empty() ? crypto::null_hash: m_blockchain.genesis();
+ for (size_t n = m_blockchain.offset(); n < m_blockchain.size(); ++n)
+ {
+ std::get<2>(bc).push_back(m_blockchain[n]);
+ }
+ return bc;
+}
+
+void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc)
+{
+ m_blockchain.clear();
+ if (std::get<0>(bc))
+ {
+ for (size_t n = std::get<0>(bc); n > 0; ++n)
+ m_blockchain.push_back(std::get<1>(bc));
+ m_blockchain.trim(std::get<0>(bc));
+ }
+ for (auto const &b : std::get<2>(bc))
+ {
+ m_blockchain.push_back(b);
+ }
+ cryptonote::block genesis;
+ generate_genesis(genesis);
+ crypto::hash genesis_hash = get_block_hash(genesis);
+ check_genesis(genesis_hash);
+ m_local_bc_height = m_blockchain.size();
+}
//----------------------------------------------------------------------------------------------------
std::vector<tools::wallet2::transfer_details> wallet2::export_outputs() const
{
@@ -5499,7 +5703,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
crypto::secret_key_to_public_key(skey, pkey);
const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
- error::wallet_internal_error, "Failed to authenticate criphertext");
+ error::wallet_internal_error, "Failed to authenticate ciphertext");
}
crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
return plaintext;
@@ -5747,10 +5951,14 @@ bool wallet2::is_synced() const
return get_blockchain_current_height() >= height;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::estimate_backlog(uint64_t blob_size, uint64_t fee)
+std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees)
{
- THROW_WALLET_EXCEPTION_IF(blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
- THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee");
+ for (uint64_t fee: fees)
+ {
+ THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
+ }
// get txpool backlog
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request> req = AUTO_VAL_INIT(req);
@@ -5776,27 +5984,35 @@ uint64_t wallet2::estimate_backlog(uint64_t blob_size, uint64_t fee)
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_info");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_info");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+ uint64_t full_reward_zone = resp_t.result.block_size_limit / 2;
- double our_fee_byte = fee / (double)blob_size;
- uint64_t priority_size = 0;
- for (const auto &i: res.result.backlog)
+ std::vector<std::pair<uint64_t, uint64_t>> blocks;
+ for (uint64_t fee: fees)
{
- if (i.blob_size == 0)
+ double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size;
+ uint64_t priority_size_min = 0, priority_size_max = 0;
+ for (const auto &i: res.result.backlog)
{
- MWARNING("Got 0 sized blob from txpool, ignored");
- continue;
+ if (i.blob_size == 0)
+ {
+ MWARNING("Got 0 sized blob from txpool, ignored");
+ continue;
+ }
+ double this_fee_byte = i.fee / (double)i.blob_size;
+ if (this_fee_byte >= our_fee_byte_min)
+ priority_size_min += i.blob_size;
+ if (this_fee_byte >= our_fee_byte_max)
+ priority_size_max += i.blob_size;
}
- double this_fee_byte = i.fee / (double)i.blob_size;
- if (this_fee_byte < our_fee_byte)
- continue;
- priority_size += i.blob_size;
- }
- uint64_t full_reward_zone = resp_t.result.block_size_limit / 2;
- uint64_t nblocks = (priority_size + full_reward_zone - 1) / full_reward_zone;
- MDEBUG("estimate_backlog: priority_size " << priority_size << " for " << our_fee_byte << " (" << our_fee_byte << " piconero fee/byte), "
- << nblocks << " blocks at block size " << full_reward_zone);
- return nblocks;
+ uint64_t nblocks_min = (priority_size_min + full_reward_zone - 1) / full_reward_zone;
+ uint64_t nblocks_max = (priority_size_max + full_reward_zone - 1) / full_reward_zone;
+ MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for " << fee
+ << " (" << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee), "
+ << nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone);
+ blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
+ }
+ return blocks;
}
//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) {
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f30c97635..1dff14a95 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -36,6 +36,7 @@
#include <boost/program_options/variables_map.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
+#include <boost/serialization/deque.hpp>
#include <atomic>
#include "include_base_utils.h"
@@ -52,6 +53,7 @@
#include "crypto/hash.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
+#include "checkpoints/checkpoints.h"
#include "wallet_errors.h"
#include "common/password.h"
@@ -91,6 +93,38 @@ namespace tools
}
};
+ class hashchain
+ {
+ public:
+ hashchain(): m_genesis(crypto::null_hash), m_offset(0) {}
+
+ size_t size() const { return m_blockchain.size() + m_offset; }
+ size_t offset() const { return m_offset; }
+ const crypto::hash &genesis() const { return m_genesis; }
+ void push_back(const crypto::hash &hash) { if (m_offset == 0 && m_blockchain.empty()) m_genesis = hash; m_blockchain.push_back(hash); }
+ bool is_in_bounds(size_t idx) const { return idx >= m_offset && idx < size(); }
+ const crypto::hash &operator[](size_t idx) const { return m_blockchain[idx - m_offset]; }
+ crypto::hash &operator[](size_t idx) { return m_blockchain[idx - m_offset]; }
+ void crop(size_t height) { m_blockchain.resize(height - m_offset); }
+ void clear() { m_offset = 0; m_blockchain.clear(); }
+ bool empty() const { return m_blockchain.empty() && m_offset == 0; }
+ void trim(size_t height) { while (height > m_offset+1 && m_blockchain.size() > 1) { m_blockchain.pop_front(); ++m_offset; } m_blockchain.shrink_to_fit(); }
+ void refill(const crypto::hash &hash) { m_blockchain.push_back(hash); --m_offset; }
+
+ template <class t_archive>
+ inline void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & m_offset;
+ a & m_genesis;
+ a & m_blockchain;
+ }
+
+ private:
+ size_t m_offset;
+ crypto::hash m_genesis;
+ std::deque<crypto::hash> m_blockchain;
+ };
+
class wallet2
{
friend class ::Serialization_portability_wallet_Test;
@@ -133,6 +167,19 @@ namespace tools
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_confirm_backlog(true), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ struct tx_scan_info_t
+ {
+ cryptonote::keypair in_ephemeral;
+ crypto::key_image ki;
+ rct::key mask;
+ uint64_t amount;
+ uint64_t money_transfered;
+ bool error;
+ bool received;
+
+ tx_scan_info_t(): money_transfered(0), error(true), received(false) {}
+ };
+
struct transfer_details
{
uint64_t m_block_height;
@@ -203,7 +250,7 @@ namespace tools
uint64_t m_timestamp;
uint64_t m_unlock_time;
- confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash), m_timestamp(0), m_unlock_time(0) {}
+ confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0) {}
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time) {}
};
@@ -363,7 +410,7 @@ namespace tools
* \brief Checks if deterministic wallet
*/
bool is_deterministic() const;
- bool get_seed(std::string& electrum_words) const;
+ bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const;
/*!
* \brief Gets the seed language
*/
@@ -439,7 +486,19 @@ namespace tools
uint64_t dummy_refresh_height = 0; // moved to keys file
if(ver < 5)
return;
- a & m_blockchain;
+ if (ver < 19)
+ {
+ std::vector<crypto::hash> blockchain;
+ a & blockchain;
+ for (const auto &b: blockchain)
+ {
+ m_blockchain.push_back(b);
+ }
+ }
+ else
+ {
+ a & m_blockchain;
+ }
a & m_transfers;
a & m_account_public_address;
a & m_key_images;
@@ -582,12 +641,17 @@ namespace tools
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;
+ // Import/Export wallet data
std::vector<tools::wallet2::transfer_details> export_outputs() const;
size_t import_outputs(const std::vector<tools::wallet2::transfer_details> &outputs);
-
+ payment_container export_payments() const;
+ void import_payments(const payment_container &payments);
+ void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
+ std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
+ void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
bool export_key_images(const std::string filename);
std::vector<std::pair<crypto::key_image, crypto::signature>> export_key_images() const;
- uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent);
+ uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
void update_pool_state(bool refreshed = false);
@@ -604,7 +668,10 @@ namespace tools
bool is_synced() const;
- uint64_t estimate_backlog(uint64_t blob_size, uint64_t fee);
+ std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees);
+
+ uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
+ uint64_t get_per_kb_fee();
private:
/*!
@@ -642,13 +709,11 @@ namespace tools
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
crypto::hash8 get_short_payment_id(const pending_tx &ptx) const;
- void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
+ void check_acc_out_precomp(const crypto::public_key &spend_public_key, const cryptonote::tx_out &o, const crypto::key_derivation &derivation, 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;
uint64_t get_upper_transaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
- uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm) const;
uint64_t get_dynamic_per_kb_fee_estimate();
- uint64_t get_per_kb_fee();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money) const;
void set_spent(size_t idx, uint64_t height);
@@ -658,6 +723,8 @@ namespace tools
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
+ void scan_output(const cryptonote::account_keys &keys, const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, uint64_t &tx_money_got_in_outs, std::vector<size_t> &outs);
+ void trim_hashchain();
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
@@ -665,12 +732,13 @@ namespace tools
std::string m_wallet_file;
std::string m_keys_file;
epee::net_utils::http::http_simple_client m_http_client;
- std::vector<crypto::hash> m_blockchain;
+ hashchain m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
+ cryptonote::checkpoints m_checkpoints;
transfer_container m_transfers;
payment_container m_payments;
@@ -710,7 +778,7 @@ namespace tools
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 18)
+BOOST_CLASS_VERSION(tools::wallet2, 19)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 8da8c62eb..7a5e01af7 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -663,7 +663,7 @@ struct WalletManager
* \param wallet previously opened / created wallet instance
* \return None
*/
- virtual bool closeWallet(Wallet *wallet) = 0;
+ virtual bool closeWallet(Wallet *wallet, bool store = true) = 0;
/*
* ! checks if wallet with the given name already exists
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 34c5a2a5d..22cfcc269 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -84,6 +84,7 @@ namespace wallet_args
#endif
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
+ const command_line::arg_descriptor<std::size_t> arg_max_log_file_size = {"max-log-file-size", "Specify maximum log file size [B]", MAX_LOG_FILE_SIZE};
const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY};
const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""};
const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true};
@@ -99,8 +100,9 @@ namespace wallet_args
command_line::add_arg(desc_general, command_line::arg_help);
command_line::add_arg(desc_general, command_line::arg_version);
- command_line::add_arg(desc_params, arg_log_file, "");
+ command_line::add_arg(desc_params, arg_log_file);
command_line::add_arg(desc_params, arg_log_level);
+ command_line::add_arg(desc_params, arg_max_log_file_size);
command_line::add_arg(desc_params, arg_max_concurrency);
command_line::add_arg(desc_params, arg_config_file);
@@ -114,6 +116,21 @@ namespace wallet_args
auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options);
po::store(parser.run(), vm);
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+ tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
+ "daemon to work correctly.") << ENDL;
+ tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
+ tools::msg_writer() << desc_all;
+ return false;
+ }
+ else if (command_line::get_arg(vm, command_line::arg_version))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ return false;
+ }
+
if(command_line::has_arg(vm, arg_config_file))
{
std::string config = command_line::get_arg(vm, arg_config_file);
@@ -141,27 +158,12 @@ namespace wallet_args
log_path = command_line::get_arg(vm, arg_log_file);
else
log_path = mlog_get_default_log_path(default_log_name);
- mlog_configure(log_path, log_to_console);
+ mlog_configure(log_path, log_to_console, command_line::get_arg(vm, arg_max_log_file_size));
if (!vm["log-level"].defaulted())
{
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
- tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
- "daemon to work correctly.") << ENDL;
- tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
- tools::msg_writer() << desc_all;
- return boost::none;
- }
- else if (command_line::get_arg(vm, command_line::arg_version))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- return boost::none;
- }
-
if(command_line::has_arg(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 773d12775..46b092376 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <boost/format.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
#include <cstdint>
@@ -37,7 +38,6 @@ using namespace epee;
#include "wallet/wallet_args.h"
#include "common/command_line.h"
#include "common/i18n.h"
-#include "common/util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "wallet_rpc_server_commands_defs.h"
@@ -70,18 +70,12 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false)
+ wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false), m_vm(NULL)
{
}
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
- try
- {
- boost::system::error_code ec{};
- boost::filesystem::remove(rpc_login_filename, ec);
- }
- catch (...) {}
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -160,7 +154,15 @@ namespace tools
#else
#define MKDIR(path, mode) mkdir(path, mode)
#endif
- MKDIR(m_wallet_dir.c_str(), 0700);
+ if (MKDIR(m_wallet_dir.c_str(), 0700) < 0)
+ {
+#ifdef _WIN32
+ LOG_ERROR(tr("Failed to create directory ") + m_wallet_dir);
+#else
+ LOG_ERROR((boost::format(tr("Failed to create directory %s: %s")) % m_wallet_dir % strerror(errno)).str());
+#endif
+ return false;
+ }
}
if (disable_auth)
@@ -182,34 +184,32 @@ namespace tools
default_rpc_username,
string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
);
+
+ std::string temp = "monero-wallet-rpc." + bind_port + ".login";
+ rpc_login_file = tools::private_file::create(temp);
+ if (!rpc_login_file.handle())
+ {
+ LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
+ return false;
+ }
+ std::fputs(http_login->username.c_str(), rpc_login_file.handle());
+ std::fputc(':', rpc_login_file.handle());
+ std::fputs(http_login->password.c_str(), rpc_login_file.handle());
+ std::fflush(rpc_login_file.handle());
+ if (std::ferror(rpc_login_file.handle()))
+ {
+ LOG_ERROR(tr("Error writing to file ") << temp);
+ return false;
+ }
+ LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
}
- else
+ else // chosen user/pass
{
http_login.emplace(
std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
);
}
assert(bool(http_login));
-
- std::string temp = "monero-wallet-rpc." + bind_port + ".login";
- const auto cookie = tools::create_private_file(temp);
- if (!cookie)
- {
- LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
- return false;
- }
- rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup
- temp = rpc_login_filename;
- std::fputs(http_login->username.c_str(), cookie.get());
- std::fputc(':', cookie.get());
- std::fputs(http_login->password.c_str(), cookie.get());
- std::fflush(cookie.get());
- if (std::ferror(cookie.get()))
- {
- LOG_ERROR(tr("Error writing to file ") << temp);
- return false;
- }
- LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
} // end auth enabled
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
@@ -362,7 +362,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er)
{
- crypto::hash8 integrated_payment_id = cryptonote::null_hash8;
+ crypto::hash8 integrated_payment_id = crypto::null_hash8;
std::string extra_nonce;
for (auto it = destinations.begin(); it != destinations.end(); it++)
{
@@ -395,7 +395,7 @@ namespace tools
if (has_payment_id)
{
- if (!payment_id.empty() || integrated_payment_id != cryptonote::null_hash8)
+ if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "A single payment id is allowed per transaction";
@@ -1485,7 +1485,7 @@ namespace tools
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id8;
- crypto::hash payment_id = cryptonote::null_hash;
+ crypto::hash payment_id = crypto::null_hash;
er.message = "";
if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
@@ -1545,7 +1545,7 @@ namespace tools
er.message = "Failed to add address book entry";
return false;
}
- res.index = m_wallet->get_address_book().size();
+ res.index = m_wallet->get_address_book().size() - 1;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1896,7 +1896,15 @@ just_dir:
wrpc.send_stop_signal();
});
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet rpc server"));
- wrpc.run();
+ try
+ {
+ wrpc.run();
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
+ return 1;
+ }
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet rpc server"));
try
{
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index dd54222b0..e5ed0a846 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -33,6 +33,7 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <string>
+#include "common/util.h"
#include "net/http_server_impl_base.h"
#include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h"
@@ -154,7 +155,7 @@ namespace tools
wallet2 *m_wallet;
std::string m_wallet_dir;
- std::string rpc_login_filename;
+ tools::private_file rpc_login_file;
std::atomic<bool> m_stop;
bool m_trusted_daemon;
epee::net_utils::http::http_simple_client m_http_client;