diff options
Diffstat (limited to 'src')
54 files changed, 1532 insertions, 1594 deletions
diff --git a/src/cryptonote_basic/cryptonote_stat_info.h b/src/blockchain_db/locked_txn.h index 4cc1bc764..d14631ddb 100644 --- a/src/cryptonote_basic/cryptonote_stat_info.h +++ b/src/blockchain_db/locked_txn.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-2019, 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 @@ -25,30 +25,29 @@ // 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. -// +// // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include "serialization/keyvalue_serialization.h" - namespace cryptonote { - struct core_stat_info_t - { - uint64_t tx_pool_size; - uint64_t blockchain_height; - uint64_t mining_speed; - uint64_t alternative_blocks; - std::string top_block_id_str; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_pool_size) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(mining_speed) - KV_SERIALIZE(alternative_blocks) - KV_SERIALIZE(top_block_id_str) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<core_stat_info_t> core_stat_info; + // This class is meant to create a batch when none currently exists. + // If a batch exists, it can't be from another thread, since we can + // only be called with the txpool lock taken, and it is held during + // the whole prepare/handle/cleanup incoming block sequence. + class LockedTXN { + public: + LockedTXN(BlockchainDB &db): m_db(db), m_batch(false), m_active(false) { + m_batch = m_db.batch_start(); + m_active = true; + } + void commit() { try { if (m_batch && m_active) { m_db.batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } } + void abort() { try { if (m_batch && m_active) { m_db.batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } } + ~LockedTXN() { abort(); } + private: + BlockchainDB &m_db; + bool m_batch; + bool m_active; + }; } diff --git a/src/common/util.cpp b/src/common/util.cpp index 57e747837..747235646 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1239,7 +1239,7 @@ std::string get_nix_version_display_string() return get_string_prefix_by_width(s, 999999999).second; }; - std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns) + std::vector<std::pair<std::string, size_t>> split_line_by_width(const std::string &s, size_t columns) { std::vector<std::string> words; std::vector<std::pair<std::string, size_t>> lines; @@ -1279,4 +1279,17 @@ std::string get_nix_version_display_string() return lines; } + std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns) + { + std::vector<std::string> lines; + std::vector<std::pair<std::string, size_t>> all_lines; + boost::split(lines, s, boost::is_any_of("\n"), boost::token_compress_on); + for (const auto &e: lines) + { + std::vector<std::pair<std::string, size_t>> new_lines = split_line_by_width(e, columns); + for (auto &l: new_lines) + all_lines.push_back(std::move(l)); + } + return all_lines; + } } diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index a7a459ad3..606c67336 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -62,6 +62,7 @@ static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT; static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}}; static randomx_dataset *rx_dataset; +static int rx_dataset_nomem; static uint64_t rx_dataset_height; static THREADV randomx_vm *rx_vm = NULL; @@ -246,20 +247,25 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch } if (miners) { CTHR_MUTEX_LOCK(rx_dataset_mutex); - if (rx_dataset == NULL) { - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); + if (!rx_dataset_nomem) { if (rx_dataset == NULL) { - mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); - rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); + if (rx_dataset == NULL) { + mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset"); + rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); + } + if (rx_dataset != NULL) + rx_initdata(rx_sp->rs_cache, miners, seedheight); } - if (rx_dataset != NULL) - rx_initdata(rx_sp->rs_cache, miners, seedheight); } if (rx_dataset != NULL) flags |= RANDOMX_FLAG_FULL_MEM; else { miners = 0; - mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner"); + if (!rx_dataset_nomem) { + rx_dataset_nomem = 1; + mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner"); + } } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } @@ -278,6 +284,10 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch CTHR_MUTEX_LOCK(rx_dataset_mutex); if (rx_dataset != NULL && rx_dataset_height != seedheight) rx_initdata(cache, miners, seedheight); + else if (rx_dataset == NULL) { + /* this is a no-op if the cache hasn't changed */ + randomx_vm_set_cache(rx_vm, rx_sp->rs_cache); + } CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } else { /* this is a no-op if the cache hasn't changed */ @@ -309,5 +319,6 @@ void rx_stop_mining(void) { rx_dataset = NULL; randomx_release_dataset(rd); } + rx_dataset_nomem = 0; CTHR_MUTEX_UNLOCK(rx_dataset_mutex); } diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 5bb56e083..59040d8a2 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -54,7 +54,6 @@ set(cryptonote_basic_private_headers cryptonote_basic_impl.h cryptonote_boost_serialization.h cryptonote_format_utils.h - cryptonote_stat_info.h difficulty.h hardfork.h miner.h diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 138cf49f4..651d61b06 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -996,17 +996,31 @@ namespace cryptonote } } //--------------------------------------------------------------- - std::string print_money(uint64_t amount, unsigned int decimal_point) + static void insert_money_decimal_point(std::string &s, unsigned int decimal_point) { if (decimal_point == (unsigned int)-1) decimal_point = default_decimal_point; - std::string s = std::to_string(amount); if(s.size() < decimal_point+1) { s.insert(0, decimal_point+1 - s.size(), '0'); } if (decimal_point > 0) s.insert(s.size() - decimal_point, "."); + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount, unsigned int decimal_point) + { + std::string s = std::to_string(amount); + insert_money_decimal_point(s, decimal_point); + return s; + } + //--------------------------------------------------------------- + std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point) + { + std::stringstream ss; + ss << amount; + std::string s = ss.str(); + insert_money_decimal_point(s, decimal_point); return s; } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 29e4def64..8ed3b0b43 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -38,6 +38,7 @@ #include "crypto/crypto.h" #include "crypto/hash.h" #include <unordered_map> +#include <boost/multiprecision/cpp_int.hpp> namespace epee { @@ -139,6 +140,7 @@ namespace cryptonote unsigned int get_default_decimal_point(); std::string get_unit(unsigned int decimal_point = -1); std::string print_money(uint64_t amount, unsigned int decimal_point = -1); + std::string print_money(const boost::multiprecision::uint128_t &amount, unsigned int decimal_point = -1); //--------------------------------------------------------------- template<class t_object> bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 134b630f7..4598baad7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -144,8 +144,6 @@ #define RPC_IP_FAILS_BEFORE_BLOCK 3 -#define ALLOW_DEBUG_COMMANDS - #define CRYPTONOTE_NAME "bitmonero" #define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" #define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" @@ -193,7 +191,6 @@ namespace config uint8_t const FEE_CALCULATION_MAX_RETRIES = 10; uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9) uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8) - std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 78893fdf2..5578f519e 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2541,6 +2541,13 @@ bool Blockchain::add_block_as_invalid(const block_extended_info& bei, const cryp return true; } //------------------------------------------------------------------ +void Blockchain::flush_invalid_blocks() +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_invalid_blocks.clear(); +} +//------------------------------------------------------------------ bool Blockchain::have_block(const crypto::hash& id) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -3416,7 +3423,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const if (version >= HF_VERSION_PER_BYTE_FEE) { const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median, version); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals @@ -3474,7 +3481,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0; uint64_t base_reward; - if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version)) { MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound"); base_reward = BLOCK_REWARD_OVERESTIMATE; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 0aecdcb57..4ed7a2f02 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1017,6 +1017,11 @@ namespace cryptonote */ bool has_block_weights(uint64_t height, uint64_t nblocks) const; + /** + * @brief flush the invalid blocks set + */ + void flush_invalid_blocks(); + #ifndef IN_UNIT_TESTS private: #endif diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 5c8f59291..212616af8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1056,17 +1056,6 @@ namespace cryptonote return handle_incoming_txs({std::addressof(tx_blob), 1}, {std::addressof(tvc), 1}, tx_relay, relayed); } //----------------------------------------------------------------------------------------------- - bool core::get_stat_info(core_stat_info& st_inf) const - { - st_inf.mining_speed = m_miner.get_speed(); - st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); - st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - st_inf.tx_pool_size = m_mempool.get_transactions_count(); - st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id()); - return true; - } - - //----------------------------------------------------------------------------------------------- bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const { if(!tx.vin.size()) @@ -1175,10 +1164,10 @@ namespace cryptonote 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) + std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count) { - uint64_t emission_amount = 0; - uint64_t total_fee_amount = 0; + boost::multiprecision::uint128_t emission_amount = 0; + boost::multiprecision::uint128_t total_fee_amount = 0; if (count) { const uint64_t end = start_offset + count - 1; @@ -1200,7 +1189,7 @@ namespace cryptonote }); } - return std::pair<uint64_t, uint64_t>(emission_amount, total_fee_amount); + return std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t>(emission_amount, total_fee_amount); } //----------------------------------------------------------------------------------------------- bool core::check_tx_inputs_keyimages_diff(const transaction& tx) const @@ -1942,6 +1931,16 @@ namespace cryptonote bad_semantics_txes_lock.unlock(); } //----------------------------------------------------------------------------------------------- + void core::flush_invalid_blocks() + { + m_blockchain_storage.flush_invalid_blocks(); + } + //----------------------------------------------------------------------------------------------- + bool core::get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) + { + return m_mempool.get_complement(hashes, txes); + } + //----------------------------------------------------------------------------------------------- bool core::update_blockchain_pruning() { return m_blockchain_storage.update_blockchain_pruning(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 55efb566c..79a846de1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -45,7 +45,6 @@ #include "blockchain.h" #include "cryptonote_basic/miner.h" #include "cryptonote_basic/connection_context.h" -#include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" #include "span.h" @@ -556,15 +555,6 @@ namespace cryptonote bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; /** - * @brief gets some stats about the daemon - * - * @param st_inf return-by-reference container for the stats requested - * - * @return true - */ - bool get_stat_info(core_stat_info& st_inf) const; - - /** * @copydoc Blockchain::get_tx_outputs_gindexs * * @note see Blockchain::get_tx_outputs_gindexs @@ -765,7 +755,7 @@ namespace cryptonote * * @return the number of blocks to sync in one go */ - std::pair<uint64_t, uint64_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); + std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); /** * @brief get the network type we're on @@ -859,6 +849,20 @@ namespace cryptonote */ void flush_bad_txs_cache(); + /** + * @brief flushes the invalid block cache + */ + void flush_invalid_blocks(); + + /** + * @brief returns the set of transactions in the txpool which are not in the argument + * + * @param hashes hashes of transactions to exclude from the result + * + * @return true iff success, false otherwise + */ + bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes); + private: /** diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 13320e69f..53a6ce579 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -38,6 +38,7 @@ #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "blockchain.h" +#include "blockchain_db/locked_txn.h" #include "blockchain_db/blockchain_db.h" #include "common/boost_serialization_helper.h" #include "int-util.h" @@ -88,25 +89,6 @@ namespace cryptonote else return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } - - // This class is meant to create a batch when none currently exists. - // If a batch exists, it can't be from another thread, since we can - // only be called with the txpool lock taken, and it is held during - // the whole prepare/handle/cleanup incoming block sequence. - class LockedTXN { - public: - LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) { - m_batch = m_blockchain.get_db().batch_start(); - m_active = true; - } - void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } } - void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } } - ~LockedTXN() { abort(); } - private: - Blockchain &m_blockchain; - bool m_batch; - bool m_active; - }; } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- @@ -256,7 +238,7 @@ namespace cryptonote if (kept_by_block) m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); if (!insert_key_images(tx, id, tx_relay)) return false; @@ -301,7 +283,7 @@ namespace cryptonote if (kept_by_block) m_parsed_tx_cache.insert(std::make_pair(id, tx)); CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); m_blockchain.remove_txpool_tx(id); if (!insert_key_images(tx, id, tx_relay)) return false; @@ -362,7 +344,7 @@ namespace cryptonote if (bytes == 0) bytes = m_txpool_max_weight; CRITICAL_REGION_LOCAL1(m_blockchain); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); bool changed = false; // this will never remove the first one, but we don't care @@ -494,7 +476,7 @@ namespace cryptonote try { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(id, meta)) { @@ -549,7 +531,7 @@ namespace cryptonote try { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); txpool_tx_meta_t meta; if (!m_blockchain.get_txpool_tx_meta(txid, meta)) { @@ -594,6 +576,39 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + + m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { + const auto relay_method = meta.get_relay_method(); + if (relay_method != relay_method::block && relay_method != relay_method::fluff) + return true; + const auto i = std::find(hashes.begin(), hashes.end(), txid); + if (i == hashes.end()) + { + cryptonote::blobdata bd; + try + { + if (!m_blockchain.get_txpool_tx_blob(txid, bd, cryptonote::relay_category::broadcasted)) + { + MERROR("Failed to get blob for txpool transaction " << txid); + return true; + } + txes.emplace_back(std::move(bd)); + } + catch (const std::exception &e) + { + MERROR("Failed to get blob for txpool transaction " << txid << ": " << e.what()); + return true; + } + } + return true; + }, false); + return true; + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();}); @@ -638,7 +653,7 @@ namespace cryptonote if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const std::pair<crypto::hash, uint64_t> &entry: remove) { const crypto::hash &txid = entry.first; @@ -709,7 +724,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); const time_t now = time(NULL); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const auto& hash : hashes) { try @@ -1186,7 +1201,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); bool changed = false; - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for(size_t i = 0; i!= tx.vin.size(); i++) { CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void()); @@ -1282,7 +1297,7 @@ namespace cryptonote LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); auto sorted_it = m_txs_by_fee_and_receive_time.begin(); for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) @@ -1419,7 +1434,7 @@ namespace cryptonote size_t n_removed = 0; if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const crypto::hash &txid: remove) { try @@ -1499,7 +1514,7 @@ namespace cryptonote } if (!remove.empty()) { - LockedTXN lock(m_blockchain); + LockedTXN lock(m_blockchain.get_db()); for (const auto &txid: remove) { try diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index f716440ad..ca0e50415 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -441,6 +441,11 @@ namespace cryptonote */ bool get_transaction_info(const crypto::hash &txid, tx_details &td) const; + /** + * @brief get transactions not in the passed set + */ + bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const; + private: /** diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index 03cbb5c26..e99982def 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -28,7 +28,7 @@ #include <stdint.h> #include <vector> -#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "blockchain.h" #include "tx_sanity_check.h" @@ -39,7 +39,7 @@ namespace cryptonote { -bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob) +bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available) { cryptonote::transaction tx; @@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob n_indices += in_to_key.key_offsets.size(); } + return tx_sanity_check(rct_indices, n_indices, rct_outs_available); +} + +bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available) +{ if (n_indices <= 10) { MDEBUG("n_indices is only " << n_indices << ", not checking"); return true; } - uint64_t n_available = blockchain.get_num_mature_outputs(0); - if (n_available < 10000) + if (rct_outs_available < 10000) return true; if (rct_indices.size() < n_indices * 8 / 10) @@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end()); uint64_t median = epee::misc_utils::median(offsets); - if (median < n_available * 6 / 10) + if (median < rct_outs_available * 6 / 10) { - MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs."); + MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs."); return false; } diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h index c12d1b0b1..4a469462f 100644 --- a/src/cryptonote_core/tx_sanity_check.h +++ b/src/cryptonote_core/tx_sanity_check.h @@ -26,11 +26,11 @@ // 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 <set> #include "cryptonote_basic/blobdatatype.h" namespace cryptonote { - class Blockchain; - - bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob); + bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available); + bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available); } diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 201001c8e..f809bff74 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -257,7 +257,10 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(current_height) KV_SERIALIZE(cumulative_difficulty) - KV_SERIALIZE(cumulative_difficulty_top64) + if (is_store) + KV_SERIALIZE(cumulative_difficulty_top64) + else + KV_SERIALIZE_OPT(cumulative_difficulty_top64, (uint64_t)0) KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) KV_SERIALIZE_OPT(top_version, (uint8_t)0) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) @@ -298,7 +301,10 @@ namespace cryptonote KV_SERIALIZE(start_height) KV_SERIALIZE(total_height) KV_SERIALIZE(cumulative_difficulty) - KV_SERIALIZE(cumulative_difficulty_top64) + if (is_store) + KV_SERIALIZE(cumulative_difficulty_top64) + else + KV_SERIALIZE_OPT(cumulative_difficulty_top64, (uint64_t)0) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_weights) END_KV_SERIALIZE_MAP() @@ -347,5 +353,23 @@ namespace cryptonote }; typedef epee::misc_utils::struct_init<request_t> request; }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_GET_TXPOOL_COMPLEMENT + { + const static int ID = BC_COMMANDS_POOL_BASE + 10; + + struct request_t + { + std::vector<crypto::hash> hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(hashes) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index ddbd45a61..2664716a8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -45,7 +45,6 @@ #include "block_queue.h" #include "common/perf_timer.h" #include "cryptonote_basic/connection_context.h" -#include "cryptonote_basic/cryptonote_stat_info.h" #include <boost/circular_buffer.hpp> PUSH_WARNINGS @@ -77,7 +76,6 @@ namespace cryptonote { public: typedef cryptonote_connection_context connection_context; - typedef core_stat_info stat_info; typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler; typedef CORE_SYNC_DATA payload_type; @@ -92,6 +90,7 @@ namespace cryptonote HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block) HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx) + HANDLE_NOTIFY_T2(NOTIFY_GET_TXPOOL_COMPLEMENT, &cryptonote_protocol_handler::handle_notify_get_txpool_complement) END_INVOKE_MAP2() bool on_idle(); @@ -102,7 +101,6 @@ namespace cryptonote bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); bool get_payload_sync_data(blobdata& data); bool get_payload_sync_data(CORE_SYNC_DATA& hshd); - bool get_stat_info(core_stat_info& stat_inf); bool on_callback(cryptonote_connection_context& context); t_core& get_core(){return m_core;} bool is_synchronized(){return m_synchronized;} @@ -127,6 +125,7 @@ namespace cryptonote int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context); int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context); + int handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context); //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); @@ -147,6 +146,7 @@ namespace cryptonote int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; + bool request_txpool_complement(cryptonote_connection_context &context); t_core& m_core; @@ -156,6 +156,7 @@ namespace cryptonote std::atomic<bool> m_synchronized; std::atomic<bool> m_stopping; std::atomic<bool> m_no_sync; + std::atomic<bool> m_ask_for_txpool_complement; boost::mutex m_sync_lock; block_queue m_block_queue; epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index a7bf0c283..3aacce421 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -83,6 +83,7 @@ namespace cryptonote m_p2p(p_net_layout), m_syncronized_connections_count(0), m_synchronized(offline), + m_ask_for_txpool_complement(true), m_stopping(false), m_no_sync(false) @@ -154,12 +155,6 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> - bool t_cryptonote_protocol_handler<t_core>::get_stat_info(core_stat_info& stat_inf) - { - return m_core.get_stat_info(stat_inf); - } - //------------------------------------------------------------------------------------------------------------------------ - template<class t_core> void t_cryptonote_protocol_handler<t_core>::log_connections() { std::stringstream ss; @@ -312,7 +307,7 @@ namespace cryptonote if (version >= 6 && version != hshd.top_version) { if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version()) - MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version that we think (" << + MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" << (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version << ") - we may be forked from the network and a software upgrade may be needed"); return false; @@ -344,7 +339,7 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; - if(is_inital && target == m_core.get_current_blockchain_height()) + if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; } @@ -885,6 +880,34 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) + { + MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + + std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; + std::vector<cryptonote::blobdata> local_txs; + + std::vector<cryptonote::blobdata> txes; + if (!m_core.get_txpool_complement(arg.hashes, txes)) + { + LOG_ERROR_CCONTEXT("failed to get txpool complement"); + return 1; + } + + NOTIFY_NEW_TRANSACTIONS::request new_txes; + new_txes.txs = std::move(txes); + + MLOG_P2P_MESSAGE + ( + "-->>NOTIFY_NEW_TRANSACTIONS: " + << ", txs.size()=" << new_txes.txs.size() + ); + + post_notify<NOTIFY_NEW_TRANSACTIONS>(new_txes, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); @@ -1671,9 +1694,9 @@ skip: const float max_multiplier = 10.f; const float min_multiplier = 1.25f; float multiplier = max_multiplier; - if (dt/1e6 >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) + if (dt >= REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) { - multiplier = max_multiplier - (dt/1e6-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY); + multiplier = max_multiplier - (dt-REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY) * (max_multiplier - min_multiplier) / (REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD - REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY); multiplier = std::min(max_multiplier, std::max(min_multiplier, multiplier)); } if (dl_speed * .8f > ctx.m_current_speed_down * multiplier) @@ -2201,6 +2224,27 @@ skip: } m_core.safesyncmode(true); m_p2p->clear_used_stripe_peers(); + + // ask for txpool complement from any suitable node if we did not yet + val_expected = true; + if (m_ask_for_txpool_complement.compare_exchange_strong(val_expected, false)) + { + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + { + if(context.m_state < cryptonote_connection_context::state_synchronizing) + { + MDEBUG(context << "not ready, ignoring"); + return true; + } + if (!request_txpool_complement(context)) + { + MERROR(context << "Failed to request txpool complement"); + return true; + } + return false; + }); + } + return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -2354,6 +2398,21 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::request_txpool_complement(cryptonote_connection_context &context) + { + NOTIFY_GET_TXPOOL_COMPLEMENT::request r = {}; + if (!m_core.get_pool_transaction_hashes(r.hashes, false)) + { + MERROR("Failed to get txpool hashes"); + return false; + } + MLOG_P2P_MESSAGE("-->>NOTIFY_GET_TXPOOL_COMPLEMENT: hashes.size()=" << r.hashes.size() ); + post_notify<NOTIFY_GET_TXPOOL_COMPLEMENT>(r, context); + MLOG_PEER_STATE("requesting txpool complement"); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> std::string t_cryptonote_protocol_handler<t_core>::get_peers_overview() const { std::stringstream ss; @@ -2463,7 +2522,10 @@ skip: MINFO("Target height decreasing from " << previous_target << " to " << target); m_core.set_target_blockchain_height(target); if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping) + { MCWARNING("global", "monerod is now disconnected from the network"); + m_ask_for_txpool_complement = true; + } } m_block_queue.flush_spans(context.m_connection_id, false); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index ed6d3af01..3a4081e39 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -857,13 +857,27 @@ bool t_command_parser_executor::set_bootstrap_daemon(const std::vector<std::stri bool t_command_parser_executor::flush_cache(const std::vector<std::string>& args) { + bool bad_txs = false, bad_blocks = false; + std::string arg; + if (args.empty()) goto show_list; - if (args[0] == "bad-txs") - return m_executor.flush_cache(true); + + for (size_t i = 0; i < args.size(); ++i) + { + arg = args[i]; + if (arg == "bad-txs") + bad_txs = true; + else if (arg == "bad-blocks") + bad_blocks = true; + else + goto show_list; + } + return m_executor.flush_cache(bad_txs, bad_blocks); show_list: - std::cout << "Cache type needed: bad-txs" << std::endl; + std::cout << "Invalid cache type: " << arg << std::endl; + std::cout << "Cache types: bad-txs bad-blocks" << std::endl; return true; } diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8ec690631..7fae77c30 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -325,7 +325,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "flush_cache" , std::bind(&t_command_parser_executor::flush_cache, &m_parser, p::_1) - , "flush_cache bad-txs" + , "flush_cache [bad-txs] [bad-blocks]" , "Flush the specified cache(s)." ); } diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index cb96b37b6..056f2f320 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -78,13 +78,14 @@ public: const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc); const auto main_rpc_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); - rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"}); + const auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; + const bool has_restricted_rpc_port_arg = !command_line::is_arg_defaulted(vm, restricted_rpc_port_arg); + rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core", !has_restricted_rpc_port_arg}); - auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; - if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg)) + if(has_restricted_rpc_port_arg) { auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); - rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"}); + rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true}); } } }; diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 213593aa7..6f545a3b7 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -56,12 +56,13 @@ public: , const bool restricted , const std::string & port , const std::string & description + , bool allow_rpc_payment ) : m_server{core.get(), p2p.get()}, m_description{description} { MGINFO("Initializing " << m_description << " RPC server..."); - if (!m_server.init(vm, restricted, port)) + if (!m_server.init(vm, restricted, port, allow_rpc_payment)) { throw std::runtime_error("Failed to initialize " + m_description + " RPC server."); } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 1ca728e39..086808f8e 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1905,9 +1905,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou tools::msg_writer() << "Sum of coinbase transactions between block heights [" << height << ", " << (height + count) << ") is " - << cryptonote::print_money(res.emission_amount + res.fee_amount) << " " - << "consisting of " << cryptonote::print_money(res.emission_amount) - << " in emissions, and " << cryptonote::print_money(res.fee_amount) << " in fees"; + << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount) + boost::multiprecision::uint128_t(res.wide_fee_amount)) << " " + << "consisting of " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_emission_amount)) + << " in emissions, and " << cryptonote::print_money(boost::multiprecision::uint128_t(res.wide_fee_amount)) << " in fees"; return true; } @@ -2427,7 +2427,7 @@ bool t_rpc_command_executor::set_bootstrap_daemon( return true; } -bool t_rpc_command_executor::flush_cache(bool bad_txs) +bool t_rpc_command_executor::flush_cache(bool bad_txs, bool bad_blocks) { cryptonote::COMMAND_RPC_FLUSH_CACHE::request req; cryptonote::COMMAND_RPC_FLUSH_CACHE::response res; @@ -2435,6 +2435,7 @@ bool t_rpc_command_executor::flush_cache(bool bad_txs) epee::json_rpc::error error_resp; req.bad_txs = bad_txs; + req.bad_blocks = bad_blocks; if (m_is_rpc) { diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 1754ce32e..66ada6f1f 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -172,7 +172,7 @@ public: bool rpc_payments(); - bool flush_cache(bool bad_txs); + bool flush_cache(bool bad_txs, bool invalid_blocks); }; } // namespace daemonize diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index cba829d3f..f4cc75fee 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -171,7 +171,8 @@ namespace net bool i2p_address::less(const i2p_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp index 904a9a0fc..4414861e7 100644 --- a/src/net/tor_address.cpp +++ b/src/net/tor_address.cpp @@ -173,7 +173,8 @@ namespace net bool tor_address::less(const tor_address& rhs) const noexcept { - return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + int res = std::strcmp(host_str(), rhs.host_str()); + return res < 0 || (res == 0 && port() < rhs.port()); } bool tor_address::is_same_host(const tor_address& rhs) const noexcept diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 0e9c1c942..31d8aad3f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -123,6 +123,7 @@ namespace nodetool peerid_type peer_id; uint32_t support_flags; bool m_in_timedsync; + std::set<epee::net_utils::network_address> sent_addresses; }; template<class t_payload_net_handler> @@ -300,8 +301,6 @@ namespace nodetool bool islimitup=false; bool islimitdown=false; - typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO; - CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing @@ -312,11 +311,6 @@ namespace nodetool HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake) HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping) -#ifdef ALLOW_DEBUG_COMMANDS - HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) - HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) - HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) -#endif HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, &node_server::handle_get_support_flags) CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() @@ -327,17 +321,11 @@ namespace nodetool int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); -#ifdef ALLOW_DEBUG_COMMANDS - int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); - int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); - int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); -#endif int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context); bool init_config(); bool make_default_peer_id(); bool make_default_config(); bool store_config(); - bool check_trust(const proof_of_trust& tr, epee::net_utils::zone zone_type); //----------------- levin_commands_handler ------------------------------------------------------------- @@ -362,7 +350,7 @@ namespace nodetool const boost::program_options::variables_map& vm ); bool idle_worker(); - bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); + bool handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, const epee::net_utils::connection_context_base& context); bool get_local_node_data(basic_node_data& node_data, const network_zone& zone); //bool get_local_handshake_data(handshake_data& hshd); @@ -392,7 +380,7 @@ namespace nodetool bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb); bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f); bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections); - void cache_connect_fail_info(const epee::net_utils::network_address& addr); + void record_addr_failed(const epee::net_utils::network_address& addr); bool is_addr_recently_failed(const epee::net_utils::network_address& addr); bool is_priority_node(const epee::net_utils::network_address& na); std::set<std::string> get_seed_nodes(cryptonote::network_type nettype) const; @@ -413,7 +401,6 @@ namespace nodetool bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit); bool has_too_many_connections(const epee::net_utils::network_address &address); - uint64_t get_connections_count(); size_t get_incoming_connections_count(); size_t get_incoming_connections_count(network_zone&); size_t get_outgoing_connections_count(); @@ -477,9 +464,6 @@ namespace nodetool epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; -#ifdef ALLOW_DEBUG_COMMANDS - uint64_t m_last_stat_request_time; -#endif std::list<epee::net_utils::network_address> m_priority_peers; std::vector<epee::net_utils::network_address> m_exclusive_peers; std::vector<epee::net_utils::network_address> m_seed_nodes; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 08bc76d26..9b6358dee 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -813,7 +813,6 @@ namespace nodetool //only in case if we really sure that we have external visible ip m_have_address = true; - m_last_stat_request_time = 0; //configure self @@ -940,15 +939,6 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - uint64_t node_server<t_payload_net_handler>::get_connections_count() - { - std::uint64_t count = 0; - for (auto& zone : m_network_zones) - count += zone.second.m_net_server.get_config_object().get_connections_count(); - return count; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::deinit() { kill(); @@ -1041,7 +1031,7 @@ namespace nodetool return; } - if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.node_data.local_time, context)) + if(!handle_remote_peerlist(rsp.local_peerlist_new, context)) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); add_host_fail(context.m_remote_address); @@ -1119,7 +1109,7 @@ namespace nodetool return; } - if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.local_time, context)) + if(!handle_remote_peerlist(rsp.local_peerlist_new, context)) { LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); @@ -1262,7 +1252,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str() /*<< ", try " << try_count*/); - //m_peerlist.set_peer_unreachable(pe); + record_addr_failed(na); return false; } @@ -1277,6 +1267,7 @@ namespace nodetool << na.str() /*<< ", try " << try_count*/); zone.m_net_server.get_config_object().close(con->m_connection_id); + record_addr_failed(na); return false; } @@ -1327,6 +1318,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << na.str()); + record_addr_failed(na); return false; } @@ -1339,6 +1331,7 @@ namespace nodetool LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str()); zone.m_net_server.get_config_object().close(con->m_connection_id); + record_addr_failed(na); return false; } @@ -1353,6 +1346,13 @@ namespace nodetool //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::record_addr_failed(const epee::net_utils::network_address& addr) + { + CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); + m_conn_fails_cache[addr.host_str()] = time(NULL); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr) { CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock); @@ -1434,10 +1434,10 @@ namespace nodetool std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); - size_t idx = 0, skipped = 0; for (int step = 0; step < 2; ++step) { bool skip_duplicate_class_B = step == 0; + size_t idx = 0, skipped = 0; zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ if (filtered.size() >= limit) return false; @@ -1543,6 +1543,7 @@ namespace nodetool return true; size_t try_count = 0; + bool is_connected_to_at_least_one_seed_node = false; size_t current_index = crypto::rand_idx(m_seed_nodes.size()); const net_server& server = m_network_zones.at(epee::net_utils::zone::public_).m_net_server; while(true) @@ -1550,21 +1551,25 @@ namespace nodetool if(server.is_stop_signal_sent()) return false; - if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + peerlist_entry pe_seed{}; + pe_seed.adr = m_seed_nodes[current_index]; + if (is_peer_used(pe_seed)) + is_connected_to_at_least_one_seed_node = true; + else if (try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) break; if(++try_count > m_seed_nodes.size()) { if (!m_fallback_seed_nodes_added) { MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); - current_index = m_seed_nodes.size(); + current_index = m_seed_nodes.size() - 1; for (const auto &peer: get_seed_nodes(m_nettype)) { MDEBUG("Fallback seed node: " << peer); append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT); } m_fallback_seed_nodes_added = true; - if (current_index == m_seed_nodes.size()) + if (current_index == m_seed_nodes.size() - 1) { MWARNING("No fallback seeds, continuing without seeds"); break; @@ -1573,7 +1578,8 @@ namespace nodetool } else { - MWARNING("Failed to connect to any of seed peers, continuing without seeds"); + if (!is_connected_to_at_least_one_seed_node) + MWARNING("Failed to connect to any of seed peers, continuing without seeds"); break; } } @@ -1894,7 +1900,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) + bool node_server<t_payload_net_handler>::handle_remote_peerlist(const std::vector<peerlist_entry>& peerlist, const epee::net_utils::connection_context_base& context) { std::vector<peerlist_entry> peerlist_ = peerlist; if(!sanitize_peerlist(peerlist_)) @@ -1911,16 +1917,13 @@ namespace nodetool } LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); - LOG_DEBUG_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); - return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_); + LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); + return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); }); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::get_local_node_data(basic_node_data& node_data, const network_zone& zone) { - time_t local_time; - time(&local_time); - node_data.local_time = local_time; // \TODO This can be an identifying value across zones (public internet to tor/i2p) ... node_data.peer_id = zone.m_config.m_peer_id; if(!m_hide_my_port && zone.m_can_pingback) node_data.my_port = m_external_port ? m_external_port : m_listening_port; @@ -1932,91 +1935,6 @@ namespace nodetool return true; } //----------------------------------------------------------------------------------- -#ifdef ALLOW_DEBUG_COMMANDS - template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::check_trust(const proof_of_trust& tr, const epee::net_utils::zone zone_type) - { - uint64_t local_time = time(NULL); - uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; - if(time_delata > 24*60*60 ) - { - MWARNING("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); - return false; - } - if(m_last_stat_request_time >= tr.time ) - { - MWARNING("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); - return false; - } - - const network_zone& zone = m_network_zones.at(zone_type); - if(zone.m_config.m_peer_id != tr.peer_id) - { - MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << peerid_to_string(zone.m_config.m_peer_id) << ")"); - return false; - } - crypto::public_key pk = AUTO_VAL_INIT(pk); - epee::string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pk); - crypto::hash h = get_proof_of_trust_hash(tr); - if(!crypto::check_signature(h, pk, tr.sign)) - { - MWARNING("check_trust failed: sign check failed"); - return false; - } - //update last request time - m_last_stat_request_time = tr.time; - return true; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr, context.m_remote_address.get_zone())) - { - drop_connection(context); - return 1; - } - rsp.connections_count = get_connections_count(); - rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); - rsp.version = MONERO_VERSION_FULL; - rsp.os_version = tools::get_os_version_string(); - m_payload_handler.get_stat_info(rsp.payload_info); - return 1; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr, context.m_remote_address.get_zone())) - { - drop_connection(context); - return 1; - } - m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - connection_entry ce; - ce.adr = cntxt.m_remote_address; - ce.id = cntxt.peer_id; - ce.is_income = cntxt.m_is_income; - rsp.connections_list.push_back(ce); - return true; - }); - - network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone()); - zone.m_peerlist.get_peerlist(rsp.local_peerlist_gray, rsp.local_peerlist_white); - rsp.my_id = zone.m_config.m_peer_id; - rsp.local_time = time(NULL); - return 1; - } - //----------------------------------------------------------------------------------- - template<class t_payload_net_handler> - int node_server<t_payload_net_handler>::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) - { - rsp.my_id = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_peer_id; - return 1; - } -#endif - //----------------------------------------------------------------------------------- template<class t_payload_net_handler> int node_server<t_payload_net_handler>::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context) { @@ -2291,12 +2209,20 @@ namespace nodetool } //fill response - rsp.local_time = time(NULL); - const epee::net_utils::zone zone_type = context.m_remote_address.get_zone(); network_zone& zone = m_network_zones.at(zone_type); - zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true); + std::vector<peerlist_entry> local_peerlist_new; + zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, P2P_DEFAULT_PEERS_IN_HANDSHAKE); + + //only include out peers we did not already send + rsp.local_peerlist_new.reserve(local_peerlist_new.size()); + for (auto &pe: local_peerlist_new) + { + if (!context.sent_addresses.insert(pe.adr).second) + continue; + rsp.local_peerlist_new.push_back(std::move(pe)); + } m_payload_handler.get_payload_sync_data(rsp.payload_data); /* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon) @@ -2418,6 +2344,8 @@ namespace nodetool //fill response zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true); + for (const auto &e: rsp.local_peerlist_new) + context.sent_addresses.insert(e.adr); get_local_node_data(rsp.node_data, zone); m_payload_handler.get_payload_sync_data(rsp.payload_data); LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE"); diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 58b704f73..300181bbb 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -43,6 +43,7 @@ #include <boost/range/adaptor/reversed.hpp> +#include "crypto/crypto.h" #include "cryptonote_config.h" #include "net/enums.h" #include "net/local_ip.h" @@ -101,7 +102,7 @@ namespace nodetool bool init(peerlist_types&& peers, bool allow_local_ip); size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} - bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs); + bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f = NULL); bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white); void get_peerlist(peerlist_types& peers); @@ -112,7 +113,6 @@ namespace nodetool bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash); - bool set_peer_unreachable(const peerlist_entry& pr); bool is_host_allowed(const epee::net_utils::network_address &address); bool get_random_gray_peer(peerlist_entry& pe); bool remove_from_peer_gray(const peerlist_entry& pe); @@ -213,12 +213,13 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs) + bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f) { CRITICAL_REGION_LOCAL(m_peerlist_lock); for(const peerlist_entry& be: outer_bs) { - append_with_peer_gray(be); + if (!f || f(be)) + append_with_peer_gray(be); } // delete extra elements trim_gray_peerlist(); @@ -269,19 +270,19 @@ namespace nodetool peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>(); uint32_t cnt = 0; - // picks a random set of peers within the first 120%, rather than a set of the first 100%. + // picks a random set of peers within the whole set, rather pick the first depth elements. // The intent is that if someone asks twice, they can't easily tell: // - this address was not in the first list, but is in the second, so the only way this can be // is if its last_seen was recently reset, so this means the target node recently had a new // connection to that address // - this address was in the first list, and not in the second, which means either the address - // was moved to the gray list (if it's not accessibe, which the attacker can check if + // was moved to the gray list (if it's not accessible, which the attacker can check if // the address accepts incoming connections) or it was the oldest to still fit in the 250 items, // so its last_seen is old. // // See Cao, Tong et al. "Exploring the Monero Peer-to-Peer Network". https://eprint.iacr.org/2019/411 // - const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth; + const uint32_t pick_depth = anonymize ? m_peers_white.size() : depth; bs_head.reserve(pick_depth); for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index)) { diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 393bddd05..609661871 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -40,9 +40,6 @@ #include "string_tools.h" #include "time_helper.h" #include "cryptonote_config.h" -#ifdef ALLOW_DEBUG_COMMANDS -#include "crypto/crypto.h" -#endif namespace nodetool { @@ -82,8 +79,7 @@ namespace nodetool BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(adr) KV_SERIALIZE(id) - if (!is_store || this_ref.last_seen != 0) - KV_SERIALIZE_OPT(last_seen, (int64_t)0) + KV_SERIALIZE_OPT(last_seen, (int64_t)0) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0) @@ -166,7 +162,6 @@ namespace nodetool struct basic_node_data { uuid network_id; - uint64_t local_time; uint32_t my_port; uint16_t rpc_port; uint32_t rpc_credits_per_hash; @@ -175,7 +170,6 @@ namespace nodetool BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) KV_SERIALIZE(peer_id) - KV_SERIALIZE(local_time) KV_SERIALIZE(my_port) KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0)) KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0) @@ -214,35 +208,7 @@ namespace nodetool BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(node_data) KV_SERIALIZE(payload_data) - if (is_store) - { - // saving: save both, so old and new peers can understand it - KV_SERIALIZE(local_peerlist_new) - std::vector<peerlist_entry_base<network_address_old>> local_peerlist; - for (const auto &p: this_ref.local_peerlist_new) - { - if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) - { - const epee::net_utils::network_address &na = p.adr; - const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); - local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash})); - } - else - MDEBUG("Not including in legacy peer list: " << p.adr.str()); - } - epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); - } - else - { - // loading: load old list only if there is no new one - if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) - { - std::vector<peerlist_entry_base<network_address_old>> local_peerlist; - epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); - for (const auto &p: local_peerlist) - ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash})); - } - } + KV_SERIALIZE(local_peerlist_new) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -268,42 +234,12 @@ namespace nodetool struct response_t { - uint64_t local_time; t_playload_type payload_data; std::vector<peerlist_entry> local_peerlist_new; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(local_time) KV_SERIALIZE(payload_data) - if (is_store) - { - // saving: save both, so old and new peers can understand it - KV_SERIALIZE(local_peerlist_new) - std::vector<peerlist_entry_base<network_address_old>> local_peerlist; - for (const auto &p: this_ref.local_peerlist_new) - { - if (p.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) - { - const epee::net_utils::network_address &na = p.adr; - const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); - local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen})); - } - else - MDEBUG("Not including in legacy peer list: " << p.adr.str()); - } - epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); - } - else - { - // loading: load old list only if there is no new one - if (!epee::serialization::selector<is_store>::serialize(this_ref.local_peerlist_new, stg, hparent_section, "local_peerlist_new")) - { - std::vector<peerlist_entry_base<network_address_old>> local_peerlist; - epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); - for (const auto &p: local_peerlist) - ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen})); - } - } + KV_SERIALIZE(local_peerlist_new) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -347,117 +283,6 @@ namespace nodetool }; -#ifdef ALLOW_DEBUG_COMMANDS - //These commands are considered as insecure, and made in debug purposes for a limited lifetime. - //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro. - - struct proof_of_trust - { - peerid_type peer_id; - uint64_t time; - crypto::signature sign; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(peer_id) - KV_SERIALIZE(time) - KV_SERIALIZE_VAL_POD_AS_BLOB(sign) - END_KV_SERIALIZE_MAP() - }; - - - template<class payload_stat_info> - struct COMMAND_REQUEST_STAT_INFO_T - { - const static int ID = P2P_COMMANDS_POOL_BASE + 4; - - struct request_t - { - proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string version; - std::string os_version; - uint64_t connections_count; - uint64_t incoming_connections_count; - payload_stat_info payload_info; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(version) - KV_SERIALIZE(os_version) - KV_SERIALIZE(connections_count) - KV_SERIALIZE(incoming_connections_count) - KV_SERIALIZE(payload_info) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_REQUEST_NETWORK_STATE - { - const static int ID = P2P_COMMANDS_POOL_BASE + 5; - - struct request_t - { - proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::vector<peerlist_entry> local_peerlist_white; - std::vector<peerlist_entry> local_peerlist_gray; - std::vector<connection_entry> connections_list; - peerid_type my_id; - uint64_t local_time; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list) - KV_SERIALIZE(my_id) - KV_SERIALIZE(local_time) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_REQUEST_PEER_ID - { - const static int ID = P2P_COMMANDS_POOL_BASE + 6; - - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - peerid_type my_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(my_id) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - /************************************************************************/ /* */ /************************************************************************/ @@ -482,16 +307,4 @@ namespace nodetool }; typedef epee::misc_utils::struct_init<response_t> response; }; - -#endif - - - inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) - { - std::string s; - s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id)); - s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); - } - } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index e92ae7c08..8e5207e82 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -125,11 +125,16 @@ namespace return (value + quantum - 1) / quantum * quantum; } + void store_128(boost::multiprecision::uint128_t value, uint64_t &slow64, std::string &swide, uint64_t &stop64) + { + slow64 = (value & 0xffffffffffffffff).convert_to<uint64_t>(); + swide = cryptonote::hex(value); + stop64 = ((value >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + } + void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64) { - sdiff = (difficulty & 0xffffffffffffffff).convert_to<uint64_t>(); - swdiff = cryptonote::hex(difficulty); - stop64 = ((difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + store_128(difficulty, sdiff, swdiff, stop64); } } @@ -148,6 +153,7 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_payment_address); command_line::add_arg(desc, arg_rpc_payment_difficulty); command_line::add_arg(desc, arg_rpc_payment_credits); + command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -157,6 +163,8 @@ namespace cryptonote : m_core(cr) , m_p2p(p2p) , m_was_bootstrap_ever_used(false) + , disable_rpc_ban(false) + , m_rpc_payment_allow_free_loopback(false) {} //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password) @@ -236,6 +244,7 @@ namespace cryptonote const boost::program_options::variables_map& vm , const bool restricted , const std::string& port + , bool allow_rpc_payment ) { m_restricted = restricted; @@ -246,8 +255,9 @@ namespace cryptonote if (!rpc_config) return false; + disable_rpc_ban = rpc_config->disable_rpc_ban; std::string address = command_line::get_arg(vm, arg_rpc_payment_address); - if (!address.empty()) + if (!address.empty() && allow_rpc_payment) { if (!m_restricted && nettype() != FAKECHAIN) { @@ -272,6 +282,7 @@ namespace cryptonote MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given"); return false; } + m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); m_rpc_payment.reset(new rpc_payment(info.address, diff, credits)); m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir)); m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff)); @@ -345,7 +356,7 @@ namespace cryptonote #define CHECK_PAYMENT_BASE(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P > 0 && !check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) #define CHECK_PAYMENT(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, false) #define CHECK_PAYMENT_SAME_TS(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, true) -#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) +#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx || (m_rpc_payment_allow_free_loopback && ctx->m_remote_address.is_loopback())) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0) //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_ready() { @@ -358,7 +369,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::add_host_fail(const connection_context *ctx, unsigned int score) { - if(!ctx || !ctx->m_remote_address.is_blockable()) + if(!ctx || !ctx->m_remote_address.is_blockable() || disable_rpc_ban) return false; CRITICAL_REGION_LOCAL(m_host_fails_score_lock); @@ -543,7 +554,7 @@ namespace cryptonote CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK); - size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + size_t size = 0, ntxes = 0; res.blocks.reserve(bs.size()); res.output_indices.reserve(bs.size()); for(auto& bd: bs) @@ -551,8 +562,7 @@ namespace cryptonote res.blocks.resize(res.blocks.size()+1); res.blocks.back().pruned = req.prune; res.blocks.back().block = bd.first.first; - pruned_size += bd.first.first.size(); - unpruned_size += bd.first.first.size(); + size += bd.first.first.size(); res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); ntxes += bd.second.size(); res.output_indices.back().indices.reserve(1 + bd.second.size()); @@ -561,11 +571,10 @@ namespace cryptonote res.blocks.back().txs.reserve(bd.second.size()); for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { - unpruned_size += i->second.size(); res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash}); i->second.clear(); i->second.shrink_to_fit(); - pruned_size += res.blocks.back().txs.back().blob.size(); + size += res.blocks.back().txs.back().blob.size(); } const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1); @@ -588,7 +597,7 @@ namespace cryptonote } } - MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size); + MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, size " << size); res.status = CORE_RPC_STATUS_OK; return true; } @@ -782,6 +791,9 @@ namespace cryptonote CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); + const bool restricted = m_restricted && ctx; + const bool request_has_rpc_origin = ctx != NULL; + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -816,7 +828,7 @@ namespace cryptonote { std::vector<tx_info> pool_tx_info; std::vector<spent_key_image_info> pool_key_image_info; - bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info); + bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, !request_has_rpc_origin || !restricted); if(r) { // sort to match original request @@ -1094,7 +1106,7 @@ namespace cryptonote return true; } - if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob)) + if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0))) { res.status = "Failed"; res.reason = "Sanity check failed"; @@ -2210,6 +2222,7 @@ namespace cryptonote error_resp.message = "Internal error: can't produce valid response."; return false; } + res.miner_tx_hash = res.block_header.miner_tx_hash; for (size_t n = 0; n < blk.tx_hashes.size(); ++n) { res.tx_hashes.push_back(epee::string_tools::pod_to_hex(blk.tx_hashes[n])); @@ -2496,9 +2509,9 @@ namespace cryptonote return true; } CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false); - std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count); - res.emission_amount = amounts.first; - res.fee_amount = amounts.second; + std::pair<boost::multiprecision::uint128_t, boost::multiprecision::uint128_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count); + store_128(amounts.first, res.emission_amount, res.wide_emission_amount, res.emission_amount_top64); + store_128(amounts.second, res.fee_amount, res.wide_fee_amount, res.fee_amount_top64); res.status = CORE_RPC_STATUS_OK; return true; } @@ -3013,6 +3026,8 @@ namespace cryptonote RPC_TRACKER(flush_cache); if (req.bad_txs) m_core.flush_bad_txs_cache(); + if (req.bad_blocks) + m_core.flush_invalid_blocks(); res.status = CORE_RPC_STATUS_OK; return true; } @@ -3246,4 +3261,10 @@ namespace cryptonote , "Restrict RPC to clients sending micropayment, yields that many credits per payment" , DEFAULT_PAYMENT_CREDITS_PER_HASH }; + + const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_payment_allow_free_loopback = { + "rpc-payment-allow-free-loopback" + , "Allow free access from the loopback address (ie, the local host)" + , false + }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 23c611470..d82ab6af4 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -75,6 +75,7 @@ namespace cryptonote static const command_line::arg_descriptor<std::string> arg_rpc_payment_address; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty; static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits; + static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback; typedef epee::net_utils::connection_context_base connection_context; @@ -88,7 +89,8 @@ namespace cryptonote bool init( const boost::program_options::variables_map& vm, const bool restricted, - const std::string& port + const std::string& port, + bool allow_rpc_payment ); network_type nettype() const { return m_core.get_nettype(); } @@ -285,6 +287,8 @@ private: epee::critical_section m_host_fails_score_lock; std::map<std::string, uint64_t> m_host_fails_score; std::unique_ptr<rpc_payment> m_rpc_payment; + bool disable_rpc_ban; + bool m_rpc_payment_allow_free_loopback; }; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 12b042c7e..dbb1d4472 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -2023,12 +2023,20 @@ namespace cryptonote struct response_t: public rpc_access_response_base { uint64_t emission_amount; + std::string wide_emission_amount; + uint64_t emission_amount_top64; uint64_t fee_amount; + std::string wide_fee_amount; + uint64_t fee_amount_top64; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_access_response_base) KV_SERIALIZE(emission_amount) + KV_SERIALIZE(wide_emission_amount) + KV_SERIALIZE(emission_amount_top64) KV_SERIALIZE(fee_amount) + KV_SERIALIZE(wide_fee_amount) + KV_SERIALIZE(fee_amount_top64) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -2561,10 +2569,12 @@ namespace cryptonote struct request_t: public rpc_request_base { bool bad_txs; + bool bad_blocks; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(bad_txs, false) + KV_SERIALIZE_OPT(bad_blocks, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 24800ff20..125688ba5 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -28,6 +28,10 @@ #include "daemon_handler.h" +#include <algorithm> +#include <cstring> +#include <stdexcept> + #include <boost/uuid/nil_generator.hpp> // likely included by daemon_handler.h's includes, // but including here for clarity @@ -42,6 +46,74 @@ namespace cryptonote namespace rpc { + namespace + { + using handler_function = std::string(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& msg); + struct handler_map + { + const char* method_name; + handler_function* call; + }; + + bool operator<(const handler_map& lhs, const handler_map& rhs) noexcept + { + return std::strcmp(lhs.method_name, rhs.method_name) < 0; + } + + bool operator<(const handler_map& lhs, const std::string& rhs) noexcept + { + return std::strcmp(lhs.method_name, rhs.c_str()) < 0; + } + + template<typename Message> + std::string handle_message(DaemonHandler& handler, const rapidjson::Value& id, const rapidjson::Value& parameters) + { + typename Message::Request request{}; + request.fromJson(parameters); + + typename Message::Response response{}; + handler.handle(request, response); + return FullMessage::getResponse(response, id); + } + + constexpr const handler_map handlers[] = + { + {u8"get_block_hash", handle_message<GetBlockHash>}, + {u8"get_block_header_by_hash", handle_message<GetBlockHeaderByHash>}, + {u8"get_block_header_by_height", handle_message<GetBlockHeaderByHeight>}, + {u8"get_block_headers_by_height", handle_message<GetBlockHeadersByHeight>}, + {u8"get_blocks_fast", handle_message<GetBlocksFast>}, + {u8"get_dynamic_fee_estimate", handle_message<GetFeeEstimate>}, + {u8"get_hashes_fast", handle_message<GetHashesFast>}, + {u8"get_height", handle_message<GetHeight>}, + {u8"get_info", handle_message<GetInfo>}, + {u8"get_last_block_header", handle_message<GetLastBlockHeader>}, + {u8"get_output_distribution", handle_message<GetOutputDistribution>}, + {u8"get_output_histogram", handle_message<GetOutputHistogram>}, + {u8"get_output_keys", handle_message<GetOutputKeys>}, + {u8"get_peer_list", handle_message<GetPeerList>}, + {u8"get_rpc_version", handle_message<GetRPCVersion>}, + {u8"get_transaction_pool", handle_message<GetTransactionPool>}, + {u8"get_transactions", handle_message<GetTransactions>}, + {u8"get_tx_global_output_indices", handle_message<GetTxGlobalOutputIndices>}, + {u8"hard_fork_info", handle_message<HardForkInfo>}, + {u8"key_images_spent", handle_message<KeyImagesSpent>}, + {u8"mining_status", handle_message<MiningStatus>}, + {u8"save_bc", handle_message<SaveBC>}, + {u8"send_raw_tx", handle_message<SendRawTxHex>}, + {u8"set_log_level", handle_message<SetLogLevel>}, + {u8"start_mining", handle_message<StartMining>}, + {u8"stop_mining", handle_message<StopMining>} + }; + } // anonymous + + DaemonHandler::DaemonHandler(cryptonote::core& c, t_p2p& p2p) + : m_core(c), m_p2p(p2p) + { + const auto last_sorted = std::is_sorted_until(std::begin(handlers), std::end(handlers)); + if (last_sorted != std::end(handlers)) + throw std::logic_error{std::string{"ZMQ JSON-RPC handlers map is not properly sorted, see "} + last_sorted->method_name}; + } void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res) { @@ -840,68 +912,21 @@ namespace rpc { 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, SendRawTx, req_json, resp_message, handle); - REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, 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, GetFeeEstimate, req_json, resp_message, handle); - REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle); - - // if none of the request types matches - if (resp_message == NULL) - { + const auto matched_handler = std::lower_bound(std::begin(handlers), std::end(handlers), request_type); + if (matched_handler == std::end(handlers) || matched_handler->method_name != request_type) 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; + std::string response = matched_handler->call(*this, req_full.getID(), req_full.getMessage()); MDEBUG("Returning RPC response: " << response); - return response; } catch (const std::exception& e) { - if (resp_message) - { - delete resp_message; - } - return BAD_JSON(e.what()); } } diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 34723f676..c33f608ab 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -50,7 +50,7 @@ class DaemonHandler : public RpcHandler { public: - DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { } + DaemonHandler(cryptonote::core& c, t_p2p& p2p); ~DaemonHandler() { } diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index cf0f5ece1..5c179408e 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -34,99 +34,47 @@ namespace cryptonote namespace rpc { +void GetHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -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 SendRawTx::name = "send_raw_tx"; -const char* const SendRawTxHex::name = "send_raw_tx_hex"; -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 GetFeeEstimate::name = "get_dynamic_fee_estimate"; -const char* const GetOutputDistribution::name = "get_output_distribution"; - - - - -rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const +void GetHeight::Request::fromJson(const rapidjson::Value& val) { - return Message::toJson(doc); } -void GetHeight::Request::fromJson(rapidjson::Value& val) +void GetHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { + INSERT_INTO_JSON_OBJECT(dest, height, height); } -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) +void GetHeight::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, height, height); } -rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const +void GetBlocksFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) 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; + INSERT_INTO_JSON_OBJECT(dest, block_ids, block_ids); + INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); + INSERT_INTO_JSON_OBJECT(dest, prune, prune); } -void GetBlocksFast::Request::fromJson(rapidjson::Value& val) +void GetBlocksFast::Request::fromJson(const 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 +void GetBlocksFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) 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; + INSERT_INTO_JSON_OBJECT(dest, blocks, blocks); + INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); + INSERT_INTO_JSON_OBJECT(dest, current_height, current_height); + INSERT_INTO_JSON_OBJECT(dest, output_indices, output_indices); } -void GetBlocksFast::Response::fromJson(rapidjson::Value& val) +void GetBlocksFast::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, blocks, blocks); GET_FROM_JSON_OBJECT(val, start_height, start_height); @@ -135,38 +83,26 @@ void GetBlocksFast::Response::fromJson(rapidjson::Value& val) } -rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const +void GetHashesFast::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) 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; + INSERT_INTO_JSON_OBJECT(dest, known_hashes, known_hashes); + INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); } -void GetHashesFast::Request::fromJson(rapidjson::Value& val) +void GetHashesFast::Request::fromJson(const 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 +void GetHashesFast::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) 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; + INSERT_INTO_JSON_OBJECT(dest, hashes, hashes); + INSERT_INTO_JSON_OBJECT(dest, start_height, start_height); + INSERT_INTO_JSON_OBJECT(dest, current_height, current_height); } -void GetHashesFast::Response::fromJson(rapidjson::Value& val) +void GetHashesFast::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, hashes, hashes); GET_FROM_JSON_OBJECT(val, start_height, start_height); @@ -174,154 +110,114 @@ void GetHashesFast::Response::fromJson(rapidjson::Value& val) } -rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const +void GetTransactions::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes); - - return val; + INSERT_INTO_JSON_OBJECT(dest, tx_hashes, tx_hashes); } -void GetTransactions::Request::fromJson(rapidjson::Value& val) +void GetTransactions::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes); } -rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const +void GetTransactions::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - rapidjson::Value val(rapidjson::kObjectType); - - INSERT_INTO_JSON_OBJECT(val, doc, txs, txs); - INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes); - - return val; + INSERT_INTO_JSON_OBJECT(dest, txs, txs); + INSERT_INTO_JSON_OBJECT(dest, missed_hashes, missed_hashes); } -void GetTransactions::Response::fromJson(rapidjson::Value& val) +void GetTransactions::Response::fromJson(const 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 +void KeyImagesSpent::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); - - return val; + INSERT_INTO_JSON_OBJECT(dest, key_images, key_images); } -void KeyImagesSpent::Request::fromJson(rapidjson::Value& val) +void KeyImagesSpent::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, key_images, key_images); } -rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const +void KeyImagesSpent::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status); - - return val; + INSERT_INTO_JSON_OBJECT(dest, spent_status, spent_status); } -void KeyImagesSpent::Response::fromJson(rapidjson::Value& val) +void KeyImagesSpent::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, spent_status, spent_status); } -rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const +void GetTxGlobalOutputIndices::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash); - - return val; + INSERT_INTO_JSON_OBJECT(dest, tx_hash, tx_hash); } -void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val) +void GetTxGlobalOutputIndices::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash); } -rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const +void GetTxGlobalOutputIndices::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); - - return val; + INSERT_INTO_JSON_OBJECT(dest, output_indices, output_indices); } -void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val) +void GetTxGlobalOutputIndices::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, output_indices, output_indices); } -rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const +void SendRawTx::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, tx, tx); - INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); - - return val; + INSERT_INTO_JSON_OBJECT(dest, tx, tx); + INSERT_INTO_JSON_OBJECT(dest, relay, relay); } -void SendRawTx::Request::fromJson(rapidjson::Value& val) +void SendRawTx::Request::fromJson(const 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 +void SendRawTx::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed); - - return val; + INSERT_INTO_JSON_OBJECT(dest, relayed, relayed); } -void SendRawTx::Response::fromJson(rapidjson::Value& val) +void SendRawTx::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, relayed, relayed); } -rapidjson::Value SendRawTxHex::Request::toJson(rapidjson::Document& doc) const +void SendRawTxHex::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, tx_as_hex, tx_as_hex); - INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); - - return val; + INSERT_INTO_JSON_OBJECT(dest, tx_as_hex, tx_as_hex); + INSERT_INTO_JSON_OBJECT(dest, relay, relay); } -void SendRawTxHex::Request::fromJson(rapidjson::Value& val) +void SendRawTxHex::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex); GET_FROM_JSON_OBJECT(val, relay, relay); } -rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const +void StartMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - 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; + INSERT_INTO_JSON_OBJECT(dest, miner_address, miner_address); + INSERT_INTO_JSON_OBJECT(dest, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(dest, do_background_mining, do_background_mining); + INSERT_INTO_JSON_OBJECT(dest, ignore_battery, ignore_battery); } -void StartMining::Request::fromJson(rapidjson::Value& val) +void StartMining::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, miner_address, miner_address); GET_FROM_JSON_OBJECT(val, threads_count, threads_count); @@ -329,58 +225,46 @@ void StartMining::Request::fromJson(rapidjson::Value& val) 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::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void StartMining::Response::fromJson(rapidjson::Value& val) +void StartMining::Response::fromJson(const rapidjson::Value& val) { } -rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const -{ - return Message::toJson(doc); -} +void StopMining::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void StopMining::Request::fromJson(rapidjson::Value& val) +void StopMining::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const -{ - return Message::toJson(doc); -} +void StopMining::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void StopMining::Response::fromJson(rapidjson::Value& val) +void StopMining::Response::fromJson(const rapidjson::Value& val) { } -rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const -{ - return Message::toJson(doc); -} +void MiningStatus::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void MiningStatus::Request::fromJson(rapidjson::Value& val) +void MiningStatus::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const +void MiningStatus::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - 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; + INSERT_INTO_JSON_OBJECT(dest, active, active); + INSERT_INTO_JSON_OBJECT(dest, speed, speed); + INSERT_INTO_JSON_OBJECT(dest, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(dest, address, address); + INSERT_INTO_JSON_OBJECT(dest, is_background_mining_enabled, is_background_mining_enabled); } -void MiningStatus::Response::fromJson(rapidjson::Value& val) +void MiningStatus::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, active, active); GET_FROM_JSON_OBJECT(val, speed, speed); @@ -390,318 +274,230 @@ void MiningStatus::Response::fromJson(rapidjson::Value& val) } -rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const -{ - return Message::toJson(doc); -} +void GetInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void GetInfo::Request::fromJson(rapidjson::Value& val) +void GetInfo::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const +void GetInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, info, info); - - return val; + INSERT_INTO_JSON_OBJECT(dest, info, info); } -void GetInfo::Response::fromJson(rapidjson::Value& val) +void GetInfo::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, info, info); } -rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); +void SaveBC::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} - return val; -} - -void SaveBC::Request::fromJson(rapidjson::Value& val) +void SaveBC::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); +void SaveBC::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} - return val; -} - -void SaveBC::Response::fromJson(rapidjson::Value& val) +void SaveBC::Response::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const +void GetBlockHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, height, height); - - return val; + INSERT_INTO_JSON_OBJECT(dest, height, height); } -void GetBlockHash::Request::fromJson(rapidjson::Value& val) +void GetBlockHash::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, height, height); } -rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const +void GetBlockHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); - - return val; + INSERT_INTO_JSON_OBJECT(dest, hash, hash); } -void GetBlockHash::Response::fromJson(rapidjson::Value& val) +void GetBlockHash::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, hash, hash); } -rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); - - return val; -} +void GetLastBlockHeader::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val) +void GetLastBlockHeader::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const +void GetLastBlockHeader::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, header, header); - - return val; + INSERT_INTO_JSON_OBJECT(dest, header, header); } -void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val) +void GetLastBlockHeader::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, header, header); } -rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const +void GetBlockHeaderByHash::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); - - return val; + INSERT_INTO_JSON_OBJECT(dest, hash, hash); } -void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val) +void GetBlockHeaderByHash::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, hash, hash); } -rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const +void GetBlockHeaderByHash::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, header, header); - - return val; + INSERT_INTO_JSON_OBJECT(dest, header, header); } -void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val) +void GetBlockHeaderByHash::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, header, header); } -rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const +void GetBlockHeaderByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, height, height); - - return val; + INSERT_INTO_JSON_OBJECT(dest, height, height); } -void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val) +void GetBlockHeaderByHeight::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, height, height); } -rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const +void GetBlockHeaderByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, header, header); - - return val; + INSERT_INTO_JSON_OBJECT(dest, header, header); } -void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val) +void GetBlockHeaderByHeight::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, header, header); } -rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const +void GetBlockHeadersByHeight::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, heights, heights); - - return val; + INSERT_INTO_JSON_OBJECT(dest, heights, heights); } -void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val) +void GetBlockHeadersByHeight::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, heights, heights); } -rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const +void GetBlockHeadersByHeight::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, headers, headers); - - return val; + INSERT_INTO_JSON_OBJECT(dest, headers, headers); } -void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val) +void GetBlockHeadersByHeight::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, headers, headers); } -rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const -{ - auto val = Message::toJson(doc); +void GetPeerList::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} - return val; -} - -void GetPeerList::Request::fromJson(rapidjson::Value& val) +void GetPeerList::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const +void GetPeerList::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list); - INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list); - - return val; + INSERT_INTO_JSON_OBJECT(dest, white_list, white_list); + INSERT_INTO_JSON_OBJECT(dest, gray_list, gray_list); } -void GetPeerList::Response::fromJson(rapidjson::Value& val) +void GetPeerList::Response::fromJson(const 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 +void SetLogLevel::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - auto& al = doc.GetAllocator(); - - val.AddMember("level", level, al); - - return val; + INSERT_INTO_JSON_OBJECT(dest, level, level); } -void SetLogLevel::Request::fromJson(rapidjson::Value& val) +void SetLogLevel::Request::fromJson(const 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::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void SetLogLevel::Response::fromJson(rapidjson::Value& val) +void SetLogLevel::Response::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const -{ - return Message::toJson(doc); -} +void GetTransactionPool::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void GetTransactionPool::Request::fromJson(rapidjson::Value& val) +void GetTransactionPool::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const +void GetTransactionPool::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions); - INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); - - return val; + INSERT_INTO_JSON_OBJECT(dest, transactions, transactions); + INSERT_INTO_JSON_OBJECT(dest, key_images, key_images); } -void GetTransactionPool::Response::fromJson(rapidjson::Value& val) +void GetTransactionPool::Response::fromJson(const 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 +void HardForkInfo::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, version, version); - - return val; + INSERT_INTO_JSON_OBJECT(dest, version, version); } -void HardForkInfo::Request::fromJson(rapidjson::Value& val) +void HardForkInfo::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, version, version); } -rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const +void HardForkInfo::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, info, info); - - return val; + INSERT_INTO_JSON_OBJECT(dest, info, info); } -void HardForkInfo::Response::fromJson(rapidjson::Value& val) +void HardForkInfo::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, info, info); } -rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const +void GetOutputHistogram::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - 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; + INSERT_INTO_JSON_OBJECT(dest, amounts, amounts); + INSERT_INTO_JSON_OBJECT(dest, min_count, min_count); + INSERT_INTO_JSON_OBJECT(dest, max_count, max_count); + INSERT_INTO_JSON_OBJECT(dest, unlocked, unlocked); + INSERT_INTO_JSON_OBJECT(dest, recent_cutoff, recent_cutoff); } -void GetOutputHistogram::Request::fromJson(rapidjson::Value& val) +void GetOutputHistogram::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, amounts, amounts); GET_FROM_JSON_OBJECT(val, min_count, min_count); @@ -710,100 +506,74 @@ void GetOutputHistogram::Request::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff); } -rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const +void GetOutputHistogram::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram); - - return val; + INSERT_INTO_JSON_OBJECT(dest, histogram, histogram); } -void GetOutputHistogram::Response::fromJson(rapidjson::Value& val) +void GetOutputHistogram::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, histogram, histogram); } -rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const +void GetOutputKeys::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs); - - return val; + INSERT_INTO_JSON_OBJECT(dest, outputs, outputs); } -void GetOutputKeys::Request::fromJson(rapidjson::Value& val) +void GetOutputKeys::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, outputs, outputs); } -rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const +void GetOutputKeys::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, keys, keys); - - return val; + INSERT_INTO_JSON_OBJECT(dest, keys, keys); } -void GetOutputKeys::Response::fromJson(rapidjson::Value& val) +void GetOutputKeys::Response::fromJson(const 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::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const +{} -void GetRPCVersion::Request::fromJson(rapidjson::Value& val) +void GetRPCVersion::Request::fromJson(const rapidjson::Value& val) { } -rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const +void GetRPCVersion::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, version, version); - - return val; + INSERT_INTO_JSON_OBJECT(dest, version, version); } -void GetRPCVersion::Response::fromJson(rapidjson::Value& val) +void GetRPCVersion::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, version, version); } -rapidjson::Value GetFeeEstimate::Request::toJson(rapidjson::Document& doc) const +void GetFeeEstimate::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks); - - return val; + INSERT_INTO_JSON_OBJECT(dest, num_grace_blocks, num_grace_blocks); } -void GetFeeEstimate::Request::fromJson(rapidjson::Value& val) +void GetFeeEstimate::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks); } -rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) const +void GetFeeEstimate::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee); - INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask); - INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale); - INSERT_INTO_JSON_OBJECT(val, doc, hard_fork_version, hard_fork_version); - - return val; + INSERT_INTO_JSON_OBJECT(dest, estimated_base_fee, estimated_base_fee); + INSERT_INTO_JSON_OBJECT(dest, fee_mask, fee_mask); + INSERT_INTO_JSON_OBJECT(dest, size_scale, size_scale); + INSERT_INTO_JSON_OBJECT(dest, hard_fork_version, hard_fork_version); } -void GetFeeEstimate::Response::fromJson(rapidjson::Value& val) +void GetFeeEstimate::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee); GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask); @@ -811,19 +581,15 @@ void GetFeeEstimate::Response::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version); } -rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const +void GetOutputDistribution::Request::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); - INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height); - INSERT_INTO_JSON_OBJECT(val, doc, to_height, to_height); - INSERT_INTO_JSON_OBJECT(val, doc, cumulative, cumulative); - - return val; + INSERT_INTO_JSON_OBJECT(dest, amounts, amounts); + INSERT_INTO_JSON_OBJECT(dest, from_height, from_height); + INSERT_INTO_JSON_OBJECT(dest, to_height, to_height); + INSERT_INTO_JSON_OBJECT(dest, cumulative, cumulative); } -void GetOutputDistribution::Request::fromJson(rapidjson::Value& val) +void GetOutputDistribution::Request::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, amounts, amounts); GET_FROM_JSON_OBJECT(val, from_height, from_height); @@ -831,17 +597,13 @@ void GetOutputDistribution::Request::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, cumulative, cumulative); } -rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const +void GetOutputDistribution::Response::doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const { - auto val = Message::toJson(doc); - - INSERT_INTO_JSON_OBJECT(val, doc, status, status); - INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions); - - return val; + INSERT_INTO_JSON_OBJECT(dest, status, status); + INSERT_INTO_JSON_OBJECT(dest, distributions, distributions); } -void GetOutputDistribution::Response::fromJson(rapidjson::Value& val) +void GetOutputDistribution::Response::fromJson(const rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, status, status); GET_FROM_JSON_OBJECT(val, distributions, distributions); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index c0d9aed0a..bb5059cdc 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -28,6 +28,8 @@ #pragma once +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> #include <unordered_map> #include <vector> @@ -40,26 +42,25 @@ #define BEGIN_RPC_MESSAGE_CLASS(classname) \ class classname \ { \ - public: \ - static const char* const name; + public: #define BEGIN_RPC_MESSAGE_REQUEST \ - class Request : public Message \ + class Request final : public Message \ { \ public: \ Request() { } \ ~Request() { } \ - rapidjson::Value toJson(rapidjson::Document& doc) const; \ - void fromJson(rapidjson::Value& val); + void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \ + void fromJson(const rapidjson::Value& val) override final; #define BEGIN_RPC_MESSAGE_RESPONSE \ - class Response : public Message \ + class Response final : public Message \ { \ public: \ Response() { } \ ~Response() { } \ - rapidjson::Value toJson(rapidjson::Document& doc) const; \ - void fromJson(rapidjson::Value& val); + void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const override final; \ + void fromJson(const rapidjson::Value& val) override final; #define END_RPC_MESSAGE_REQUEST }; #define END_RPC_MESSAGE_RESPONSE }; diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 158b58005..a3df7fb56 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -27,12 +27,10 @@ // 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 { @@ -54,60 +52,23 @@ constexpr const char params_field[] = "params"; constexpr const char result_field[] = "result"; } -rapidjson::Value Message::toJson(rapidjson::Document& doc) const +void Message::toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) 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; + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, status, status); + INSERT_INTO_JSON_OBJECT(dest, error_details, error_details); + INSERT_INTO_JSON_OBJECT(dest, rpc_version, DAEMON_RPC_VERSION_ZMQ); + doToJson(dest); + dest.EndObject(); } -void Message::fromJson(rapidjson::Value& val) +void Message::fromJson(const 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_field, rapidjson::StringRef(request.c_str()), doc.GetAllocator()); - doc.AddMember(params_field, 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(result_field, 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()); @@ -132,30 +93,13 @@ FullMessage::FullMessage(const std::string& json_string, bool request) } } -std::string FullMessage::getJson() -{ - - if (!doc.HasMember(id_field)) - { - doc.AddMember(id_field, 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_field) return doc[method_field].GetString(); } -rapidjson::Value& FullMessage::getMessage() +const rapidjson::Value& FullMessage::getMessage() const { if (doc.HasMember(params_field)) { @@ -174,30 +118,15 @@ rapidjson::Value& FullMessage::getMessage() rapidjson::Value FullMessage::getMessageCopy() { - rapidjson::Value& val = getMessage(); - - return rapidjson::Value(val, doc.GetAllocator()); + return rapidjson::Value(getMessage(), doc.GetAllocator()); } -rapidjson::Value& FullMessage::getID() +const rapidjson::Value& FullMessage::getID() const { OBJECT_HAS_MEMBER_OR_THROW(doc, id_field) return doc[id_field]; } -void FullMessage::setID(rapidjson::Value& id) -{ - auto itr = doc.FindMember(id_field); - if (itr != doc.MemberEnd()) - { - itr->value = id; - } - else - { - doc.AddMember(id_field, id, doc.GetAllocator()); - } -} - cryptonote::rpc::error FullMessage::getError() { cryptonote::rpc::error err; @@ -211,82 +140,89 @@ cryptonote::rpc::error FullMessage::getError() return err; } -FullMessage FullMessage::requestMessage(const std::string& request, Message* message) +std::string FullMessage::getRequest(const std::string& request, const Message& message, const unsigned id) { - return FullMessage(request, message); -} + rapidjson::StringBuffer buffer; + { + rapidjson::Writer<rapidjson::StringBuffer> dest{buffer}; -FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id) -{ - auto mes = requestMessage(request, message); - mes.setID(id); - return mes; -} + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3})); -FullMessage FullMessage::responseMessage(Message* message) -{ - return FullMessage(message); -} + dest.Key(id_field); + json::toJsonValue(dest, id); -FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id) -{ - auto mes = responseMessage(message); - mes.setID(id); - return mes; + dest.Key(method_field); + json::toJsonValue(dest, request); + + dest.Key(params_field); + message.toJson(dest); + + dest.EndObject(); + + if (!dest.IsComplete()) + throw std::logic_error{"Invalid JSON tree generated"}; + } + return std::string{buffer.GetString(), buffer.GetSize()}; } -FullMessage* FullMessage::timeoutMessage() + +std::string FullMessage::getResponse(const Message& message, const rapidjson::Value& id) { - auto *full_message = new FullMessage(); + rapidjson::StringBuffer buffer; + { + rapidjson::Writer<rapidjson::StringBuffer> dest{buffer}; - auto& doc = full_message->doc; - auto& al = full_message->doc.GetAllocator(); + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, jsonrpc, (boost::string_ref{"2.0", 3})); - doc.SetObject(); + dest.Key(id_field); + json::toJsonValue(dest, id); - // required by JSON-RPC 2.0 spec - doc.AddMember("jsonrpc", "2.0", al); + if (message.status == Message::STATUS_OK) + { + dest.Key(result_field); + message.toJson(dest); + } + else + { + cryptonote::rpc::error err; - cryptonote::rpc::error err; + err.error_str = message.status; + err.message = message.error_details; - err.error_str = "RPC request timed out."; - INSERT_INTO_JSON_OBJECT(doc, doc, err, err); + INSERT_INTO_JSON_OBJECT(dest, error, err); + } + dest.EndObject(); - return full_message; + if (!dest.IsComplete()) + throw std::logic_error{"Invalid JSON tree generated"}; + } + return std::string{buffer.GetString(), buffer.GetSize()}; } // 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(); + rapidjson::Value invalid; + return BAD_REQUEST(request, invalid); } -std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id) +std::string BAD_REQUEST(const std::string& request, const 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(); + return FullMessage::getResponse(fail, id); } std::string BAD_JSON(const std::string& error_details) { + rapidjson::Value invalid; Message fail; fail.status = Message::STATUS_BAD_JSON; fail.error_details = error_details; - - FullMessage fail_response = FullMessage::responseMessage(&fail); - - return fail_response.getJson(); + return FullMessage::getResponse(fail, invalid); } diff --git a/src/rpc/message.h b/src/rpc/message.h index 2b7b61ab3..4cbc84fe4 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -28,27 +28,12 @@ #pragma once -#include "rapidjson/document.h" -#include "rpc/message_data_structs.h" +#include <rapidjson/document.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.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; \ - } +#include "rpc/message_data_structs.h" namespace cryptonote { @@ -58,6 +43,9 @@ namespace rpc class Message { + virtual void doToJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const + {} + public: static const char* STATUS_OK; static const char* STATUS_RETRY; @@ -69,9 +57,9 @@ namespace rpc virtual ~Message() { } - virtual rapidjson::Value toJson(rapidjson::Document& doc) const; + void toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const; - virtual void fromJson(rapidjson::Value& val); + virtual void fromJson(const rapidjson::Value& val); std::string status; std::string error_details; @@ -87,27 +75,18 @@ namespace rpc FullMessage(const std::string& json_string, bool request=false); - std::string getJson(); - std::string getRequestType() const; - rapidjson::Value& getMessage(); + const rapidjson::Value& getMessage() const; rapidjson::Value getMessageCopy(); - rapidjson::Value& getID(); - - void setID(rapidjson::Value& id); + const rapidjson::Value& getID() const; 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(); + static std::string getRequest(const std::string& request, const Message& message, unsigned id); + static std::string getResponse(const Message& message, const rapidjson::Value& id); private: FullMessage() = default; @@ -121,7 +100,7 @@ namespace rpc // 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_REQUEST(const std::string& request, const rapidjson::Value& id); std::string BAD_JSON(const std::string& error_details); diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 0eaa0ef0e..dcb804d3e 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -103,6 +103,7 @@ namespace cryptonote , rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")}) , rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false}) , rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false}) + , disable_rpc_ban({"disable-rpc-ban", rpc_args::tr("Do not ban hosts on RPC errors"), false, false}) {} const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } @@ -123,6 +124,7 @@ namespace cryptonote command_line::add_arg(desc, arg.rpc_ssl_ca_certificates); command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints); command_line::add_arg(desc, arg.rpc_ssl_allow_chained); + command_line::add_arg(desc, arg.disable_rpc_ban); if (any_cert_option) command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert); } @@ -136,6 +138,7 @@ namespace cryptonote config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address); config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6); config.require_ipv4 = !command_line::get_arg(vm, arg.rpc_ignore_ipv4); + config.disable_rpc_ban = command_line::get_arg(vm, arg.disable_rpc_ban); if (!config.bind_ip.empty()) { // always parse IP here for error consistency diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index bdb9c70d5..ac6eb2744 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -65,6 +65,7 @@ namespace cryptonote const command_line::arg_descriptor<std::vector<std::string>> rpc_ssl_allowed_fingerprints; const command_line::arg_descriptor<bool> rpc_ssl_allow_chained; const command_line::arg_descriptor<bool> rpc_ssl_allow_any_cert; + const command_line::arg_descriptor<bool> disable_rpc_ban; }; // `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration @@ -85,5 +86,6 @@ namespace cryptonote std::vector<std::string> access_control_origins; boost::optional<tools::login> login; // currently `boost::none` if unspecified by user epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + bool disable_rpc_ban = false; }; } diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt index 28b775a37..0b231f156 100644 --- a/src/serialization/CMakeLists.txt +++ b/src/serialization/CMakeLists.txt @@ -44,6 +44,7 @@ target_link_libraries(serialization LINK_PRIVATE cryptonote_core cryptonote_protocol + epee ${Boost_CHRONO_LIBRARY} ${Boost_REGEX_LIBRARY} ${Boost_SYSTEM_LIBRARY} diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index ea67209dc..e98ba0483 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -109,9 +109,14 @@ namespace } } -void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src) { - val = rapidjson::Value(i.c_str(), doc.GetAllocator()); + src.Accept(dest); +} + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const boost::string_ref i) +{ + dest.String(i.data(), i.size()); } void fromJsonValue(const rapidjson::Value& val, std::string& str) @@ -124,9 +129,9 @@ void fromJsonValue(const rapidjson::Value& val, std::string& str) str = val.GetString(); } -void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i) { - val.SetBool(i); + dest.Bool(i); } void fromJsonValue(const rapidjson::Value& val, bool& b) @@ -163,9 +168,9 @@ void fromJsonValue(const rapidjson::Value& val, short& i) to_int(val, i); } -void toJsonValue(rapidjson::Document& doc, const unsigned int i, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned int i) { - val = rapidjson::Value(i); + dest.Uint(i); } void fromJsonValue(const rapidjson::Value& val, unsigned int& i) @@ -173,9 +178,9 @@ void fromJsonValue(const rapidjson::Value& val, unsigned int& i) to_uint(val, i); } -void toJsonValue(rapidjson::Document& doc, const int i, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int i) { - val = rapidjson::Value(i); + dest.Int(i); } void fromJsonValue(const rapidjson::Value& val, int& i) @@ -183,10 +188,10 @@ 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) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i) { - static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "precision loss"); - val = rapidjson::Value(std::uint64_t(i)); + static_assert(std::numeric_limits<unsigned long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad uint64 conversion"); + dest.Uint64(i); } void fromJsonValue(const rapidjson::Value& val, unsigned long long& i) @@ -194,10 +199,11 @@ 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) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i) { - static_assert(!precision_loss<long long, std::int64_t>(), "precision loss"); - val = rapidjson::Value(std::int64_t(i)); + static_assert(std::numeric_limits<std::uint64_t>::min() <= std::numeric_limits<long long>::min(), "bad int64 conversion"); + static_assert(std::numeric_limits<long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad int64 conversion"); + dest.Int64(i); } void fromJsonValue(const rapidjson::Value& val, long long& i) @@ -215,17 +221,19 @@ void fromJsonValue(const rapidjson::Value& val, long& i) to_int64(val, i); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx) { - val.SetObject(); + dest.StartObject(); - 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, inputs, tx.vin); - INSERT_INTO_JSON_OBJECT(val, doc, outputs, 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, ringct, tx.rct_signatures); + INSERT_INTO_JSON_OBJECT(dest, version, tx.version); + INSERT_INTO_JSON_OBJECT(dest, unlock_time, tx.unlock_time); + INSERT_INTO_JSON_OBJECT(dest, inputs, tx.vin); + INSERT_INTO_JSON_OBJECT(dest, outputs, tx.vout); + INSERT_INTO_JSON_OBJECT(dest, extra, tx.extra); + INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures); + INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures); + + dest.EndObject(); } @@ -245,17 +253,19 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, major_version, b.major_version); + INSERT_INTO_JSON_OBJECT(dest, minor_version, b.minor_version); + INSERT_INTO_JSON_OBJECT(dest, timestamp, b.timestamp); + INSERT_INTO_JSON_OBJECT(dest, prev_id, b.prev_id); + INSERT_INTO_JSON_OBJECT(dest, nonce, b.nonce); + INSERT_INTO_JSON_OBJECT(dest, miner_tx, b.miner_tx); + INSERT_INTO_JSON_OBJECT(dest, tx_hashes, b.tx_hashes); - 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); + dest.EndObject(); } @@ -275,35 +285,34 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b) GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin) { - val.SetObject(); - + dest.StartObject(); struct add_input { using result_type = void; - rapidjson::Document& doc; - rapidjson::Value& val; + rapidjson::Writer<rapidjson::StringBuffer>& dest; void operator()(cryptonote::txin_to_key const& input) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_key, input); + INSERT_INTO_JSON_OBJECT(dest, to_key, input); } void operator()(cryptonote::txin_gen const& input) const { - INSERT_INTO_JSON_OBJECT(val, doc, gen, input); + INSERT_INTO_JSON_OBJECT(dest, gen, input); } void operator()(cryptonote::txin_to_script const& input) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_script, input); + INSERT_INTO_JSON_OBJECT(dest, to_script, input); } void operator()(cryptonote::txin_to_scripthash const& input) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, input); + INSERT_INTO_JSON_OBJECT(dest, to_scripthash, input); } }; - boost::apply_visitor(add_input{doc, val}, txin); + boost::apply_visitor(add_input{dest}, txin); + dest.EndObject(); } @@ -348,13 +357,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin) } } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, height, txin.height); -} + INSERT_INTO_JSON_OBJECT(dest, height, txin.height); + dest.EndObject(); +} void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin) { @@ -366,13 +376,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin) GET_FROM_JSON_OBJECT(val, txin.height, height); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, prev, txin.prev); + INSERT_INTO_JSON_OBJECT(dest, prevout, txin.prevout); + INSERT_INTO_JSON_OBJECT(dest, sigset, txin.sigset); - 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); + dest.EndObject(); } @@ -388,14 +400,17 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin GET_FROM_JSON_OBJECT(val, txin.sigset, sigset); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val) + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, prev, txin.prev); + INSERT_INTO_JSON_OBJECT(dest, prevout, txin.prevout); + INSERT_INTO_JSON_OBJECT(dest, script, txin.script); + INSERT_INTO_JSON_OBJECT(dest, sigset, txin.sigset); - 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); + dest.EndObject(); } @@ -412,15 +427,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& GET_FROM_JSON_OBJECT(val, txin.sigset, sigset); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin) { - val.SetObject(); + dest.StartObject(); - 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, key_image, txin.k_image); -} + INSERT_INTO_JSON_OBJECT(dest, amount, txin.amount); + INSERT_INTO_JSON_OBJECT(dest, key_offsets, txin.key_offsets); + INSERT_INTO_JSON_OBJECT(dest, key_image, txin.k_image); + dest.EndObject(); +} void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin) { @@ -434,14 +450,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin) GET_FROM_JSON_OBJECT(val, txin.k_image, key_image); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val) + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, keys, txout.keys); - INSERT_INTO_JSON_OBJECT(val, doc, script, txout.script); -} + INSERT_INTO_JSON_OBJECT(dest, keys, txout.keys); + INSERT_INTO_JSON_OBJECT(dest, script, txout.script); + dest.EndObject(); +} void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout) { @@ -454,13 +472,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txo GET_FROM_JSON_OBJECT(val, txout.script, script); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val) + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, hash, txout.hash); -} + INSERT_INTO_JSON_OBJECT(dest, hash, txout.hash); + dest.EndObject(); +} void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout) { @@ -472,13 +492,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& GET_FROM_JSON_OBJECT(val, txout.hash, hash); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val) + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, key, txout.key); -} + INSERT_INTO_JSON_OBJECT(dest, key, txout.key); + dest.EndObject(); +} void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout) { @@ -490,33 +512,32 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout) GET_FROM_JSON_OBJECT(val, txout.key, key); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout) { - val.SetObject(); - - INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount); + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, amount, txout.amount); struct add_output { using result_type = void; - rapidjson::Document& doc; - rapidjson::Value& val; + rapidjson::Writer<rapidjson::StringBuffer>& dest; void operator()(cryptonote::txout_to_key const& output) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_key, output); + INSERT_INTO_JSON_OBJECT(dest, to_key, output); } void operator()(cryptonote::txout_to_script const& output) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_script, output); + INSERT_INTO_JSON_OBJECT(dest, to_script, output); } void operator()(cryptonote::txout_to_scripthash const& output) const { - INSERT_INTO_JSON_OBJECT(val, doc, to_scripthash, output); + INSERT_INTO_JSON_OBJECT(dest, to_scripthash, output); } }; - boost::apply_visitor(add_output{doc, val}, txout.target); + boost::apply_visitor(add_output{dest}, txout.target); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) @@ -559,37 +580,39 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) } } -void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, incoming, info.incoming); + INSERT_INTO_JSON_OBJECT(dest, localhost, info.localhost); + INSERT_INTO_JSON_OBJECT(dest, local_ip, info.local_ip); + INSERT_INTO_JSON_OBJECT(dest, address_type, info.address_type); - 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, address_type, info.address_type); + INSERT_INTO_JSON_OBJECT(dest, ip, info.ip); + INSERT_INTO_JSON_OBJECT(dest, port, info.port); + INSERT_INTO_JSON_OBJECT(dest, rpc_port, info.rpc_port); + INSERT_INTO_JSON_OBJECT(dest, rpc_credits_per_hash, info.rpc_credits_per_hash); - INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip); - INSERT_INTO_JSON_OBJECT(val, doc, port, info.port); - INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, info.rpc_port); - INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, info.rpc_credits_per_hash); + INSERT_INTO_JSON_OBJECT(dest, peer_id, info.peer_id); - INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id); + INSERT_INTO_JSON_OBJECT(dest, recv_count, info.recv_count); + INSERT_INTO_JSON_OBJECT(dest, recv_idle_time, info.recv_idle_time); - 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(dest, send_count, info.send_count); + INSERT_INTO_JSON_OBJECT(dest, send_idle_time, info.send_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(dest, state, info.state); - INSERT_INTO_JSON_OBJECT(val, doc, state, info.state); + INSERT_INTO_JSON_OBJECT(dest, live_time, info.live_time); - INSERT_INTO_JSON_OBJECT(val, doc, live_time, info.live_time); + INSERT_INTO_JSON_OBJECT(dest, avg_download, info.avg_download); + INSERT_INTO_JSON_OBJECT(dest, current_download, info.current_download); - 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(dest, avg_upload, info.avg_upload); + INSERT_INTO_JSON_OBJECT(dest, current_upload, info.current_upload); - INSERT_INTO_JSON_OBJECT(val, doc, avg_upload, info.avg_upload); - INSERT_INTO_JSON_OBJECT(val, doc, current_upload, info.current_upload); + dest.EndObject(); } @@ -629,12 +652,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, blob, tx.blob); + INSERT_INTO_JSON_OBJECT(dest, prunable_hash, tx.prunable_hash); - INSERT_INTO_JSON_OBJECT(val, doc, blob, tx.blob); - INSERT_INTO_JSON_OBJECT(val, doc, prunable_hash, tx.prunable_hash); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx) @@ -648,12 +673,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx) GET_FROM_JSON_OBJECT(val, tx.prunable_hash, prunable_hash); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block); - INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.txs); + INSERT_INTO_JSON_OBJECT(dest, block, blk.block); + INSERT_INTO_JSON_OBJECT(dest, transactions, blk.txs); + + dest.EndObject(); } @@ -668,12 +695,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry GET_FROM_JSON_OBJECT(val, blk.txs, transactions); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, block, blk.block); + INSERT_INTO_JSON_OBJECT(dest, transactions, blk.transactions); - INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block); - INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.transactions); + dest.EndObject(); } @@ -688,13 +717,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_tran GET_FROM_JSON_OBJECT(val, blk.transactions, transactions); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, height, tx_info.height); + INSERT_INTO_JSON_OBJECT(dest, in_pool, tx_info.in_pool); + INSERT_INTO_JSON_OBJECT(dest, transaction, tx_info.transaction); - 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); + dest.EndObject(); } @@ -710,12 +741,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_inf 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) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, amount_index, out.amount_index); - INSERT_INTO_JSON_OBJECT(val, doc, key, out.key); + INSERT_INTO_JSON_OBJECT(dest, amount_index, out.amount_index); + INSERT_INTO_JSON_OBJECT(dest, key, out.key); + + dest.EndObject(); } @@ -730,12 +763,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_ GET_FROM_JSON_OBJECT(val, out.key, key); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, amount, out.amount); + INSERT_INTO_JSON_OBJECT(dest, outputs, out.outputs); - INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount); - INSERT_INTO_JSON_OBJECT(val, doc, outputs, out.outputs); + dest.EndObject(); } @@ -750,17 +785,19 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_ran GET_FROM_JSON_OBJECT(val, out.outputs, outputs); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, id, peer.id); + INSERT_INTO_JSON_OBJECT(dest, ip, peer.ip); + INSERT_INTO_JSON_OBJECT(dest, port, peer.port); + INSERT_INTO_JSON_OBJECT(dest, rpc_port, peer.rpc_port); + INSERT_INTO_JSON_OBJECT(dest, rpc_credits_per_hash, peer.rpc_credits_per_hash); + INSERT_INTO_JSON_OBJECT(dest, last_seen, peer.last_seen); + INSERT_INTO_JSON_OBJECT(dest, pruning_seed, peer.pruning_seed); - 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, rpc_port, peer.rpc_port); - INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, peer.rpc_credits_per_hash); - INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen); - INSERT_INTO_JSON_OBJECT(val, doc, pruning_seed, peer.pruning_seed); + dest.EndObject(); } @@ -780,25 +817,27 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer) GET_FROM_JSON_OBJECT(val, peer.pruning_seed, pruning_seed); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx) { - val.SetObject(); + dest.StartObject(); - 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, weight, tx.weight); - 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); - INSERT_INTO_JSON_OBJECT(val, doc, double_spend_seen, tx.double_spend_seen); + INSERT_INTO_JSON_OBJECT(dest, tx, tx.tx); + INSERT_INTO_JSON_OBJECT(dest, tx_hash, tx.tx_hash); + INSERT_INTO_JSON_OBJECT(dest, blob_size, tx.blob_size); + INSERT_INTO_JSON_OBJECT(dest, weight, tx.weight); + INSERT_INTO_JSON_OBJECT(dest, fee, tx.fee); + INSERT_INTO_JSON_OBJECT(dest, max_used_block_hash, tx.max_used_block_hash); + INSERT_INTO_JSON_OBJECT(dest, max_used_block_height, tx.max_used_block_height); + INSERT_INTO_JSON_OBJECT(dest, kept_by_block, tx.kept_by_block); + INSERT_INTO_JSON_OBJECT(dest, last_failed_block_hash, tx.last_failed_block_hash); + INSERT_INTO_JSON_OBJECT(dest, last_failed_block_height, tx.last_failed_block_height); + INSERT_INTO_JSON_OBJECT(dest, receive_time, tx.receive_time); + INSERT_INTO_JSON_OBJECT(dest, last_relayed_time, tx.last_relayed_time); + INSERT_INTO_JSON_OBJECT(dest, relayed, tx.relayed); + INSERT_INTO_JSON_OBJECT(dest, do_not_relay, tx.do_not_relay); + INSERT_INTO_JSON_OBJECT(dest, double_spend_seen, tx.double_spend_seen); + + dest.EndObject(); } @@ -825,18 +864,20 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx) GET_FROM_JSON_OBJECT(val, tx.double_spend_seen, double_spend_seen); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, version, info.version); + INSERT_INTO_JSON_OBJECT(dest, enabled, info.enabled); + INSERT_INTO_JSON_OBJECT(dest, window, info.window); + INSERT_INTO_JSON_OBJECT(dest, votes, info.votes); + INSERT_INTO_JSON_OBJECT(dest, threshold, info.threshold); + INSERT_INTO_JSON_OBJECT(dest, voting, info.voting); + INSERT_INTO_JSON_OBJECT(dest, state, info.state); + INSERT_INTO_JSON_OBJECT(dest, earliest_height, info.earliest_height); - 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); + dest.EndObject(); } @@ -857,14 +898,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& 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) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out) { - val.SetObject(); + dest.StartObject(); - 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); + INSERT_INTO_JSON_OBJECT(dest, amount, out.amount); + INSERT_INTO_JSON_OBJECT(dest, total_count, out.total_count); + INSERT_INTO_JSON_OBJECT(dest, unlocked_count, out.unlocked_count); + INSERT_INTO_JSON_OBJECT(dest, recent_count, out.recent_count); + + dest.EndObject(); } @@ -881,12 +924,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_c 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) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, amount, out.amount); + INSERT_INTO_JSON_OBJECT(dest, index, out.index); - INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount); - INSERT_INTO_JSON_OBJECT(val, doc, index, out.index); + dest.EndObject(); } @@ -901,13 +946,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_a GET_FROM_JSON_OBJECT(val, out.index, index); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, key, out.key); + INSERT_INTO_JSON_OBJECT(dest, mask, out.mask); + INSERT_INTO_JSON_OBJECT(dest, unlocked, out.unlocked); - 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); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out) @@ -922,13 +969,15 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err) { - val.SetObject(); + dest.StartObject(); - 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); + INSERT_INTO_JSON_OBJECT(dest, code, err.code); + INSERT_INTO_JSON_OBJECT(dest, error_str, err.error_str); + INSERT_INTO_JSON_OBJECT(dest, message, err.message); + + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error) @@ -943,20 +992,22 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error) GET_FROM_JSON_OBJECT(val, error.message, message); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, major_version, response.major_version); + INSERT_INTO_JSON_OBJECT(dest, minor_version, response.minor_version); + INSERT_INTO_JSON_OBJECT(dest, timestamp, response.timestamp); + INSERT_INTO_JSON_OBJECT(dest, prev_id, response.prev_id); + INSERT_INTO_JSON_OBJECT(dest, nonce, response.nonce); + INSERT_INTO_JSON_OBJECT(dest, height, response.height); + INSERT_INTO_JSON_OBJECT(dest, depth, response.depth); + INSERT_INTO_JSON_OBJECT(dest, hash, response.hash); + INSERT_INTO_JSON_OBJECT(dest, difficulty, response.difficulty); + INSERT_INTO_JSON_OBJECT(dest, reward, response.reward); - 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); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response) @@ -978,34 +1029,36 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp GET_FROM_JSON_OBJECT(val, response.reward, reward); } -void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& sig) { using boost::adaptors::transform; - val.SetObject(); + dest.StartObject(); const auto just_mask = [] (rct::ctkey const& key) -> rct::key const& { return key.mask; }; - INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type); - INSERT_INTO_JSON_OBJECT(val, doc, encrypted, sig.ecdhInfo); - INSERT_INTO_JSON_OBJECT(val, doc, commitments, transform(sig.outPk, just_mask)); - INSERT_INTO_JSON_OBJECT(val, doc, fee, sig.txnFee); + INSERT_INTO_JSON_OBJECT(dest, type, sig.type); + INSERT_INTO_JSON_OBJECT(dest, encrypted, sig.ecdhInfo); + INSERT_INTO_JSON_OBJECT(dest, commitments, transform(sig.outPk, just_mask)); + INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); // prunable { - rapidjson::Value prunable; - prunable.SetObject(); + dest.Key("prunable"); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(prunable, doc, range_proofs, sig.p.rangeSigs); - INSERT_INTO_JSON_OBJECT(prunable, doc, bulletproofs, sig.p.bulletproofs); - INSERT_INTO_JSON_OBJECT(prunable, doc, mlsags, sig.p.MGs); - INSERT_INTO_JSON_OBJECT(prunable, doc, pseudo_outs, sig.get_pseudo_outs()); + INSERT_INTO_JSON_OBJECT(dest, range_proofs, sig.p.rangeSigs); + INSERT_INTO_JSON_OBJECT(dest, bulletproofs, sig.p.bulletproofs); + INSERT_INTO_JSON_OBJECT(dest, mlsags, sig.p.MGs); + INSERT_INTO_JSON_OBJECT(dest, pseudo_outs, sig.get_pseudo_outs()); - val.AddMember("prunable", prunable, doc.GetAllocator()); + dest.EndObject(); } + + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) @@ -1046,12 +1099,12 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) } } -void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple) { - val.SetObject(); - - INSERT_INTO_JSON_OBJECT(val, doc, mask, tuple.mask); - INSERT_INTO_JSON_OBJECT(val, doc, amount, tuple.amount); + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, mask, tuple.mask); + INSERT_INTO_JSON_OBJECT(dest, amount, tuple.amount); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple) @@ -1065,14 +1118,14 @@ void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple) GET_FROM_JSON_OBJECT(val, tuple.amount, amount); } -void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, asig, sig.asig); + INSERT_INTO_JSON_OBJECT(dest, asig, sig.asig); + INSERT_INTO_JSON_OBJECT(dest, Ci, epee::span<const rct::key>{sig.Ci}); - std::vector<rct::key> keyVector(sig.Ci, std::end(sig.Ci)); - INSERT_INTO_JSON_OBJECT(val, doc, Ci, keyVector); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) @@ -1102,22 +1155,24 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) } } -void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, V, p.V); + INSERT_INTO_JSON_OBJECT(dest, A, p.A); + INSERT_INTO_JSON_OBJECT(dest, S, p.S); + INSERT_INTO_JSON_OBJECT(dest, T1, p.T1); + INSERT_INTO_JSON_OBJECT(dest, T2, p.T2); + INSERT_INTO_JSON_OBJECT(dest, taux, p.taux); + INSERT_INTO_JSON_OBJECT(dest, mu, p.mu); + INSERT_INTO_JSON_OBJECT(dest, L, p.L); + INSERT_INTO_JSON_OBJECT(dest, R, p.R); + INSERT_INTO_JSON_OBJECT(dest, a, p.a); + INSERT_INTO_JSON_OBJECT(dest, b, p.b); + INSERT_INTO_JSON_OBJECT(dest, t, p.t); - INSERT_INTO_JSON_OBJECT(val, doc, V, p.V); - INSERT_INTO_JSON_OBJECT(val, doc, A, p.A); - INSERT_INTO_JSON_OBJECT(val, doc, S, p.S); - INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1); - INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2); - INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux); - INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu); - INSERT_INTO_JSON_OBJECT(val, doc, L, p.L); - INSERT_INTO_JSON_OBJECT(val, doc, R, p.R); - INSERT_INTO_JSON_OBJECT(val, doc, a, p.a); - INSERT_INTO_JSON_OBJECT(val, doc, b, p.b); - INSERT_INTO_JSON_OBJECT(val, doc, t, p.t); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p) @@ -1141,17 +1196,15 @@ void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p) GET_FROM_JSON_OBJECT(val, p.t, t); } -void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig) { - val.SetObject(); - - std::vector<rct::key> keyVector(sig.s0, std::end(sig.s0)); - INSERT_INTO_JSON_OBJECT(val, doc, s0, keyVector); + dest.StartObject(); - keyVector.assign(sig.s1, std::end(sig.s1)); - INSERT_INTO_JSON_OBJECT(val, doc, s1, keyVector); + INSERT_INTO_JSON_OBJECT(dest, s0, epee::span<const rct::key>{sig.s0}); + INSERT_INTO_JSON_OBJECT(dest, s1, epee::span<const rct::key>{sig.s1}); + INSERT_INTO_JSON_OBJECT(dest, ee, sig.ee); - INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig) @@ -1188,12 +1241,14 @@ void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig) GET_FROM_JSON_OBJECT(val, sig.ee, ee); } -void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig) { - val.SetObject(); + dest.StartObject(); - INSERT_INTO_JSON_OBJECT(val, doc, ss, sig.ss); - INSERT_INTO_JSON_OBJECT(val, doc, cc, sig.cc); + INSERT_INTO_JSON_OBJECT(dest, ss, sig.ss); + INSERT_INTO_JSON_OBJECT(dest, cc, sig.cc); + + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig) @@ -1207,32 +1262,34 @@ void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig) 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, mainnet, info.mainnet); - INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet); - INSERT_INTO_JSON_OBJECT(val, doc, stagenet, info.stagenet); - INSERT_INTO_JSON_OBJECT(val, doc, nettype, info.nettype); - 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, block_weight_limit, info.block_weight_limit); - INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median); - INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median); - INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info) +{ + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, height, info.height); + INSERT_INTO_JSON_OBJECT(dest, target_height, info.target_height); + INSERT_INTO_JSON_OBJECT(dest, difficulty, info.difficulty); + INSERT_INTO_JSON_OBJECT(dest, target, info.target); + INSERT_INTO_JSON_OBJECT(dest, tx_count, info.tx_count); + INSERT_INTO_JSON_OBJECT(dest, tx_pool_size, info.tx_pool_size); + INSERT_INTO_JSON_OBJECT(dest, alt_blocks_count, info.alt_blocks_count); + INSERT_INTO_JSON_OBJECT(dest, outgoing_connections_count, info.outgoing_connections_count); + INSERT_INTO_JSON_OBJECT(dest, incoming_connections_count, info.incoming_connections_count); + INSERT_INTO_JSON_OBJECT(dest, white_peerlist_size, info.white_peerlist_size); + INSERT_INTO_JSON_OBJECT(dest, grey_peerlist_size, info.grey_peerlist_size); + INSERT_INTO_JSON_OBJECT(dest, mainnet, info.mainnet); + INSERT_INTO_JSON_OBJECT(dest, testnet, info.testnet); + INSERT_INTO_JSON_OBJECT(dest, stagenet, info.stagenet); + INSERT_INTO_JSON_OBJECT(dest, nettype, info.nettype); + INSERT_INTO_JSON_OBJECT(dest, top_block_hash, info.top_block_hash); + INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty, info.cumulative_difficulty); + INSERT_INTO_JSON_OBJECT(dest, block_size_limit, info.block_size_limit); + INSERT_INTO_JSON_OBJECT(dest, block_weight_limit, info.block_weight_limit); + INSERT_INTO_JSON_OBJECT(dest, block_size_median, info.block_size_median); + INSERT_INTO_JSON_OBJECT(dest, block_weight_median, info.block_weight_median); + INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time); + + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info) @@ -1266,14 +1323,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.start_time, start_time); } -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val) +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist) { - val.SetObject(); + dest.StartObject(); + + INSERT_INTO_JSON_OBJECT(dest, distribution, dist.data.distribution); + INSERT_INTO_JSON_OBJECT(dest, amount, dist.amount); + INSERT_INTO_JSON_OBJECT(dest, start_height, dist.data.start_height); + INSERT_INTO_JSON_OBJECT(dest, base, dist.data.base); - INSERT_INTO_JSON_OBJECT(val, doc, distribution, dist.data.distribution); - INSERT_INTO_JSON_OBJECT(val, doc, amount, dist.amount); - INSERT_INTO_JSON_OBJECT(val, doc, start_height, dist.data.start_height); - INSERT_INTO_JSON_OBJECT(val, doc, base, dist.data.base); + dest.EndObject(); } void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist) diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 5ef75b863..a1a5105d5 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -28,8 +28,13 @@ #pragma once -#include "string_tools.h" -#include "rapidjson/document.h" +#include <boost/utility/string_ref.hpp> +#include <cstring> +#include "string_tools.h" // out of order because windows.h GetObject macro conflicts with GenericValue<..>::GetObject() +#include <rapidjson/document.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> + #include "cryptonote_basic/cryptonote_basic.h" #include "rpc/message_data_structs.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" @@ -44,10 +49,9 @@ } \ } 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 INSERT_INTO_JSON_OBJECT(dest, key, source) \ + dest.Key(#key, sizeof(#key) - 1); \ + cryptonote::json::toJsonValue(dest, source); #define GET_FROM_JSON_OBJECT(source, dst, key) \ OBJECT_HAS_MEMBER_OR_THROW(source, #key) \ @@ -114,16 +118,24 @@ inline constexpr bool is_to_hex() return std::is_pod<Type>() && !std::is_integral<Type>(); } +// POD to json key +template <class Type> +inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod) +{ + const auto hex = epee::to_hex::array(pod); + dest.Key(hex.data(), hex.size()); +} // 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) +inline typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod) { - value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator()); + const auto hex = epee::to_hex::array(pod); + dest.String(hex.data(), hex.size()); } template <class Type> -typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t) +inline typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t) { if (!val.IsString()) { @@ -139,10 +151,16 @@ typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson:: } } -void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src); + +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, boost::string_ref i); +inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const std::string& i) +{ + toJsonValue(dest, boost::string_ref{i}); +} void fromJsonValue(const rapidjson::Value& val, std::string& str); -void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, bool i); void fromJsonValue(const rapidjson::Value& val, bool& b); // integers overloads for toJsonValue are not needed for standard promotions @@ -157,144 +175,144 @@ 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned i); void fromJsonValue(const rapidjson::Value& val, unsigned& i); -void toJsonValue(rapidjson::Document& doc, const int, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const int); void fromJsonValue(const rapidjson::Value& val, int& i); -void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i); void fromJsonValue(const rapidjson::Value& val, unsigned long long& i); -void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i); 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); +inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long i) { + toJsonValue(dest, static_cast<unsigned long long>(i)); } 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); +inline void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long i) { + toJsonValue(dest, static_cast<long long>(i)); } void fromJsonValue(const rapidjson::Value& val, long& i); // end integers -void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::transaction& tx); void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx); -void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block& b); void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b); -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_v& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin); -void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_gen& txin); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_script& txin); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_scripthash& txin); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txin_to_key& txin); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_target_v& txout); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_script& txout); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_scripthash& txout); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::txout_to_key& txout); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_out& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout); -void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::connection_info& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info); -void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::tx_blob_entry& tx); void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx); -void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::block_complete_entry& blk); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::block_with_transactions& blk); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::transaction_info& tx_info); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_and_amount_index& out); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::amount_with_random_outputs& out); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::peer& peer); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::tx_in_pool& tx); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::hard_fork_info& info); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_count& out); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_amount_and_index& out); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_key_mask_unlocked& out); 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 toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::error& err); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error); -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::BlockHeaderResponse& response); 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::Writer<rapidjson::StringBuffer>& dest, const rct::rctSig& i); +void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig); -void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::ecdhTuple& tuple); void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple); -void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::rangeSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); -void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::Bulletproof& p); void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p); -void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::boroSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); -void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rct::mgSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::DaemonInfo& info); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); -void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_distribution& dist, rapidjson::Value& val); +void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const cryptonote::rpc::output_distribution& dist); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_distribution& dist); 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); +typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map); 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); +typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec); template <typename Vec> typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec); @@ -304,24 +322,22 @@ typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJson // 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) +inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Map& map) { - val.SetObject(); - - auto& al = doc.GetAllocator(); + using key_type = typename Map::key_type; + static_assert(std::is_same<std::string, key_type>() || is_to_hex<key_type>(), "invalid map key type"); + dest.StartObject(); 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); + toJsonKey(dest, i.first); + toJsonValue(dest, i.second); } + dest.EndObject(); } template <typename Map> -typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map) +inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map) { if (!val.IsObject()) { @@ -342,20 +358,16 @@ typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonVal } 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) +inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Vec &vec) { - val.SetArray(); - + dest.StartArray(); for (const auto& t : vec) - { - rapidjson::Value v; - toJsonValue(doc, t, v); - val.PushBack(v, doc.GetAllocator()); - } + toJsonValue(dest, t); + dest.EndArray(vec.size()); } template <typename Vec> -typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec) +inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec) { if (!val.IsArray()) { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 74e793669..9569863b3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -180,8 +180,8 @@ namespace const char* USAGE_PAYMENT_ID("payment_id"); const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]"); const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]"); - const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]"); - const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); + const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]"); + const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]"); const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]"); const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]"); @@ -1691,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args) rings.push_back({key_image, ring}); else if (!m_wallet->get_rings(txid, rings)) { - fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); + fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1"); return true; } @@ -3138,13 +3138,13 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("locked_sweep_all", boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1), tr(USAGE_LOCKED_SWEEP_ALL), - tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); + tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr(USAGE_SWEEP_ALL), - tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); + tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1), tr(USAGE_SWEEP_BELOW), @@ -5469,20 +5469,28 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); // failure ok tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + tx_extra_pub_key extra_pub_key; + crypto::hash8 payment_id8 = crypto::null_hash8; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_pub_key)) { - crypto::hash payment_id = crypto::null_hash; - crypto::hash8 payment_id8 = crypto::null_hash8; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + const crypto::public_key &tx_pub_key = extra_pub_key.pub_key; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - if (payment_id8 != crypto::null_hash8) - message_writer() << - tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); - } - else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - message_writer(console_color_red, false) << - tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead."); - } + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + m_wallet->get_account().get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_wallet->get_account().get_keys().m_view_secret_key); + } + } + } + + if (payment_id8 != crypto::null_hash8) + message_writer() << + tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead"); + + crypto::hash payment_id = crypto::null_hash; + if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + message_writer(console_color_red, false) << + tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead."); } if (unlock_time && !cryptonote::is_coinbase(tx)) message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid); @@ -5576,6 +5584,14 @@ boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_reque //---------------------------------------------------------------------------------------------------- void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money) { + const uint64_t rfbh = m_wallet->get_refresh_from_block_height(); + std::string err; + const uint64_t dh = m_wallet->get_daemon_blockchain_height(err); + if (err.empty() && rfbh > dh) + { + message_writer(console_color_yellow, false) << tr("The wallet's refresh-from-block-height setting is higher than the daemon's height: this may mean your wallet will skip over transactions"); + } + // Key image sync after the first refresh if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) { return; @@ -5845,8 +5861,14 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args if (uses) { std::vector<uint64_t> heights; - for (const auto &e: td.m_uses) heights.push_back(e.first); - const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height); + uint64_t idx = 0; + for (const auto &e: td.m_uses) + { + heights.push_back(e.first); + if (e.first < td.m_spent_height) + ++idx; + } + const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, idx); extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second; } message_writer(td.m_spent ? console_color_magenta : console_color_green, false) << @@ -6015,7 +6037,7 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- -std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const +std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_idx) const { std::stringstream ostr; @@ -6023,7 +6045,7 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std:: blockchain_height = std::max(blockchain_height, h); for (size_t j = 0; j < heights.size(); ++j) - ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j]; + ostr << (j == highlight_idx ? " *" : " ") << heights[j]; // visualize the distribution, using the code by moneroexamples onion-monero-viewer const uint64_t resolution = 79; @@ -6033,20 +6055,23 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std:: uint64_t pos = (heights[j] * resolution) / blockchain_height; ring_str[pos] = 'o'; } - if (highlight_height < blockchain_height) + if (highlight_idx < heights.size() && heights[highlight_idx] < blockchain_height) { - uint64_t pos = (highlight_height * resolution) / blockchain_height; + uint64_t pos = (heights[highlight_idx] * resolution) / blockchain_height; ring_str[pos] = '*'; } return std::make_pair(ostr.str(), ring_str); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr) +bool simple_wallet::process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose) { uint32_t version; if (!try_connect_to_daemon(false, &version)) + { + fail_msg_writer() << tr("failed to connect to daemon"); return false; + } // available for RPC version 1.4 or higher if (version < MAKE_CORE_RPC_VERSION(1, 4)) return true; @@ -6062,7 +6087,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending { const cryptonote::transaction& tx = ptx_vector[n].tx; const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data; - ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx); + if (verbose) + ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx); // for each input std::vector<uint64_t> spent_key_height(tx.vin.size()); std::vector<crypto::hash> spent_key_txid (tx.vin.size()); @@ -6083,7 +6109,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending } const cryptonote::tx_source_entry& source = *sptr; - ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount); + if (verbose) + ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount); // convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets); // get block heights from which those ring member keys originated @@ -6095,6 +6122,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending req.outputs[j].index = absolute_offsets[j]; } COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res); + req.get_txid = true; req.client = cryptonote::make_rpc_payment_signature(m_wallet->get_rpc_client_secret_key()); bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res); err = interpret_rpc_response(r, res.status); @@ -6112,19 +6140,18 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return false; } } - ostr << tr("\nOriginating block heights: "); + if (verbose) + ostr << tr("\nOriginating block heights: "); spent_key_height[i] = res.outs[source.real_output].height; spent_key_txid [i] = res.outs[source.real_output].txid; std::vector<uint64_t> heights(absolute_offsets.size(), 0); - uint64_t highlight_height = std::numeric_limits<uint64_t>::max(); for (size_t j = 0; j < absolute_offsets.size(); ++j) { heights[j] = res.outs[j].height; - if (j == source.real_output) - highlight_height = heights[j]; } - std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height); - ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); + std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, source.real_output); + if (verbose) + ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); } // warn if rings contain keys originating from the same tx or temporally very close block heights bool are_keys_from_same_tx = false; @@ -6143,7 +6170,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending ostr << tr("\nWarning: Some input keys being spent are from ") << (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close")) - << tr(", which can break the anonymity of ring signature. Make sure this is intentional!"); + << tr(", which can break the anonymity of ring signatures. Make sure this is intentional!"); } ostr << ENDL; } @@ -6198,7 +6225,7 @@ void simple_wallet::check_for_inactivity_lock(bool user) m_in_command = true; if (!user) { - const std::string speech = tr("I locked your Monero wallet to protect you while you were away"); + const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help_advanced set\" to configure/disable"); std::vector<std::pair<std::string, size_t>> lines = tools::split_string_by_width(speech, 45); size_t max_len = 0; @@ -6584,11 +6611,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri float days = locked_blocks / 720.0f; prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days; } - if (m_wallet->print_ring_members()) - { - if (!print_ring_members(ptx_vector, prompt)) - return false; - } + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) + return false; bool default_ring_size = true; for (const auto &ptx: ptx_vector) { @@ -6852,7 +6876,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st std::set<uint32_t> subaddr_indices; if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=") { - if (!parse_subaddress_indices(local_args[0], subaddr_indices)) + if (local_args[0] == "index=all") + { + for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i) + subaddr_indices.insert(i); + } + else if (!parse_subaddress_indices(local_args[0], subaddr_indices)) { print_usage(); return true; @@ -7039,7 +7068,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st if (subaddr_indices.size() > 1) prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n"); } - if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt)) + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) return true; if (ptx_vector.size() > 1) { prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) % @@ -7283,7 +7312,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) uint64_t total_fee = ptx_vector[0].fee; uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount(); std::ostringstream prompt; - if (!print_ring_members(ptx_vector, prompt)) + if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members())) return true; prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) % print_money(total_sent) % @@ -8365,7 +8394,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); @@ -8795,6 +8824,8 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_) } } + m_in_manual_refresh.store(true, std::memory_order_relaxed); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); return refresh_main(start_height, reset_type, true); } //---------------------------------------------------------------------------------------------------- @@ -10028,7 +10059,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) try { - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index e401f5fda..4ba2793e0 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -270,12 +270,12 @@ namespace cryptonote bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message = std::string()); bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); 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); + bool process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose); std::string get_prompt() const; bool print_seed(bool encrypted); void key_images_sync_intern(); void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); - std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; + std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_idx = std::numeric_limits<uint64_t>::max()) const; bool freeze_thaw(const std::vector<std::string>& args, bool freeze); bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector); bool on_command(bool (simple_wallet::*cmd)(const std::vector<std::string>&), const std::vector<std::string> &args); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6200c7a1f..33047f703 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1671,6 +1671,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) delete t; } +uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const +{ + const size_t pubkey_size = 33; + const size_t encrypted_paymentid_size = 11; + const size_t extra_size = pubkey_size + encrypted_paymentid_size; + + return m_wallet->estimate_fee( + m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0), + m_wallet->use_fork_rules(4, 0), + 1, + m_wallet->get_min_ring_size() - 1, + destinations.size() + 1, + extra_size, + m_wallet->use_fork_rules(8, 0), + m_wallet->get_base_fee(), + m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))), + m_wallet->get_fee_quantization_mask()); +} + TransactionHistory *WalletImpl::history() { return m_history.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 331bf4b38..e46a62340 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,6 +166,8 @@ public: bool importKeyImages(const std::string &filename) override; virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; virtual Subaddress * subaddress() override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e543a115b..3945e55c9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -879,6 +879,14 @@ struct Wallet */ virtual void disposeTransaction(PendingTransaction * t) = 0; + /*! + * \brief Estimates transaction fee. + * \param destinations Vector consisting of <address, amount> pairs. + * \return Estimated fee. + */ + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index b7efdd75c..5e88ea788 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -39,6 +39,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" +#define V1TAG ((uint64_t)798237759845202) + static const char zerokey[8] = {0}; static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey }; @@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b) return va < vb ? -1 : va > vb; } -static std::string compress_ring(const std::vector<uint64_t> &ring) +static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag) { std::string s; + s += tools::get_varint_data(tag); for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } -static std::vector<uint64_t> decompress_ring(const std::string &s) +static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag) { std::vector<uint64_t> ring; int read = 0; @@ -81,6 +84,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s) std::string tmp(i, s.cend()); read = tools::read_varint(tmp.begin(), tmp.end(), out); THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring"); + if (tag) + { + if (tag != out) + return {}; + tag = 0; + continue; + } ring.push_back(out); } return ring; @@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename) return filename.string(); } -static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key) +static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { static const char salt[] = "ringdsb"; - uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)]; + uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)]; memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer + sizeof(key_image), &key, sizeof(key)); memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt)); + memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field)); crypto::hash hash; - crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data); + // if field is 0, backward compat mode: hash without the field + crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data); static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes"); crypto::chacha_iv iv; memcpy(&iv, &hash, CHACHA_IV_SIZE); return iv; } -static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string ciphertext; ciphertext.resize(plaintext.size() + sizeof(iv)); crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); @@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image return ciphertext; } -static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key); + return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field); } -static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string plaintext; THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); plaintext.resize(ciphertext.size() - sizeof(iv)); @@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); - std::string compressed_ring = compress_ring(relative_ring); - std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); + std::string compressed_ring = compress_ring(relative_ring, V1TAG); + std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1); data.mv_size = data_ciphertext.size(); data.mv_data = (void*)data_ciphertext.c_str(); int dbr = mdb_put(txn, dbi, &key, &data, 0); @@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto for (const crypto::key_image &key_image: key_images) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); @@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im tx_active = true; MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); dbr = mdb_get(txn, dbi_rings, &key, &data); @@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return false; THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); - std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key); - outs = decompress_ring(data_plaintext); + bool try_v0 = false; + std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1); + try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; } + catch(...) { try_v0 = true; } + if (try_v0) + { + data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0); + outs = decompress_ring(data_plaintext, 0); + } MDEBUG("Found ring for key image " << key_image << ":"); MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 870aa65ac..6d2fff17b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -44,6 +44,7 @@ using namespace epee; #include "cryptonote_config.h" +#include "cryptonote_core/tx_sanity_check.h" #include "wallet_rpc_helpers.h" #include "wallet2.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -149,6 +150,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; +boost::mutex tools::wallet2::default_daemon_address_lock; +std::string tools::wallet2::default_daemon_address = ""; + namespace { std::string get_default_ringdb_path() @@ -412,6 +416,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl daemon_port = get_config(nettype).RPC_DEFAULT_PORT; } + // if no daemon settings are given and we have a previous one, reuse that one + if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address)) + { + // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard + const std::string &def = tools::wallet2::get_default_daemon_address(); + if (!def.empty()) + daemon_address = def; + } + if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); @@ -591,6 +604,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); crypto::public_key pkey; + if (viewkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(viewkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } @@ -607,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); crypto::public_key pkey; + if (spendkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(spendkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } @@ -879,20 +896,6 @@ uint8_t get_bulletproof_fork() return 8; } -uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) -{ - if (use_per_byte_fee) - { - const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); - } - else - { - const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); - } -} - uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) { if (use_per_byte_fee) @@ -1186,6 +1189,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), + m_decrypt_keys_lockers(0), m_unattended(unattended), m_devices_registered(false), m_device_last_key_image_sync(0), @@ -1312,8 +1316,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u m_node_rpc_proxy.invalidate(); } - MINFO("setting daemon to " << get_daemon_address()); - return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options)); + const std::string address = get_daemon_address(); + MINFO("setting daemon to " << address); + bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options)); + if (ret) + { + CRITICAL_REGION_LOCAL(default_daemon_address_lock); + default_daemon_address = address; + } + return ret; } //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) @@ -1842,7 +1853,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); - std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index + + // per receiving subaddress index + std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; + std::unordered_map<cryptonote::subaddress_index, amounts_container> tx_amounts_individual_outs; + crypto::public_key tx_pub_key = null_pkey; bool notify = false; @@ -1971,6 +1986,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -1994,6 +2013,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -2010,6 +2033,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -2118,6 +2145,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount, error::wallet_internal_error, "Unexpected values of new and old outputs"); tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; + + amounts_container& tx_amounts_this_out = tx_amounts_individual_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines + auto amount_iterator = std::find(tx_amounts_this_out.begin(), tx_amounts_this_out.end(), tx_scan_info[o].amount); + THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_amounts_this_out.end(), + error::wallet_internal_error, "Unexpected values of new and old outputs"); + tx_amounts_this_out.erase(amount_iterator); } else { @@ -2183,6 +2216,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } } + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs.size() != tx_amounts_individual_outs.size(), error::wallet_internal_error, "Inconsistent size of output arrays"); + uint64_t tx_money_spent_in_ins = 0; // The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679 @@ -2286,6 +2321,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (subaddr_account && i->first.major == *subaddr_account) { sub_change += i->second; + tx_amounts_individual_outs.erase(i->first); i = tx_money_got_in_outs.erase(i); } else @@ -2363,6 +2399,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_tx_hash = txid; payment.m_fee = fee; payment.m_amount = i.second; + payment.m_amounts = tx_amounts_individual_outs[i.first]; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; payment.m_timestamp = ts; @@ -2847,7 +2884,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe } //---------------------------------------------------------------------------------------------------- -void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed) +void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed) { MTRACE("update_pool_state start"); @@ -2907,7 +2944,6 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; // the inputs aren't spent anymore, since the tx failed - remove_rings(pit->second.m_tx); for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) { if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) @@ -3037,7 +3073,7 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen)); + process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); } else { @@ -3068,14 +3104,14 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b MTRACE("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs) +void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs) { const time_t now = time(NULL); for (const auto &e: txs) { - const cryptonote::transaction &tx = e.first; - const bool double_spend_seen = e.second; - const crypto::hash tx_hash = get_transaction_hash(tx); + const cryptonote::transaction &tx = std::get<0>(e); + const crypto::hash &tx_hash = std::get<1>(e); + const bool double_spend_seen = std::get<2>(e); process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {}); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) @@ -3114,6 +3150,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset()); return; } + current_index = blocks_start_height; if (hashes.size() + current_index < stop_height) { drop_from_short_history(short_chain_history, 3); std::vector<crypto::hash>::iterator right = hashes.end(); @@ -3123,7 +3160,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, short_chain_history.push_front(*right); } } - current_index = blocks_start_height; for(auto& bl_id: hashes) { if(current_index >= m_blockchain.size()) @@ -3296,7 +3332,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // since that might cause a password prompt, which would introduce a data // leak allowing a passive adversary with traffic analysis capability to // infer when we get an incoming output - std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_pool_txs; update_pool_state(process_pool_txs, true); bool first = true, last = false; @@ -4061,9 +4097,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_always_confirm_transfers = field_always_confirm_transfers; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true); m_print_ring_members = field_print_ring_members; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); - m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); + if (json.HasMember("store_tx_info")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true); + m_store_tx_info = field_store_tx_info; + } + else if (json.HasMember("store_tx_keys")) // backward compatibility + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true); + m_store_tx_info = field_store_tx_keys; + } + else + m_store_tx_info = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0); m_default_mixin = field_default_mixin; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0); @@ -4256,7 +4301,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); if (r) setup_keys(password); @@ -4345,12 +4390,18 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip void wallet2::encrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock); + if (--m_decrypt_keys_lockers) // another lock left ? + return; m_account.encrypt_keys(key); m_account.decrypt_viewkey(key); } void wallet2::decrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock); + if (m_decrypt_keys_lockers++) // already unlocked ? + return; m_account.encrypt_viewkey(key); m_account.decrypt_keys(key); } @@ -7101,6 +7152,20 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto return sign_multisig_tx_to_file(exported_txs, filename, txids); } //---------------------------------------------------------------------------------------------------- +uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const +{ + if (use_per_byte_fee) + { + const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); + } + else + { + const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); + } +} + uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) { static const struct @@ -7742,8 +7807,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ } } +std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs) +{ + std::set<uint64_t> unique; + size_t total = 0; + + for (const auto &it : outs) + { + for (const auto &out : it) + { + const uint64_t global_index = std::get<0>(out); + unique.insert(global_index); + } + total += it.size(); + } + + return std::make_pair(std::move(unique), total); +} + void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) { + std::vector<uint64_t> rct_offsets; + for (size_t attempts = 3; attempts > 0; --attempts) + { + get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets); + + const auto unique = outs_unique(outs); + if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back())) + { + return; + } + + std::vector<crypto::key_image> key_images; + key_images.reserve(selected_transfers.size()); + std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) { + key_images.push_back(m_transfers[index].m_key_image); + }); + unset_ring(key_images); + } + + THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed")); +} + +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets) +{ LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -7764,7 +7871,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // if we have at least one rct out, get the distribution, or fall back to the previous system uint64_t rct_start_height; - std::vector<uint64_t> rct_offsets; bool has_rct = false; uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) @@ -7773,7 +7879,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> has_rct = true; max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); } - const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); + const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets)); if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b17fe6f3a..1dac8acac 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -360,10 +360,12 @@ private: END_SERIALIZE() }; + typedef std::vector<uint64_t> amounts_container; struct payment_details { crypto::hash m_tx_hash; uint64_t m_amount; + amounts_container m_amounts; uint64_t m_fee; uint64_t m_block_height; uint64_t m_unlock_time; @@ -1222,8 +1224,8 @@ private: bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; - void update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed = false); - void process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs); + void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false); + void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs); void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes); std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; @@ -1244,6 +1246,7 @@ private: std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees); + uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_base_fee(); uint64_t get_fee_quantization_mask(); @@ -1387,6 +1390,8 @@ private: uint64_t credits() const { return m_rpc_payment_state.credits; } void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; } + static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } + private: /*! * \brief Stores wallet information to wallet file. @@ -1438,6 +1443,7 @@ private: bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(size_t idx, bool strict = true) const; void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets); bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) 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; @@ -1616,6 +1622,8 @@ private: crypto::chacha_key m_cache_key; boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh; + boost::mutex m_decrypt_keys_lock; + unsigned int m_decrypt_keys_lockers; bool m_unattended; bool m_devices_registered; @@ -1624,6 +1632,9 @@ private: std::unique_ptr<wallet_device_callback> m_device_callback; ExportFormat m_export_format; + + static boost::mutex default_daemon_address_lock; + static std::string default_daemon_address; }; } BOOST_CLASS_VERSION(tools::wallet2, 29) @@ -1631,7 +1642,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) -BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4) +BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5) BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6) @@ -1942,6 +1953,9 @@ namespace boost return; } a & x.m_coinbase; + if (ver < 5) + return; + a & x.m_amounts; } template <class Archive> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index d282d7cb2..db2e2344b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -326,6 +326,7 @@ namespace tools entry.height = pd.m_block_height; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); entry.fee = pd.m_fee; @@ -408,6 +409,7 @@ namespace tools entry.height = 0; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = true; entry.fee = pd.m_fee; @@ -1178,7 +1180,6 @@ namespace tools } } - std::vector<tools::wallet2::pending_tx> ptx; try { // gather info to ask the user @@ -1415,11 +1416,22 @@ namespace tools return false; } + std::set<uint32_t> subaddr_indices; + if (req.subaddr_indices_all) + { + for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i) + subaddr_indices.insert(i); + } + else + { + subaddr_indices= req.subaddr_indices; + } + try { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -2459,7 +2471,7 @@ namespace tools if (req.pool) { - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); @@ -2542,7 +2554,7 @@ namespace tools } } - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index d6735e117..a212b79e6 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -761,6 +761,7 @@ namespace wallet_rpc std::string address; uint32_t account_index; std::set<uint32_t> subaddr_indices; + bool subaddr_indices_all; uint32_t priority; uint64_t ring_size; uint64_t outputs; @@ -776,6 +777,7 @@ namespace wallet_rpc KV_SERIALIZE(address) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) + KV_SERIALIZE_OPT(subaddr_indices_all, false) KV_SERIALIZE(priority) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(outputs, (uint64_t)1) @@ -1370,6 +1372,7 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + typedef std::vector<uint64_t> amounts_container; struct transfer_entry { std::string txid; @@ -1377,6 +1380,7 @@ namespace wallet_rpc uint64_t height; uint64_t timestamp; uint64_t amount; + amounts_container amounts; uint64_t fee; std::string note; std::list<transfer_destination> destinations; @@ -1396,6 +1400,7 @@ namespace wallet_rpc KV_SERIALIZE(height); KV_SERIALIZE(timestamp); KV_SERIALIZE(amount); + KV_SERIALIZE(amounts); KV_SERIALIZE(fee); KV_SERIALIZE(note); KV_SERIALIZE(destinations); |