From 9ac2ad0744e01d7bc7d729635d21b15db5038dba Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Thu, 6 Apr 2017 15:01:07 -0400 Subject: [PATCH 1/4] DRY refactoring --- src/cryptonote_core/blockchain.cpp | 7 ++++++- src/cryptonote_core/blockchain.h | 7 +++++++ src/rpc/core_rpc_server.cpp | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c1faa703f..dec0b42ba 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -751,7 +751,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_timestamps = timestamps; m_difficulties = difficulties; } - size_t target = get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + size_t target = get_difficulty_target(); return next_difficulty(timestamps, difficulties, target); } //------------------------------------------------------------------ @@ -4075,6 +4075,11 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting); } +uint64_t Blockchain::get_difficulty_target() const +{ + return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; +} + std::map> Blockchain:: get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff) const { return m_db->get_output_histogram(amounts, unlocked, recent_cutoff); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index b8ea657b4..7a138a634 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -774,6 +774,13 @@ namespace cryptonote */ bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const; + /** + * @brief get difficulty target based on chain and hardfork version + * + * @return difficulty target + */ + uint64_t get_difficulty_target() const; + /** * @brief remove transactions from the transaction pool (if present) * diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index a6a61705b..1f7f4a1ff 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -137,7 +137,7 @@ namespace cryptonote res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); - res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + res.target = m_core.get_blockchain_storage().get_difficulty_target(); res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); From 5c1e08fe806f0f4dbd06989bafe4df7b4e3601e8 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Thu, 6 Apr 2017 15:01:29 -0400 Subject: [PATCH 2/4] Refactor some things into more composable (smaller) functions This commit refactors some of the rpc-related functions in the Blockchain class to be more composable. This change was made in order to make implementing the new zmq rpc easier without trampling on the old rpc. New functions: Blockchain::get_num_mature_outputs Blockchain::get_random_outputs Blockchain::get_output_key Blockchain::get_output_key_mask_unlocked Blockchain::find_blockchain_supplement (overload) functions which previously had this functionality inline now call these functions as necessary. --- src/cryptonote_core/blockchain.cpp | 196 ++++++++++++++++++----------- src/cryptonote_core/blockchain.h | 56 +++++++++ 2 files changed, 179 insertions(+), 73 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index dec0b42ba..3c855d6cd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1571,6 +1571,98 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A output_data_t data = m_db->get_output_key(amount, i); oen.out_key = data.pubkey; } + +uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const +{ + auto num_outs = m_db->get_num_outputs(amount); + // ensure we don't include outputs that aren't yet eligible to be used + // outpouts are sorted by height + while (num_outs > 0) + { + const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1); + const uint64_t height = m_db->get_tx_block_height(toi.first); + if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) + break; + --num_outs; + } + + return num_outs; +} + +std::vector Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const +{ + uint64_t num_outs = get_num_mature_outputs(amount); + + std::vector indices; + + std::unordered_set seen_indices; + + // if there aren't enough outputs to mix with (or just enough), + // use all of them. Eventually this should become impossible. + if (num_outs <= count) + { + for (uint64_t i = 0; i < num_outs; i++) + { + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(amount, i); + + // if tx is unlocked, add output to indices + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + indices.push_back(i); + } + } + } + else + { + // while we still need more mixins + while (indices.size() < count) + { + // if we've gone through every possible output, we've gotten all we can + if (seen_indices.size() == num_outs) + { + break; + } + + // get a random output index from the DB. If we've already seen it, + // return to the top of the loop and try again, otherwise add it to the + // list of output indices we've seen. + + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit + uint64_t r = crypto::rand() % ((uint64_t)1 << 53); + double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); + uint64_t i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after sqrt + if (i == num_outs) + --i; + + if (seen_indices.count(i)) + { + continue; + } + seen_indices.emplace(i); + + // get tx_hash, tx_out_index from DB + tx_out_index toi = m_db->get_output_tx_and_index(amount, i); + + // if the output's transaction is unlocked, add the output's index to + // our list. + if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) + { + indices.push_back(i); + } + } + } + + return indices; +} + +crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const +{ + output_data_t data = m_db->get_output_key(amount, global_index); + return data.pubkey; +} + //------------------------------------------------------------------ // This function takes an RPC request for mixins and creates an RPC response // with the requested mixins. @@ -1585,80 +1677,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT // from BlockchainDB where is req.outs_count (number of mixins). for (uint64_t amount : req.amounts) { - auto num_outs = m_db->get_num_outputs(amount); - // ensure we don't include outputs that aren't yet eligible to be used - // outpouts are sorted by height - while (num_outs > 0) - { - const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1); - const uint64_t height = m_db->get_tx_block_height(toi.first); - if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height()) - break; - --num_outs; - } - // create outs_for_amount struct and populate amount field COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); result_outs.amount = amount; - std::unordered_set seen_indices; + std::vector indices = get_random_outputs(amount, req.outs_count); - // if there aren't enough outputs to mix with (or just enough), - // use all of them. Eventually this should become impossible. - if (num_outs <= req.outs_count) + for (auto i : indices) { - for (uint64_t i = 0; i < num_outs; i++) - { - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(amount, i); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); - // if tx is unlocked, add output to result_outs - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - add_out_to_get_random_outs(result_outs, amount, i); - } - - } - } - else - { - // while we still need more mixins - while (result_outs.outs.size() < req.outs_count) - { - // if we've gone through every possible output, we've gotten all we can - if (seen_indices.size() == num_outs) - { - break; - } - - // get a random output index from the DB. If we've already seen it, - // return to the top of the loop and try again, otherwise add it to the - // list of output indices we've seen. - - // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit - uint64_t r = crypto::rand() % ((uint64_t)1 << 53); - double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); - uint64_t i = (uint64_t)(frac*num_outs); - // just in case rounding up to 1 occurs after sqrt - if (i == num_outs) - --i; - - if (seen_indices.count(i)) - { - continue; - } - seen_indices.emplace(i); - - // get tx_hash, tx_out_index from DB - tx_out_index toi = m_db->get_output_tx_and_index(amount, i); - - // if the output's transaction is unlocked, add the output's index to - // our list. - if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first))) - { - add_out_to_get_random_outs(result_outs, amount, i); - } - } + oe.global_amount_index = i; + oe.out_key = get_output_key(amount, i); } } return true; @@ -1816,6 +1846,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA return true; } //------------------------------------------------------------------ +void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const +{ + const auto o_data = m_db->get_output_key(amount, index); + key = o_data.pubkey; + mask = o_data.commitment; + tx_out_index toi = m_db->get_output_tx_and_index(amount, index); + unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); +} +//------------------------------------------------------------------ // This function takes a list of block hashes from another node // on the network to find where the split point is between us and them. // This is used to see what to send another node that needs to sync. @@ -2025,28 +2064,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const // Find the split point between us and foreign blockchain and return // (by reference) the most recent common block hash along with up to // BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. -bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const +bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); // if we can't find the split point, return false - if(!find_blockchain_supplement(qblock_ids, resp.start_height)) + if(!find_blockchain_supplement(qblock_ids, start_height)) { return false; } m_db->block_txn_start(true); - resp.total_height = get_current_blockchain_height(); + current_height = get_current_blockchain_height(); size_t count = 0; - for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) + for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { - resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i)); + hashes.push_back(m_db->get_block_hash_from_height(i)); } - resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1); + m_db->block_txn_stop(); return true; } + +bool Blockchain::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height); + resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1); + + return result; +} //------------------------------------------------------------------ //FIXME: change argument to std::vector, low priority // find split point between ours and foreign blockchain (or start at diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 7a138a634..e2da535cd 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -366,6 +366,22 @@ namespace cryptonote */ bool get_short_chain_history(std::list& ids) const; + /** + * @brief get recent block hashes for a foreign chain + * + * Find the split point between us and foreign blockchain and return + * (by reference) the most recent common block hash along with up to + * BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes. + * + * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history) + * @param hashes the hashes to be returned, return-by-reference + * @param start_height the start height, return-by-reference + * @param current_height the current blockchain height, return-by-reference + * + * @return true if a block found in common, else false + */ + bool find_blockchain_supplement(const std::list& qblock_ids, std::list& hashes, uint64_t& start_height, uint64_t& current_height) const; + /** * @brief get recent block hashes for a foreign chain * @@ -426,6 +442,35 @@ namespace cryptonote */ bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); + /** + * @brief get number of outputs of an amount past the minimum spendable age + * + * @param amount the output amount + * + * @return the number of mature outputs + */ + uint64_t get_num_mature_outputs(uint64_t amount) const; + + /** + * @brief get random outputs (indices) for an amount + * + * @param amount the amount + * @param count the number of random outputs to choose + * + * @return the outputs' amount-global indices + */ + std::vector get_random_outputs(uint64_t amount, uint64_t count) const; + + /** + * @brief get the public key for an output + * + * @param amount the output amount + * @param global_index the output amount-global index + * + * @return the public key + */ + crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const; + /** * @brief gets random outputs to mix with * @@ -457,6 +502,17 @@ namespace cryptonote */ bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const; + /** + * @brief gets an output's key and unlocked state + * + * @param amount in - the output amount + * @param index in - the output global amount index + * @param mask out - the output's RingCT mask + * @param key out - the output's key + * @param unlocked out - the output's unlocked state + */ + void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const; + /** * @brief gets random ringct outputs to mix with * From 77986023c37737e298fc7c3e9938cce0e10d8b80 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 5 Sep 2017 12:20:27 -0400 Subject: [PATCH 3/4] json serialization for rpc-relevant monero types Structured {de-,}serialization methods for (many new) types which are used for requests or responses in the RPC. New types include RPC requests and responses, and structs which compose types within those. # Conflicts: # src/cryptonote_core/blockchain.cpp --- CMakeLists.txt | 2 + src/CMakeLists.txt | 1 + src/common/sfinae_helpers.h | 147 ++++ src/cryptonote_config.h | 2 + src/cryptonote_core/blockchain.cpp | 2 +- src/cryptonote_core/cryptonote_core.cpp | 12 + src/cryptonote_core/cryptonote_core.h | 17 + src/cryptonote_core/tx_pool.cpp | 59 ++ src/cryptonote_core/tx_pool.h | 23 + src/daemon/CMakeLists.txt | 3 + src/daemon/command_line_args.h | 25 + src/daemon/daemon.cpp | 41 +- src/daemon/daemon.h | 2 + src/daemon/main.cpp | 4 + src/rpc/CMakeLists.txt | 68 +- src/rpc/daemon_handler.cpp | 800 +++++++++++++++++ src/rpc/daemon_handler.h | 147 ++++ src/rpc/daemon_messages.cpp | 800 +++++++++++++++++ src/rpc/daemon_messages.h | 436 +++++++++ src/rpc/daemon_rpc_version.h | 44 + src/rpc/message.cpp | 286 ++++++ src/rpc/message.h | 131 +++ src/rpc/message_data_structs.h | 169 ++++ src/rpc/rpc_handler.h | 54 ++ src/rpc/zmq_server.cpp | 146 ++++ src/rpc/zmq_server.h | 84 ++ src/serialization/CMakeLists.txt | 54 ++ src/serialization/json_object.cpp | 1068 +++++++++++++++++++++++ src/serialization/json_object.h | 344 ++++++++ 29 files changed, 4968 insertions(+), 3 deletions(-) create mode 100644 src/common/sfinae_helpers.h create mode 100644 src/rpc/daemon_handler.cpp create mode 100644 src/rpc/daemon_handler.h create mode 100644 src/rpc/daemon_messages.cpp create mode 100644 src/rpc/daemon_messages.h create mode 100644 src/rpc/daemon_rpc_version.h create mode 100644 src/rpc/message.cpp create mode 100644 src/rpc/message.h create mode 100644 src/rpc/message_data_structs.h create mode 100644 src/rpc/rpc_handler.h create mode 100644 src/rpc/zmq_server.cpp create mode 100644 src/rpc/zmq_server.h create mode 100644 src/serialization/CMakeLists.txt create mode 100644 src/serialization/json_object.cpp create mode 100644 src/serialization/json_object.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d53e049e..62db856f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -689,6 +689,8 @@ endif() include(version.cmake) +find_library(ZMQ_LIB zmq) + function (treat_warnings_as_errors dirs) foreach(dir ${ARGV}) set_property(DIRECTORY ${dir} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e473c2984..e3cb7cfd6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -107,6 +107,7 @@ endif() add_subdirectory(mnemonics) if(NOT IOS) add_subdirectory(rpc) + add_subdirectory(serialization) endif() add_subdirectory(wallet) if(NOT IOS) diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h new file mode 100644 index 000000000..ddd456dd2 --- /dev/null +++ b/src/common/sfinae_helpers.h @@ -0,0 +1,147 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +// the loose definitions of types in this file are, well, loose. +// +// these helpers aren't here for absolute type certainty at compile-time, +// but rather to help with templated functions telling types apart. + +namespace sfinae +{ + + typedef char true_type; + + struct false_type { true_type a[2]; }; + + template + struct is_not_container + { + private: + + // does not have const iterator + template static false_type c_iter(typename C::const_iterator*); + template static true_type c_iter(...); + + // does not have value_type + template static false_type v_type(typename C::value_type*); + template static true_type v_type(...); + + // does not have key_type + template static false_type k_type(typename C::key_type*); + template static true_type k_type(...); + + // does not have mapped_type + template static false_type m_type(typename C::mapped_type*); + template static true_type m_type(...); + + public: + + static const bool value = ( + ( + sizeof(c_iter(0)) == sizeof(true_type) && + sizeof(v_type(0)) == sizeof(true_type) && + sizeof(k_type(0)) == sizeof(true_type) && + sizeof(m_type(0)) == sizeof(true_type) + ) + || std::is_same::value + ); + + typedef T type; + }; + + template + struct is_vector_like + { + private: + + // has const iterator + template static true_type c_iter(typename C::const_iterator*); + template static false_type c_iter(...); + + // has value_type + template static true_type v_type(typename C::value_type*); + template static false_type v_type(...); + + // does not have key_type + template static false_type k_type(typename C::key_type*); + template static true_type k_type(...); + + // does not have mapped_type + template static false_type m_type(typename C::mapped_type*); + template static true_type m_type(...); + + public: + + static const bool value = ( + sizeof(c_iter(0)) == sizeof(true_type) && + sizeof(v_type(0)) == sizeof(true_type) && + sizeof(k_type(0)) == sizeof(true_type) && + sizeof(m_type(0)) == sizeof(true_type) && + !std::is_same::value + ); + + typedef T type; + }; + + template + struct is_map_like + { + private: + + // has const iterator + template static true_type c_iter(typename C::const_iterator*); + template static false_type c_iter(...); + + // has value_type + template static true_type v_type(typename C::value_type*); + template static false_type v_type(...); + + // has key_type + template static true_type k_type(typename C::key_type*); + template static false_type k_type(...); + + // has mapped_type + template static true_type m_type(typename C::mapped_type*); + template static false_type m_type(...); + + public: + + static const bool value = ( + sizeof(c_iter(0)) == sizeof(true_type) && + sizeof(v_type(0)) == sizeof(true_type) && + sizeof(k_type(0)) == sizeof(true_type) && + sizeof(m_type(0)) == sizeof(true_type) && + !std::is_same::value + ); + + typedef T type; + }; + +} // namespace sfinae diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 8ff01cf8b..cdb46dda9 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -150,6 +150,7 @@ namespace config uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; uint16_t const P2P_DEFAULT_PORT = 18080; uint16_t const RPC_DEFAULT_PORT = 18081; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082; boost::uuids::uuid const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 } }; // Bender's nightmare @@ -162,6 +163,7 @@ namespace config uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54; uint16_t const P2P_DEFAULT_PORT = 28080; uint16_t const RPC_DEFAULT_PORT = 28081; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082; boost::uuids::uuid const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 } }; // Bender's daydream diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3c855d6cd..35d42a394 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1574,7 +1574,7 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const { - auto num_outs = m_db->get_num_outputs(amount); + uint64_t num_outs = m_db->get_num_outputs(amount); // ensure we don't include outputs that aren't yet eligible to be used // outpouts are sorted by height while (num_outs > 0) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c406dd0b4..cda54c65d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -790,6 +790,13 @@ namespace cryptonote return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4; } //----------------------------------------------------------------------------------------------- + bool core::are_key_images_spent_in_pool(const std::vector& key_im, std::vector &spent) const + { + spent.clear(); + + return m_mempool.check_for_key_images(key_im, spent); + } + //----------------------------------------------------------------------------------------------- std::pair core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count) { uint64_t emission_amount = 0; @@ -1175,6 +1182,11 @@ namespace cryptonote return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos); } //----------------------------------------------------------------------------------------------- + bool core::get_pool_for_rpc(std::vector& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const + { + return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos); + } + //----------------------------------------------------------------------------------------------- bool core::get_short_chain_history(std::list& ids) const { return m_blockchain_storage.get_short_chain_history(ids); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index f565afd87..d94d1ad86 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -462,6 +462,13 @@ namespace cryptonote */ bool get_pool_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const; + /** + * @copydoc tx_memory_pool::get_pool_for_rpc + * + * @note see tx_memory_pool::get_pool_for_rpc + */ + bool get_pool_for_rpc(std::vector& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const; + /** * @copydoc tx_memory_pool::get_transactions_count * @@ -707,6 +714,16 @@ namespace cryptonote */ bool are_key_images_spent(const std::vector& key_im, std::vector &spent) const; + /** + * @brief check if multiple key images are spent in the transaction pool + * + * @param key_im list of key images to check + * @param spent return-by-reference result for each image checked + * + * @return true + */ + bool are_key_images_spent_in_pool(const std::vector& key_im, std::vector &spent) const; + /** * @brief get the number of blocks to sync in one go * diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 265a4ca3e..e1f640a97 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -684,6 +684,65 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_pool_for_rpc(std::vector& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ + cryptonote::rpc::tx_in_pool txi; + txi.tx_hash = txid; + transaction tx; + if (!parse_and_validate_tx_from_blob(*bd, tx)) + { + MERROR("Failed to parse tx from txpool"); + // continue + return true; + } + txi.tx = tx; + txi.blob_size = meta.blob_size; + txi.fee = meta.fee; + txi.kept_by_block = meta.kept_by_block; + txi.max_used_block_height = meta.max_used_block_height; + txi.max_used_block_hash = meta.max_used_block_id; + txi.last_failed_block_height = meta.last_failed_height; + txi.last_failed_block_hash = meta.last_failed_id; + txi.receive_time = meta.receive_time; + txi.relayed = meta.relayed; + txi.last_relayed_time = meta.last_relayed_time; + txi.do_not_relay = meta.do_not_relay; + tx_infos.push_back(txi); + return true; + }, true); + + for (const key_images_container::value_type& kee : m_spent_key_images) { + std::vector tx_hashes; + const std::unordered_set& kei_image_set = kee.second; + for (const crypto::hash& tx_id_hash : kei_image_set) + { + tx_hashes.push_back(tx_id_hash); + } + + const crypto::key_image& k_image = kee.first; + key_image_infos[k_image] = tx_hashes; + } + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::check_for_key_images(const std::vector& key_images, std::vector spent) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + CRITICAL_REGION_LOCAL1(m_blockchain); + + spent.clear(); + + for (const auto& image : key_images) + { + spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true); + } + + return true; + } + //--------------------------------------------------------------------------------- bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const { CRITICAL_REGION_LOCAL(m_transactions_lock); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 6414620c9..3e4ccb338 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -46,6 +46,7 @@ #include "blockchain_db/blockchain_db.h" #include "crypto/hash.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "rpc/message_data_structs.h" namespace cryptonote { @@ -268,6 +269,28 @@ namespace cryptonote */ bool get_transactions_and_spent_keys_info(std::vector& tx_infos, std::vector& key_image_infos) const; + /** + * @brief get information about all transactions and key images in the pool + * + * see documentation on tx_in_pool and key_images_with_tx_hashes for more details + * + * @param tx_infos [out] the transactions' information + * @param key_image_infos [out] the spent key images' information + * + * @return true + */ + bool get_pool_for_rpc(std::vector& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const; + + /** + * @brief check for presence of key images in the pool + * + * @param key_images [in] vector of key images to check + * @param spent [out] vector of bool to return + * + * @return true + */ + bool check_for_key_images(const std::vector& key_images, std::vector spent) const; + /** * @brief get a specific transaction from the pool * diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 795442a2d..782667867 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -92,12 +92,15 @@ target_link_libraries(daemon p2p cryptonote_protocol daemonizer + serialization + daemon_rpc_server ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_REGEX_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} + ${ZMQ_LIB} ${EXTRA_LIBRARIES}) add_dependencies(daemon version) set_property(TARGET daemon diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 8eb3db195..797285354 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -64,6 +64,31 @@ namespace daemon_args , "Max number of threads to use for a parallel job" , 0 }; + + const command_line::arg_descriptor arg_zmq_rpc_bind_ip = { + "zmq-rpc-bind-ip" + , "IP for ZMQ RPC server to listen on" + , "127.0.0.1" + }; + + const command_line::arg_descriptor arg_zmq_rpc_bind_port = { + "zmq-rpc-bind-port" + , "Port for ZMQ RPC server to listen on" + , std::to_string(config::ZMQ_RPC_DEFAULT_PORT) + }; + + const command_line::arg_descriptor arg_zmq_testnet_rpc_bind_port = { + "zmq-testnet-rpc-bind-port" + , "Port for testnet ZMQ RPC server to listen on" + , std::to_string(config::testnet::ZMQ_RPC_DEFAULT_PORT) + }; + + const command_line::arg_descriptor arg_zmq_restricted_rpc = { + "zmq-restricted-rpc" + , "Restrict ZMQ RPC to view only commands" + , false + }; + } // namespace daemon_args #endif // DAEMON_COMMAND_LINE_ARGS_H diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 683eaf4ff..faa620c54 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -32,6 +32,8 @@ #include #include "misc_log_ex.h" #include "daemon/daemon.h" +#include "rpc/daemon_handler.h" +#include "rpc/zmq_server.h" #include "common/password.h" #include "common/util.h" @@ -85,7 +87,18 @@ t_daemon::t_daemon( boost::program_options::variables_map const & vm ) : mp_internals{new t_internals{vm}} -{} +{ + bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + if (testnet) + { + zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_testnet_rpc_bind_port); + } + else + { + zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); + } + zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); +} t_daemon::~t_daemon() = default; @@ -133,6 +146,30 @@ bool t_daemon::run(bool interactive) rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } + cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get()); + cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler); + + if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port)) + { + LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address + + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server"); + + if (interactive) + { + rpc_commands->stop_handling(); + } + + mp_internals->rpc.stop(); + + return false; + } + + MINFO("Starting ZMQ server..."); + zmq_server.run(); + + MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address + + ":" + zmq_rpc_bind_port + "."); + mp_internals->p2p.run(); // blocks until p2p goes down if (rpc_commands) @@ -140,6 +177,8 @@ bool t_daemon::run(bool interactive) rpc_commands->stop_handling(); } + zmq_server.stop(); + mp_internals->rpc.stop(); mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index 2b9f18669..8c7547f62 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -43,6 +43,8 @@ private: void stop_p2p(); private: std::unique_ptr mp_internals; + std::string zmq_rpc_bind_address; + std::string zmq_rpc_bind_port; public: t_daemon( boost::program_options::variables_map const & vm diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 456eeee64..0d93a49ff 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -89,6 +89,10 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string()); command_line::add_arg(core_settings, daemon_args::arg_log_level); command_line::add_arg(core_settings, daemon_args::arg_max_concurrency); + command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); + command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); + command_line::add_arg(core_settings, daemon_args::arg_zmq_testnet_rpc_bind_port); + command_line::add_arg(core_settings, daemon_args::arg_zmq_restricted_rpc); daemonizer::init_options(hidden_options, visible_options); daemonize::t_executor::init_options(core_settings); diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index f6037d7e3..717e3e627 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -30,20 +30,61 @@ set(rpc_sources core_rpc_server.cpp rpc_args.cpp) +set(daemon_messages_sources + message.cpp + daemon_messages.cpp) + +set(daemon_rpc_server_sources + daemon_handler.cpp + zmq_server.cpp) + + set(rpc_headers rpc_args.h) -set(rpc_private_headers +set(daemon_rpc_server_headers) + + +set(rpc_daemon_private_headers core_rpc_server.h core_rpc_server_commands_defs.h core_rpc_server_error_codes.h) +set(daemon_messages_private_headers + message.h + daemon_messages.h) + +set(daemon_rpc_server_private_headers + message.h + daemon_messages.h + daemon_handler.h + rpc_handler.h + zmq_server.h) + + monero_private_headers(rpc ${rpc_private_headers}) + +monero_private_headers(daemon_rpc_server + ${daemon_rpc_server_private_headers}) + + monero_add_library(rpc ${rpc_sources} ${rpc_headers} ${rpc_private_headers}) + +monero_add_library(daemon_messages + ${daemon_messages_sources} + ${daemon_messages_headers} + ${daemon_messages_private_headers}) + +monero_add_library(daemon_rpc_server + ${daemon_rpc_server_sources} + ${daemon_rpc_server_headers} + ${daemon_rpc_server_private_headers}) + + target_link_libraries(rpc PUBLIC common @@ -54,5 +95,30 @@ target_link_libraries(rpc ${Boost_THREAD_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) + +target_link_libraries(daemon_messages + LINK_PRIVATE + cryptonote_core + cryptonote_protocol + serialization + ${EXTRA_LIBRARIES}) + +target_link_libraries(daemon_rpc_server + LINK_PRIVATE + cryptonote_core + cryptonote_protocol + daemon_messages + serialization + ${Boost_CHRONO_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${ZMQ_LIB} + ${EXTRA_LIBRARIES}) + + add_dependencies(rpc version) + +add_dependencies(daemon_rpc_server + version) diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp new file mode 100644 index 000000000..6a287b229 --- /dev/null +++ b/src/rpc/daemon_handler.cpp @@ -0,0 +1,800 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon_handler.h" + +// likely included by daemon_handler.h's includes, +// but including here for clarity +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "ringct/rctSigs.h" + +namespace cryptonote +{ + +namespace rpc +{ + + void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res) + { + res.height = m_core.get_current_blockchain_height(); + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res) + { + std::list > > blocks; + + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::find_blockchain_supplement() returned false"; + return; + } + + res.blocks.resize(blocks.size()); + res.output_indices.resize(blocks.size()); + + //TODO: really need to switch uses of std::list to std::vector unless + // it's a huge performance concern + + auto it = blocks.begin(); + + uint64_t block_count = 0; + while (it != blocks.end()) + { + cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count]; + + if (!parse_and_validate_block_from_blob(it->first, bwt.block)) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "failed retrieving a requested block"; + return; + } + + std::list txs; + for (const auto& blob : it->second) + { + txs.resize(txs.size() + 1); + if (!parse_and_validate_tx_from_blob(blob, txs.back())) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "failed retrieving a requested block"; + return; + } + } + + cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count]; + + // miner tx output indices + { + cryptonote::rpc::tx_output_indices tx_indices; + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices); + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + indices.push_back(tx_indices); + } + + // assume each block returned is returned with all its transactions + // in the correct order. + auto tx_it = txs.begin(); + for (const crypto::hash& h : bwt.block.tx_hashes) + { + bwt.transactions.emplace(h, *tx_it); + tx_it++; + + cryptonote::rpc::tx_output_indices tx_indices; + bool r = m_core.get_tx_outputs_gindexs(h, tx_indices); + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + + indices.push_back(tx_indices); + } + + it++; + block_count++; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res) + { + res.start_height = req.start_height; + + auto& chain = m_core.get_blockchain_storage(); + + if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Blockchain::find_blockchain_supplement() returned false"; + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res) + { + std::list found_txs; + std::list missed_hashes; + + bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes); + + // TODO: consider fixing core::get_transactions to not hide exceptions + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_transactions() returned false (exception caught there)"; + return; + } + + size_t num_found = found_txs.size(); + + // std::list is annoying + std::vector found_txs_vec + { + std::make_move_iterator(std::begin(found_txs)), + std::make_move_iterator(std::end(found_txs)) + }; + + std::vector missed_vec + { + std::make_move_iterator(std::begin(missed_hashes)), + std::make_move_iterator(std::end(missed_hashes)) + }; + + std::vector heights(num_found); + std::vector in_pool(num_found, false); + std::vector found_hashes(num_found); + + for (size_t i=0; i < num_found; i++) + { + found_hashes[i] = get_transaction_hash(found_txs_vec[i]); + heights[i] = m_core.get_blockchain_storage().get_db().get_tx_block_height(found_hashes[i]); + } + + // if any missing from blockchain, check in tx pool + if (!missed_vec.empty()) + { + std::list pool_txs; + + m_core.get_pool_transactions(pool_txs); + + for (const auto& tx : pool_txs) + { + crypto::hash h = get_transaction_hash(tx); + + auto itr = std::find(missed_vec.begin(), missed_vec.end(), h); + + if (itr != missed_vec.end()) + { + found_hashes.push_back(h); + found_txs_vec.push_back(tx); + heights.push_back(std::numeric_limits::max()); + in_pool.push_back(true); + missed_vec.erase(itr); + } + } + } + + for (size_t i=0; i < found_hashes.size(); i++) + { + cryptonote::rpc::transaction_info info; + info.height = heights[i]; + info.in_pool = in_pool[i]; + info.transaction = std::move(found_txs_vec[i]); + + res.txs.emplace(found_hashes[i], std::move(info)); + } + + res.missed_hashes = std::move(missed_vec); + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res) + { + res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT); + + std::vector chain_spent_status; + std::vector pool_spent_status; + + m_core.are_key_images_spent(req.key_images, chain_spent_status); + m_core.are_key_images_spent_in_pool(req.key_images, pool_spent_status); + + if ((chain_spent_status.size() != req.key_images.size()) || (pool_spent_status.size() != req.key_images.size())) + { + res.status = Message::STATUS_FAILED; + res.error_details = "tx_pool::have_key_images_as_spent() gave vectors of wrong size(s)."; + return; + } + + for(size_t i=0; i < req.key_images.size(); i++) + { + if ( chain_spent_status[i] ) + { + res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_BLOCKCHAIN; + } + else if ( pool_spent_status[i] ) + { + res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_POOL; + } + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res) + { + if (!m_core.get_tx_outputs_gindexs(req.tx_hash, res.output_indices)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + + res.status = Message::STATUS_OK; + + } + + //TODO: handle "restricted" RPC + void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res) + { + auto& chain = m_core.get_blockchain_storage(); + + try + { + for (const uint64_t& amount : req.amounts) + { + std::vector indices = chain.get_random_outputs(amount, req.count); + + outputs_for_amount ofa; + + ofa.resize(indices.size()); + + for (size_t i = 0; i < indices.size(); i++) + { + crypto::public_key key = chain.get_output_key(amount, indices[i]); + ofa[i].amount_index = indices[i]; + ofa[i].key = key; + } + + amount_with_random_outputs amt; + amt.amount = amount; + amt.outputs = ofa; + + res.amounts_with_outputs.push_back(amt); + } + + res.status = Message::STATUS_OK; + } + catch (const std::exception& e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + } + } + + void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res) + { + auto tx_blob = cryptonote::tx_to_blob(req.tx); + + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + + if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed) + { + if (tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + } + else + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + } + res.status = Message::STATUS_FAILED; + res.error_details = ""; + + if (tvc.m_low_mixin) + { + res.error_details = "mixin too low"; + } + if (tvc.m_double_spend) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "double spend"; + } + if (tvc.m_invalid_input) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "invalid input"; + } + if (tvc.m_invalid_output) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "invalid output"; + } + if (tvc.m_too_big) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "too big"; + } + if (tvc.m_overspend) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "overspend"; + } + if (tvc.m_fee_too_low) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "fee too low"; + } + if (tvc.m_not_rct) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "tx is not ringct"; + } + if (res.error_details.empty()) + { + res.error_details = "an unknown issue was found with the transaction"; + } + + return; + } + + if(!tvc.m_should_be_relayed || !req.relay) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + res.error_details = "Not relayed"; + res.relayed = false; + res.status = Message::STATUS_OK; + + return; + } + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + m_core.get_protocol()->relay_transactions(r, fake_context); + + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + res.status = Message::STATUS_OK; + res.relayed = true; + + return; + } + + void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res) + { + res.height = m_core.get_current_blockchain_height(); + + res.target_height = m_core.get_target_blockchain_height(); + + if (res.height > res.target_height) + { + res.target_height = res.height; + } + + auto& chain = m_core.get_blockchain_storage(); + + res.difficulty = chain.get_difficulty_for_next_block(); + + res.target = chain.get_difficulty_target(); + + res.tx_count = chain.get_total_transactions() - res.height; //without coinbase + + res.tx_pool_size = m_core.get_pool_transactions_count(); + + res.alt_blocks_count = chain.get_alternative_blocks_count(); + + uint64_t total_conn = m_p2p.get_connections_count(); + res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = total_conn - res.outgoing_connections_count; + + res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); + + res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + + res.testnet = m_core.get_testnet(); + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res) + { + if (!m_core.get_blockchain_storage().store_blockchain()) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Error storing the blockchain"; + } + else + { + res.status = Message::STATUS_OK; + } + } + + void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res) + { + if (m_core.get_current_blockchain_height() <= req.height) + { + res.hash = cryptonote::null_hash; + res.status = Message::STATUS_FAILED; + res.error_details = "height given is higher than current chain height"; + return; + } + + res.hash = m_core.get_block_id_by_height(req.height); + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SubmitBlock::Request& req, SubmitBlock::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res) + { + const crypto::hash block_hash = m_core.get_tail_id(); + + if (!getBlockHeaderByHash(block_hash, res.header)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Requested block does not exist"; + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res) + { + if (!getBlockHeaderByHash(req.hash, res.header)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Requested block does not exist"; + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res) + { + const crypto::hash block_hash = m_core.get_block_id_by_height(req.height); + + if (!getBlockHeaderByHash(block_hash, res.header)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Requested block does not exist"; + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + //TODO: this RPC call is marked for later implementation in the old RPC, + // need to sort out if it's necessary and what it should do. + void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res) + { + if (req.level < 0 || req.level > 4) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Error: log level not valid"; + } + else + { + res.status = Message::STATUS_OK; + mlog_set_log_level(req.level); + } + } + + void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res) + { + bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images); + + if (!r) res.status = Message::STATUS_FAILED; + else res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const FastExit::Request& req, FastExit::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const OutPeers::Request& req, OutPeers::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res) + { + const Blockchain &blockchain = m_core.get_blockchain_storage(); + uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version(); + res.info.version = blockchain.get_current_hard_fork_version(); + res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting); + res.info.state = blockchain.get_hard_fork_state(); + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res) + { + std::map > histogram; + try + { + histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff); + } + catch (const std::exception &e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + return; + } + + res.histogram.clear(); + res.histogram.reserve(histogram.size()); + for (const auto &i: histogram) + { + if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0)) + res.histogram.emplace_back(output_amount_count{i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)}); + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res) + { + for (const auto& i : req.outputs) + { + crypto::public_key key; + rct::key mask; + bool unlocked; + m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked); + res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked}); + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res) + { + res.version = DAEMON_RPC_VERSION; + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) + { + res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks); + res.status = Message::STATUS_OK; + } + + bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header) + { + block b; + + if (!m_core.get_block_by_hash(hash_in, b)) + { + return false; + } + + header.hash = hash_in; + header.height = boost::get(b.miner_tx.vin.front()).height; + + header.major_version = b.major_version; + header.minor_version = b.minor_version; + header.timestamp = b.timestamp; + header.nonce = b.nonce; + header.prev_id = b.prev_id; + + header.depth = m_core.get_current_blockchain_height() - header.height - 1; + + header.reward = 0; + for (const auto& out : b.miner_tx.vout) + { + header.reward += out.amount; + } + + header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + + return true; + } + + std::string DaemonHandler::handle(const std::string& request) + { + MDEBUG("Handling RPC request: " << request); + + Message* resp_message = NULL; + + try + { + FullMessage req_full(request, true); + + rapidjson::Value& req_json = req_full.getMessage(); + + const std::string request_type = req_full.getRequestType(); + + // create correct Message subclass and call handle() on it + REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, 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, GetPeerList, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle); + + // if none of the request types matches + if (resp_message == NULL) + { + return BAD_REQUEST(request_type, req_full.getID()); + } + + FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID()); + + const std::string response = resp_full.getJson(); + delete resp_message; + resp_message = NULL; + + MDEBUG("Returning RPC response: " << response); + + return response; + } + catch (const std::exception& e) + { + if (resp_message) + { + delete resp_message; + } + + return BAD_JSON(e.what()); + } + } + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h new file mode 100644 index 000000000..9997956c7 --- /dev/null +++ b/src/rpc/daemon_handler.h @@ -0,0 +1,147 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "daemon_messages.h" +#include "daemon_rpc_version.h" +#include "rpc_handler.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" + +namespace +{ + typedef nodetool::node_server > t_p2p; +} // anonymous namespace + +namespace cryptonote +{ + +namespace rpc +{ + +class DaemonHandler : public RpcHandler +{ + public: + + DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { } + + ~DaemonHandler() { } + + void handle(const GetHeight::Request& req, GetHeight::Response& res); + + void handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res); + + void handle(const GetHashesFast::Request& req, GetHashesFast::Response& res); + + void handle(const GetTransactions::Request& req, GetTransactions::Response& res); + + void handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res); + + void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res); + + void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res); + + void handle(const SendRawTx::Request& req, SendRawTx::Response& res); + + void handle(const StartMining::Request& req, StartMining::Response& res); + + void handle(const GetInfo::Request& req, GetInfo::Response& res); + + void handle(const StopMining::Request& req, StopMining::Response& res); + + void handle(const MiningStatus::Request& req, MiningStatus::Response& res); + + void handle(const SaveBC::Request& req, SaveBC::Response& res); + + void handle(const GetBlockHash::Request& req, GetBlockHash::Response& res); + + void handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res); + + void handle(const SubmitBlock::Request& req, SubmitBlock::Response& res); + + void handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res); + + void handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res); + + void handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res); + + void handle(const GetBlock::Request& req, GetBlock::Response& res); + + void handle(const GetPeerList::Request& req, GetPeerList::Response& res); + + void handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res); + + void handle(const SetLogLevel::Request& req, SetLogLevel::Response& res); + + void handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res); + + void handle(const GetConnections::Request& req, GetConnections::Response& res); + + void handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res); + + void handle(const StopDaemon::Request& req, StopDaemon::Response& res); + + void handle(const FastExit::Request& req, FastExit::Response& res); + + void handle(const OutPeers::Request& req, OutPeers::Response& res); + + void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res); + + void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res); + + void handle(const HardForkInfo::Request& req, HardForkInfo::Response& res); + + void handle(const GetBans::Request& req, GetBans::Response& res); + + void handle(const SetBans::Request& req, SetBans::Response& res); + + void handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res); + + void handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res); + + void handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res); + + void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res); + + void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res); + + std::string handle(const std::string& request); + + private: + + bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response); + + cryptonote::core& m_core; + t_p2p& m_p2p; +}; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp new file mode 100644 index 000000000..b999a0f18 --- /dev/null +++ b/src/rpc/daemon_messages.cpp @@ -0,0 +1,800 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon_messages.h" +#include "serialization/json_object.h" + +namespace cryptonote +{ + +namespace rpc +{ + +const char* const GetHeight::name = "get_height"; +const char* const GetBlocksFast::name = "get_blocks_fast"; +const char* const GetHashesFast::name = "get_hashes_fast"; +const char* const GetTransactions::name = "get_transactions"; +const char* const KeyImagesSpent::name = "key_images_spent"; +const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; +const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts"; +const char* const SendRawTx::name = "send_raw_tx"; +const char* const 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 GetPeerList::name = "get_peer_list"; +const char* const SetLogLevel::name = "set_log_level"; +const char* const GetTransactionPool::name = "get_transaction_pool"; +const char* const HardForkInfo::name = "hard_fork_info"; +const char* const GetOutputHistogram::name = "get_output_histogram"; +const char* const GetOutputKeys::name = "get_output_keys"; +const char* const GetRPCVersion::name = "get_rpc_version"; +const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate"; + + + + +rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetHeight::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + val.AddMember("height", height, al); + + return val; +} + +void GetHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + + +rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids); + val.AddMember("start_height", start_height, al); + + return val; +} + +void GetBlocksFast::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, block_ids, block_ids); + GET_FROM_JSON_OBJECT(val, start_height, start_height); +} + +rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks); + val.AddMember("start_height", start_height, al); + val.AddMember("current_height", current_height, al); + INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); + + return val; +} + +void GetBlocksFast::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, blocks, blocks); + GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, current_height, current_height); + GET_FROM_JSON_OBJECT(val, output_indices, output_indices); +} + + +rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes); + val.AddMember("start_height", start_height, al); + + return val; +} + +void GetHashesFast::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes); + GET_FROM_JSON_OBJECT(val, start_height, start_height); +} + +rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes); + val.AddMember("start_height", start_height, al); + val.AddMember("current_height", current_height, al); + + return val; +} + +void GetHashesFast::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hashes, hashes); + GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, current_height, current_height); +} + + +rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes); + + return val; +} + +void GetTransactions::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes); +} + +rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const +{ + rapidjson::Value val(rapidjson::kObjectType); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, txs, txs); + INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes); + + return val; +} + +void GetTransactions::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, txs, txs); + GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes); +} + + +rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); + + return val; +} + +void KeyImagesSpent::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, key_images, key_images); +} + +rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status); + + return val; +} + +void KeyImagesSpent::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, spent_status, spent_status); +} + + +rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash); + + return val; +} + +void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash); +} + +rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); + + return val; +} + +void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, output_indices, output_indices); +} + + +rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); + INSERT_INTO_JSON_OBJECT(val, doc, count, count); + + return val; +} + +void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts, amounts); + GET_FROM_JSON_OBJECT(val, count, count); +} + +rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs); + + return val; +} + +void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs); +} + + +rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx, tx); + INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); + + return val; +} + +void SendRawTx::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx, tx); + GET_FROM_JSON_OBJECT(val, relay, relay); +} + +rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed); + + return val; +} + +void SendRawTx::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, relayed, relayed); +} + + +rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetInfo::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, height); + INSERT_INTO_JSON_OBJECT(val, doc, target_height, target_height); + INSERT_INTO_JSON_OBJECT(val, doc, difficulty, difficulty); + INSERT_INTO_JSON_OBJECT(val, doc, target, target); + INSERT_INTO_JSON_OBJECT(val, doc, tx_count, tx_count); + INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, tx_pool_size); + INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, alt_blocks_count); + INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, outgoing_connections_count); + INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, incoming_connections_count); + INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, white_peerlist_size); + INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, grey_peerlist_size); + INSERT_INTO_JSON_OBJECT(val, doc, testnet, testnet); + INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, top_block_hash); + + return val; +} + +void GetInfo::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); + GET_FROM_JSON_OBJECT(val, target_height, target_height); + GET_FROM_JSON_OBJECT(val, difficulty, difficulty); + GET_FROM_JSON_OBJECT(val, target, target); + GET_FROM_JSON_OBJECT(val, tx_count, tx_count); + GET_FROM_JSON_OBJECT(val, tx_pool_size, tx_pool_size); + GET_FROM_JSON_OBJECT(val, alt_blocks_count, alt_blocks_count); + GET_FROM_JSON_OBJECT(val, outgoing_connections_count, outgoing_connections_count); + GET_FROM_JSON_OBJECT(val, incoming_connections_count, incoming_connections_count); + GET_FROM_JSON_OBJECT(val, white_peerlist_size, white_peerlist_size); + GET_FROM_JSON_OBJECT(val, grey_peerlist_size, grey_peerlist_size); + GET_FROM_JSON_OBJECT(val, testnet, testnet); + GET_FROM_JSON_OBJECT(val, top_block_hash, top_block_hash); +} + + +rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void SaveBC::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void SaveBC::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, height); + + return val; +} + +void GetBlockHash::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + +rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); + + return val; +} + +void GetBlockHash::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hash, hash); +} + + +rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); + + return val; +} + +void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hash, hash); +} + +rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, height); + + return val; +} + +void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + +rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void GetPeerList::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list); + INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list); + + return val; +} + +void GetPeerList::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, white_list, white_list); + GET_FROM_JSON_OBJECT(val, gray_list, gray_list); +} + + +rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + val.AddMember("level", level, al); + + return val; +} + +void SetLogLevel::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, level, level); +} + +rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void SetLogLevel::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetTransactionPool::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions); + INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); + + return val; +} + +void GetTransactionPool::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, transactions, transactions); + GET_FROM_JSON_OBJECT(val, key_images, key_images); +} + + +rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, version); + + return val; +} + +void HardForkInfo::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, version, version); +} + +rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, info, info); + + return val; +} + +void HardForkInfo::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, info, info); +} + + +rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); + INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count); + INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count); + INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked); + INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff); + + return val; +} + +void GetOutputHistogram::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts, amounts); + GET_FROM_JSON_OBJECT(val, min_count, min_count); + GET_FROM_JSON_OBJECT(val, max_count, max_count); + GET_FROM_JSON_OBJECT(val, unlocked, unlocked); + GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff); +} + +rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram); + + return val; +} + +void GetOutputHistogram::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, histogram, histogram); +} + + +rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs); + + return val; +} + +void GetOutputKeys::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, outputs, outputs); +} + +rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, keys, keys); + + return val; +} + +void GetOutputKeys::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, keys, keys); +} + + +rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetRPCVersion::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, version); + + return val; +} + +void GetRPCVersion::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, version, version); +} + +rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks); + + return val; +} + +void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks); +} + +rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb); + + return val; +} + +void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb); +} + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h new file mode 100644 index 000000000..adcd7f7c6 --- /dev/null +++ b/src/rpc/daemon_messages.h @@ -0,0 +1,436 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "message.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/message_data_structs.h" +#include "rpc/daemon_rpc_version.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#define BEGIN_RPC_MESSAGE_CLASS(classname) \ +class classname \ +{ \ + public: \ + static const char* const name; + +#define BEGIN_RPC_MESSAGE_REQUEST \ + class Request : public Message \ + { \ + public: \ + Request() { } \ + ~Request() { } \ + rapidjson::Value toJson(rapidjson::Document& doc) const; \ + void fromJson(rapidjson::Value& val); + +#define BEGIN_RPC_MESSAGE_RESPONSE \ + class Response : public Message \ + { \ + public: \ + Response() { } \ + ~Response() { } \ + rapidjson::Value toJson(rapidjson::Document& doc) const; \ + void fromJson(rapidjson::Value& val); + +#define END_RPC_MESSAGE_REQUEST }; +#define END_RPC_MESSAGE_RESPONSE }; +#define END_RPC_MESSAGE_CLASS }; + +#define COMMA() , + +// NOTE: when using a type with multiple template parameters, +// replace any comma in the template specifier with the macro +// above, or the preprocessor will eat the comma in a bad way. +#define RPC_MESSAGE_MEMBER(type, name) type name; + + +namespace cryptonote +{ + +namespace rpc +{ + +BEGIN_RPC_MESSAGE_CLASS(GetHeight); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::list, block_ids); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, blocks); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(uint64_t, current_height); + RPC_MESSAGE_MEMBER(std::vector, output_indices); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetHashesFast); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::list, known_hashes); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::list, hashes); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(uint64_t, current_height); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetTransactions); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, tx_hashes); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::unordered_map, txs); + RPC_MESSAGE_MEMBER(std::vector, missed_hashes); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(KeyImagesSpent); + enum STATUS { + UNSPENT = 0, + SPENT_IN_BLOCKCHAIN = 1, + SPENT_IN_POOL = 2, + }; + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, key_images); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, spent_status); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetTxGlobalOutputIndices); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(crypto::hash, tx_hash); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, output_indices); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetRandomOutputsForAmounts); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, amounts); + RPC_MESSAGE_MEMBER(uint64_t, count); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, amounts_with_outputs); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SendRawTx); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(cryptonote::transaction, tx); + RPC_MESSAGE_MEMBER(bool, relay); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, relayed); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StartMining); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::string, miner_address); + RPC_MESSAGE_MEMBER(uint64_t, thread_count); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, success); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetInfo); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint64_t, height); + RPC_MESSAGE_MEMBER(uint64_t, target_height); + RPC_MESSAGE_MEMBER(uint64_t, difficulty); + RPC_MESSAGE_MEMBER(uint64_t, target); + RPC_MESSAGE_MEMBER(uint64_t, tx_count); + RPC_MESSAGE_MEMBER(uint64_t, tx_pool_size); + RPC_MESSAGE_MEMBER(uint64_t, alt_blocks_count); + RPC_MESSAGE_MEMBER(uint64_t, outgoing_connections_count); + RPC_MESSAGE_MEMBER(uint64_t, incoming_connections_count); + RPC_MESSAGE_MEMBER(uint64_t, white_peerlist_size); + RPC_MESSAGE_MEMBER(uint64_t, grey_peerlist_size); + RPC_MESSAGE_MEMBER(bool, testnet); + RPC_MESSAGE_MEMBER(crypto::hash, top_block_hash); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopMining); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, success); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(MiningStatus); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, active); + RPC_MESSAGE_MEMBER(uint64_t, speed); + RPC_MESSAGE_MEMBER(uint64_t, thread_count); + RPC_MESSAGE_MEMBER(std::string, address); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SaveBC); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHash); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(crypto::hash, hash); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockTemplate); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SubmitBlock); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetLastBlockHeader); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHash); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(crypto::hash, hash); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlock); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetPeerList); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, white_list); + RPC_MESSAGE_MEMBER(std::vector, gray_list); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetLogHashRate); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetLogLevel); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(int8_t, level); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetTransactionPool); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, transactions); + RPC_MESSAGE_MEMBER(key_images_with_tx_hashes, key_images); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetConnections); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersRange); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopDaemon); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(FastExit); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(OutPeers); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopSaveGraph); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(HardForkInfo); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint8_t, version); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(hard_fork_info, info); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBans); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetBans); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(FlushTransactionPool); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetOutputHistogram); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, amounts); + RPC_MESSAGE_MEMBER(uint64_t, min_count); + RPC_MESSAGE_MEMBER(uint64_t, max_count); + RPC_MESSAGE_MEMBER(bool, unlocked); + RPC_MESSAGE_MEMBER(uint64_t, recent_cutoff); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, histogram); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetOutputKeys); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, outputs); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, keys); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint32_t, version); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, num_grace_blocks); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint64_t, estimated_fee_per_kb); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h new file mode 100644 index 000000000..b354b32c2 --- /dev/null +++ b/src/rpc/daemon_rpc_version.h @@ -0,0 +1,44 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace cryptonote +{ + +namespace rpc +{ + +static const uint32_t DAEMON_RPC_VERSION_MINOR = 0; +static const uint32_t DAEMON_RPC_VERSION_MAJOR = 1; + +static const uint32_t DAEMON_RPC_VERSION = DAEMON_RPC_VERSION_MINOR + (DAEMON_RPC_VERSION_MAJOR << 16); + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp new file mode 100644 index 000000000..7d4499bcf --- /dev/null +++ b/src/rpc/message.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "message.h" +#include "daemon_rpc_version.h" +#include "serialization/json_object.h" + +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +namespace cryptonote +{ + +namespace rpc +{ + +const char* Message::STATUS_OK = "OK"; +const char* Message::STATUS_RETRY = "Retry"; +const char* Message::STATUS_FAILED = "Failed"; +const char* Message::STATUS_BAD_REQUEST = "Invalid request type"; +const char* Message::STATUS_BAD_JSON = "Malformed json"; + +rapidjson::Value Message::toJson(rapidjson::Document& doc) const +{ + rapidjson::Value val(rapidjson::kObjectType); + + auto& al = doc.GetAllocator(); + + val.AddMember("status", rapidjson::StringRef(status.c_str()), al); + val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al); + INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION); + + return val; +} + +void Message::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, status, status); + GET_FROM_JSON_OBJECT(val, error_details, error_details); + GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version); +} + + +FullMessage::FullMessage(const std::string& request, Message* message) +{ + doc.SetObject(); + + doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator()); + doc.AddMember("params", message->toJson(doc), doc.GetAllocator()); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator()); +} + +FullMessage::FullMessage(Message* message) +{ + doc.SetObject(); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", "2.0", doc.GetAllocator()); + + if (message->status == Message::STATUS_OK) + { + doc.AddMember("response", message->toJson(doc), doc.GetAllocator()); + } + else + { + cryptonote::rpc::error err; + + err.error_str = message->status; + err.message = message->error_details; + + INSERT_INTO_JSON_OBJECT(doc, doc, error, err); + } +} + +FullMessage::FullMessage(const std::string& json_string, bool request) +{ + doc.Parse(json_string.c_str()); + if (doc.HasParseError()) + { + throw cryptonote::json::PARSE_FAIL(); + } + + OBJECT_HAS_MEMBER_OR_THROW(doc, "jsonrpc") + + if (request) + { + OBJECT_HAS_MEMBER_OR_THROW(doc, "method") + OBJECT_HAS_MEMBER_OR_THROW(doc, "params") + } + else + { + if (!doc.HasMember("response") && !doc.HasMember("error")) + { + throw cryptonote::json::MISSING_KEY("error/response"); + } + } +} + +std::string FullMessage::getJson() +{ + + if (!doc.HasMember("id")) + { + doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator()); + } + + rapidjson::StringBuffer buf; + + rapidjson::Writer writer(buf); + + doc.Accept(writer); + + return std::string(buf.GetString(), buf.GetSize()); +} + +std::string FullMessage::getRequestType() const +{ + OBJECT_HAS_MEMBER_OR_THROW(doc, "method") + return doc["method"].GetString(); +} + +rapidjson::Value& FullMessage::getMessage() +{ + if (doc.HasMember("params")) + { + return doc["params"]; + } + else if (doc.HasMember("response")) + { + return doc["response"]; + } + + //else + OBJECT_HAS_MEMBER_OR_THROW(doc, "error") + return doc["error"]; + +} + +rapidjson::Value FullMessage::getMessageCopy() +{ + rapidjson::Value& val = getMessage(); + + return rapidjson::Value(val, doc.GetAllocator()); +} + +rapidjson::Value& FullMessage::getID() +{ + OBJECT_HAS_MEMBER_OR_THROW(doc, "id") + return doc["id"]; +} + +void FullMessage::setID(rapidjson::Value& id) +{ + auto itr = doc.FindMember("id"); + if (itr != doc.MemberEnd()) + { + itr->value = id; + } + else + { + doc.AddMember("id", id, doc.GetAllocator()); + } +} + +cryptonote::rpc::error FullMessage::getError() +{ + cryptonote::rpc::error err; + err.use = false; + if (doc.HasMember("error")) + { + GET_FROM_JSON_OBJECT(doc, err, error); + err.use = true; + } + + return err; +} + +FullMessage FullMessage::requestMessage(const std::string& request, Message* message) +{ + return FullMessage(request, message); +} + +FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id) +{ + auto mes = requestMessage(request, message); + mes.setID(id); + return mes; +} + +FullMessage FullMessage::responseMessage(Message* message) +{ + return FullMessage(message); +} + +FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id) +{ + auto mes = responseMessage(message); + mes.setID(id); + return mes; +} + +FullMessage* FullMessage::timeoutMessage() +{ + auto *full_message = new FullMessage(); + + auto& doc = full_message->doc; + auto& al = full_message->doc.GetAllocator(); + + doc.SetObject(); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", "2.0", al); + + cryptonote::rpc::error err; + + err.error_str = "RPC request timed out."; + INSERT_INTO_JSON_OBJECT(doc, doc, err, err); + + return full_message; +} + +// convenience functions for bad input +std::string BAD_REQUEST(const std::string& request) +{ + Message fail; + fail.status = Message::STATUS_BAD_REQUEST; + fail.error_details = std::string("\"") + request + "\" is not a valid request."; + + FullMessage fail_response = FullMessage::responseMessage(&fail); + + return fail_response.getJson(); +} + +std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id) +{ + Message fail; + fail.status = Message::STATUS_BAD_REQUEST; + fail.error_details = std::string("\"") + request + "\" is not a valid request."; + + FullMessage fail_response = FullMessage::responseMessage(&fail, id); + + return fail_response.getJson(); +} + +std::string BAD_JSON(const std::string& error_details) +{ + Message fail; + fail.status = Message::STATUS_BAD_JSON; + fail.error_details = error_details; + + FullMessage fail_response = FullMessage::responseMessage(&fail); + + return fail_response.getJson(); +} + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message.h b/src/rpc/message.h new file mode 100644 index 000000000..d1abe3fbe --- /dev/null +++ b/src/rpc/message.h @@ -0,0 +1,131 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "rapidjson/document.h" +#include "rpc/message_data_structs.h" +#include + +/* I normally hate using macros, but in this case it would be untenably + * verbose to not use a macro. This macro saves the trouble of explicitly + * writing the below if block for every single RPC call. + */ +#define REQ_RESP_TYPES_MACRO( runtime_str, type, reqjson, resp_message_ptr, handler) \ + \ + if (runtime_str == type::name) \ + { \ + type::Request reqvar; \ + type::Response *respvar = new type::Response(); \ + \ + reqvar.fromJson(reqjson); \ + \ + handler(reqvar, *respvar); \ + \ + resp_message_ptr = respvar; \ + } + +namespace cryptonote +{ + +namespace rpc +{ + + class Message + { + public: + static const char* STATUS_OK; + static const char* STATUS_RETRY; + static const char* STATUS_FAILED; + static const char* STATUS_BAD_REQUEST; + static const char* STATUS_BAD_JSON; + + Message() : status(STATUS_OK) { } + + virtual ~Message() { } + + virtual rapidjson::Value toJson(rapidjson::Document& doc) const; + + virtual void fromJson(rapidjson::Value& val); + + std::string status; + std::string error_details; + uint32_t rpc_version; + }; + + class FullMessage + { + public: + ~FullMessage() { } + + FullMessage(FullMessage&& rhs) noexcept : doc(std::move(rhs.doc)) { } + + FullMessage(const std::string& json_string, bool request=false); + + std::string getJson(); + + std::string getRequestType() const; + + rapidjson::Value& getMessage(); + + rapidjson::Value getMessageCopy(); + + rapidjson::Value& getID(); + + void setID(rapidjson::Value& id); + + cryptonote::rpc::error getError(); + + static FullMessage requestMessage(const std::string& request, Message* message); + static FullMessage requestMessage(const std::string& request, Message* message, rapidjson::Value& id); + + static FullMessage responseMessage(Message* message); + static FullMessage responseMessage(Message* message, rapidjson::Value& id); + + static FullMessage* timeoutMessage(); + private: + + FullMessage() = default; + + FullMessage(const std::string& request, Message* message); + FullMessage(Message* message); + + rapidjson::Document doc; + }; + + + // convenience functions for bad input + std::string BAD_REQUEST(const std::string& request); + std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id); + + std::string BAD_JSON(const std::string& error_details); + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h new file mode 100644 index 000000000..c2492681d --- /dev/null +++ b/src/rpc/message_data_structs.h @@ -0,0 +1,169 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "crypto/hash.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "ringct/rctSigs.h" + +#include +#include + +namespace cryptonote +{ + +namespace rpc +{ + + struct block_with_transactions + { + cryptonote::block block; + std::unordered_map transactions; + }; + + typedef std::vector tx_output_indices; + + typedef std::vector block_output_indices; + + struct transaction_info + { + cryptonote::transaction transaction; + bool in_pool; + uint64_t height; + }; + + struct output_key_and_amount_index + { + uint64_t amount_index; + crypto::public_key key; + }; + + typedef std::vector outputs_for_amount; + + struct amount_with_random_outputs + { + uint64_t amount; + outputs_for_amount outputs; + }; + + struct peer + { + uint64_t id; + uint32_t ip; + uint16_t port; + uint64_t last_seen; + }; + + struct tx_in_pool + { + cryptonote::transaction tx; + crypto::hash tx_hash; + uint64_t blob_size; + uint64_t fee; + crypto::hash max_used_block_hash; + uint64_t max_used_block_height; + bool kept_by_block; + crypto::hash last_failed_block_hash; + uint64_t last_failed_block_height; + uint64_t receive_time; + uint64_t last_relayed_time; + bool relayed; + bool do_not_relay; + }; + + typedef std::unordered_map > key_images_with_tx_hashes; + + struct output_amount_count + { + uint64_t amount; + uint64_t total_count; + uint64_t unlocked_count; + uint64_t recent_count; + }; + + struct output_amount_and_index + { + uint64_t amount; + uint64_t index; + }; + + struct output_key_mask_unlocked + { + crypto::public_key key; + rct::key mask; + bool unlocked; + }; + + struct hard_fork_info + { + uint8_t version; + bool enabled; + uint32_t window; + uint32_t votes; + uint32_t threshold; + uint8_t voting; + uint32_t state; + uint64_t earliest_height; + }; + + //required by JSON-RPC 2.0 spec + struct error + { + // not really using code, maybe later. + error() : use(false), code(1) { } + + bool use; // do not serialize + + int32_t code; + + // not required by spec, but int error codes aren't perfect + std::string error_str; + + std::string message; + + //TODO: data member? not required, may want later. + }; + + struct BlockHeaderResponse + { + uint64_t major_version; + uint64_t minor_version; + uint64_t timestamp; + crypto::hash prev_id; + uint32_t nonce; + uint64_t height; + uint64_t depth; + crypto::hash hash; + uint64_t difficulty; + uint64_t reward; + }; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h new file mode 100644 index 000000000..d75180199 --- /dev/null +++ b/src/rpc/rpc_handler.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include + +namespace cryptonote +{ + +namespace rpc +{ + + +class RpcHandler +{ + public: + + virtual std::string handle(const std::string& request) = 0; + + RpcHandler() { } + + virtual ~RpcHandler() { } +}; + + +} // rpc + +} // cryptonote diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp new file mode 100644 index 000000000..8e1e47b38 --- /dev/null +++ b/src/rpc/zmq_server.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "zmq_server.h" +#include + +namespace cryptonote +{ + +namespace rpc +{ + +ZmqServer::ZmqServer(RpcHandler& h) : + handler(h), + stop_signal(false), + running(false), + context(DEFAULT_NUM_ZMQ_THREADS) // TODO: make this configurable +{ +} + +ZmqServer::~ZmqServer() +{ + for (zmq::socket_t* socket : sockets) + { + delete socket; + } +} + +void ZmqServer::serve() +{ + + while (1) + { + try + { + for (zmq::socket_t* socket : sockets) + { + zmq::message_t message; + + while (socket->recv(&message)) + { + std::string message_string(reinterpret_cast(message.data()), message.size()); + + MDEBUG(std::string("Received RPC request: \"") + message_string + "\""); + + std::string response = handler.handle(message_string); + + zmq::message_t reply(response.size()); + memcpy((void *) reply.data(), response.c_str(), response.size()); + + socket->send(reply); + MDEBUG(std::string("Sent RPC reply: \"") + response + "\""); + } + + } + } + catch (boost::thread_interrupted& e) + { + MDEBUG("ZMQ Server thread interrupted."); + } + boost::this_thread::interruption_point(); + } +} + +bool ZmqServer::addIPCSocket(std::string address, std::string port) +{ + MERROR("ZmqServer::addIPCSocket not yet implemented!"); + return false; +} + +bool ZmqServer::addTCPSocket(std::string address, std::string port) +{ + zmq::socket_t *new_socket = nullptr; + try + { + std::string addr_prefix("tcp://"); + + new_socket = new zmq::socket_t(context, ZMQ_REP); + + new_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS); + + std::string bind_address = addr_prefix + address + std::string(":") + port; + new_socket->bind(bind_address.c_str()); + sockets.push_back(new_socket); + } + catch (std::exception& e) + { + MERROR(std::string("Error creating ZMQ Socket: ") + e.what()); + if (new_socket) + { + delete new_socket; + } + return false; + } + return true; +} + +void ZmqServer::run() +{ + running = true; + run_thread = boost::thread(boost::bind(&ZmqServer::serve, this)); +} + +void ZmqServer::stop() +{ + if (!running) return; + + stop_signal = true; + + run_thread.interrupt(); + run_thread.join(); + + running = false; + + return; +} + + +} // namespace cryptonote + +} // namespace rpc diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h new file mode 100644 index 000000000..964ce2071 --- /dev/null +++ b/src/rpc/zmq_server.h @@ -0,0 +1,84 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include +#include "common/command_line.h" + +#include +#include + +#include "rpc_handler.h" + +namespace cryptonote +{ + +namespace rpc +{ + +static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1; +static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000; + +class ZmqServer +{ + public: + + ZmqServer(RpcHandler& h); + + ~ZmqServer(); + + static void init_options(boost::program_options::options_description& desc); + + void serve(); + + bool addIPCSocket(std::string address, std::string port); + bool addTCPSocket(std::string address, std::string port); + + void run(); + void stop(); + + private: + RpcHandler& handler; + + volatile bool stop_signal; + volatile bool running; + + zmq::context_t context; + + boost::thread run_thread; + + std::vector sockets; +}; + + +} // namespace cryptonote + +} // namespace rpc diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt new file mode 100644 index 000000000..e4aa0b6a2 --- /dev/null +++ b/src/serialization/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2016, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(serialization_sources + json_object.cpp) + +set(serialization_headers) + +set(serialization_private_headers + json_object.h) + +monero_private_headers(serialization + ${serialization_private_headers}) +monero_add_library(serialization + ${serialization_sources} + ${serialization_headers} + ${serialization_private_headers}) +target_link_libraries(serialization + LINK_PRIVATE + cryptonote_core + cryptonote_protocol + ${Boost_CHRONO_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${EXTRA_LIBRARIES}) +add_dependencies(serialization + version) + diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp new file mode 100644 index 000000000..50ef414f4 --- /dev/null +++ b/src/serialization/json_object.cpp @@ -0,0 +1,1068 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "json_object.h" + +#include "string_tools.h" + +namespace cryptonote +{ + +namespace json +{ + +void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i.c_str(), doc.GetAllocator()); +} + +void fromJsonValue(const rapidjson::Value& val, std::string& str) +{ + if (!val.IsString()) + { + throw WRONG_TYPE("string"); + } + + str = val.GetString(); +} + +void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val) +{ + val.SetBool(i); +} + +void fromJsonValue(const rapidjson::Value& val, bool& b) +{ + if (!val.IsBool()) + { + throw WRONG_TYPE("boolean"); + } + b = val.GetBool(); +} + +void toJsonValue(rapidjson::Document& doc, const uint8_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, uint8_t& i) +{ + if (!val.IsUint()) + { + throw WRONG_TYPE("unsigned integer"); + } + + i = (uint8_t)( val.GetUint() & 0xFF); +} + +void toJsonValue(rapidjson::Document& doc, const int8_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, int8_t& i) +{ + if (!val.IsInt()) + { + throw WRONG_TYPE("integer"); + } + + i = (int8_t) ( val.GetInt() & 0xFF); +} + +void toJsonValue(rapidjson::Document& doc, const uint16_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, uint16_t& i) +{ + if (!val.IsUint()) + { + throw WRONG_TYPE("unsigned integer"); + } + + i = (uint16_t) ( val.GetUint() & 0xFFFF); +} + +void toJsonValue(rapidjson::Document& doc, const int32_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, int32_t& i) +{ + if (!val.IsInt()) + { + throw WRONG_TYPE("signed integer"); + } + + i = val.GetInt(); +} + +void toJsonValue(rapidjson::Document& doc, const uint32_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, uint32_t& i) +{ + if (!val.IsUint()) + { + throw WRONG_TYPE("unsigned integer"); + } + + i = val.GetUint(); +} + +void toJsonValue(rapidjson::Document& doc, const uint64_t& i, rapidjson::Value& val) +{ + val = rapidjson::Value(i); +} + + +void fromJsonValue(const rapidjson::Value& val, uint64_t& i) +{ + if (!val.IsUint64()) + { + throw WRONG_TYPE("unsigned integer"); + } + + i = val.GetUint64(); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version); + INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time); + INSERT_INTO_JSON_OBJECT(val, doc, vin, tx.vin); + INSERT_INTO_JSON_OBJECT(val, doc, vout, tx.vout); + INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra); + INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures); + INSERT_INTO_JSON_OBJECT(val, doc, rct_signatures, tx.rct_signatures); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, tx.version, version); + GET_FROM_JSON_OBJECT(val, tx.unlock_time, unlock_time); + GET_FROM_JSON_OBJECT(val, tx.vin, vin); + GET_FROM_JSON_OBJECT(val, tx.vout, vout); + GET_FROM_JSON_OBJECT(val, tx.extra, extra); + GET_FROM_JSON_OBJECT(val, tx.signatures, signatures); + GET_FROM_JSON_OBJECT(val, tx.rct_signatures, rct_signatures); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, major_version, b.major_version); + INSERT_INTO_JSON_OBJECT(val, doc, minor_version, b.minor_version); + INSERT_INTO_JSON_OBJECT(val, doc, timestamp, b.timestamp); + INSERT_INTO_JSON_OBJECT(val, doc, prev_id, b.prev_id); + INSERT_INTO_JSON_OBJECT(val, doc, nonce, b.nonce); + INSERT_INTO_JSON_OBJECT(val, doc, miner_tx, b.miner_tx); + INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, b.tx_hashes); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, b.major_version, major_version); + GET_FROM_JSON_OBJECT(val, b.minor_version, minor_version); + GET_FROM_JSON_OBJECT(val, b.timestamp, timestamp); + GET_FROM_JSON_OBJECT(val, b.prev_id, prev_id); + GET_FROM_JSON_OBJECT(val, b.nonce, nonce); + GET_FROM_JSON_OBJECT(val, b.miner_tx, miner_tx); + GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val) +{ + val.SetObject(); + + if (txin.type() == typeid(cryptonote::txin_gen)) + { + val.AddMember("type", "txin_gen", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); + } + else if (txin.type() == typeid(cryptonote::txin_to_script)) + { + val.AddMember("type", "txin_to_script", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); + } + else if (txin.type() == typeid(cryptonote::txin_to_scripthash)) + { + val.AddMember("type", "txin_to_scripthash", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); + } + else if (txin.type() == typeid(cryptonote::txin_to_key)) + { + val.AddMember("type", "txin_to_key", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txin)); + } +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + OBJECT_HAS_MEMBER_OR_THROW(val, "type") + OBJECT_HAS_MEMBER_OR_THROW(val, "value") + if (val["type"]== "txin_gen") + { + cryptonote::txin_gen tmpVal; + fromJsonValue(val["value"], tmpVal); + txin = tmpVal; + } + else if (val["type"]== "txin_to_script") + { + cryptonote::txin_to_script tmpVal; + fromJsonValue(val["value"], tmpVal); + txin = tmpVal; + } + else if (val["type"] == "txin_to_scripthash") + { + cryptonote::txin_to_scripthash tmpVal; + fromJsonValue(val["value"], tmpVal); + txin = tmpVal; + } + else if (val["type"] == "txin_to_key") + { + cryptonote::txin_to_key tmpVal; + fromJsonValue(val["value"], tmpVal); + txin = tmpVal; + } +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, txin.height); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txin.height, height); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev); + INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout); + INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txin.prev, prev); + GET_FROM_JSON_OBJECT(val, txin.prevout, prevout); + GET_FROM_JSON_OBJECT(val, txin.sigset, sigset); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev); + INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout); + INSERT_INTO_JSON_OBJECT(val, doc, script, txin.script); + INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txin.prev, prev); + GET_FROM_JSON_OBJECT(val, txin.prevout, prevout); + GET_FROM_JSON_OBJECT(val, txin.script, script); + GET_FROM_JSON_OBJECT(val, txin.sigset, sigset); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount); + INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets); + INSERT_INTO_JSON_OBJECT(val, doc, k_image, txin.k_image); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txin.amount, amount); + GET_FROM_JSON_OBJECT(val, txin.key_offsets, key_offsets); + GET_FROM_JSON_OBJECT(val, txin.k_image, k_image); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val) +{ + val.SetObject(); + + if (txout.type() == typeid(cryptonote::txout_to_script)) + { + val.AddMember("type", "txout_to_script", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); + } + else if (txout.type() == typeid(cryptonote::txout_to_scripthash)) + { + val.AddMember("type", "txout_to_scripthash", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); + } + else if (txout.type() == typeid(cryptonote::txout_to_key)) + { + val.AddMember("type", "txout_to_key", doc.GetAllocator()); + INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get(txout)); + } +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + OBJECT_HAS_MEMBER_OR_THROW(val, "type") + OBJECT_HAS_MEMBER_OR_THROW(val, "value") + if (val["type"]== "txout_to_script") + { + cryptonote::txout_to_script tmpVal; + fromJsonValue(val["value"], tmpVal); + txout = tmpVal; + } + else if (val["type"] == "txout_to_scripthash") + { + cryptonote::txout_to_scripthash tmpVal; + fromJsonValue(val["value"], tmpVal); + txout = tmpVal; + } + else if (val["type"] == "txout_to_key") + { + cryptonote::txout_to_key tmpVal; + fromJsonValue(val["value"], tmpVal); + txout = tmpVal; + } +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, keys, txout.keys); + INSERT_INTO_JSON_OBJECT(val, doc, script, txout.script); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txout.keys, keys); + GET_FROM_JSON_OBJECT(val, txout.script, script); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, hash, txout.hash); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txout.hash, hash); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, key, txout.key); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txout.key, key); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount); + INSERT_INTO_JSON_OBJECT(val, doc, target, txout.target); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, txout.amount, amount); + GET_FROM_JSON_OBJECT(val, txout.target, target); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val) +{ + val.SetObject(); + + auto& al = doc.GetAllocator(); + INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming); + INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost); + INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip); + + INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip); + INSERT_INTO_JSON_OBJECT(val, doc, port, info.port); + + INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id); + + INSERT_INTO_JSON_OBJECT(val, doc, recv_count, info.recv_count); + INSERT_INTO_JSON_OBJECT(val, doc, recv_idle_time, info.recv_idle_time); + + INSERT_INTO_JSON_OBJECT(val, doc, send_count, info.send_count); + INSERT_INTO_JSON_OBJECT(val, doc, send_idle_time, info.send_idle_time); + + INSERT_INTO_JSON_OBJECT(val, doc, state, info.state); + + INSERT_INTO_JSON_OBJECT(val, doc, live_time, info.live_time); + + INSERT_INTO_JSON_OBJECT(val, doc, avg_download, info.avg_download); + INSERT_INTO_JSON_OBJECT(val, doc, current_download, info.current_download); + + INSERT_INTO_JSON_OBJECT(val, doc, avg_upload, info.avg_upload); + INSERT_INTO_JSON_OBJECT(val, doc, current_upload, info.current_upload); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, info.incoming, incoming); + GET_FROM_JSON_OBJECT(val, info.localhost, localhost); + GET_FROM_JSON_OBJECT(val, info.local_ip, local_ip); + + GET_FROM_JSON_OBJECT(val, info.ip, ip); + GET_FROM_JSON_OBJECT(val, info.port, port); + + GET_FROM_JSON_OBJECT(val, info.peer_id, peer_id); + + GET_FROM_JSON_OBJECT(val, info.recv_count, recv_count); + GET_FROM_JSON_OBJECT(val, info.recv_idle_time, recv_idle_time); + + GET_FROM_JSON_OBJECT(val, info.send_count, send_count); + GET_FROM_JSON_OBJECT(val, info.send_idle_time, send_idle_time); + + GET_FROM_JSON_OBJECT(val, info.state, state); + + GET_FROM_JSON_OBJECT(val, info.live_time, live_time); + + GET_FROM_JSON_OBJECT(val, info.avg_download, avg_download); + GET_FROM_JSON_OBJECT(val, info.current_download, current_download); + + GET_FROM_JSON_OBJECT(val, info.avg_upload, avg_upload); + GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block); + INSERT_INTO_JSON_OBJECT(val, doc, txs, blk.txs); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, blk.block, block); + GET_FROM_JSON_OBJECT(val, blk.txs, txs); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block); + INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.transactions); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, blk.block, block); + GET_FROM_JSON_OBJECT(val, blk.transactions, transactions); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, tx_info.height); + INSERT_INTO_JSON_OBJECT(val, doc, in_pool, tx_info.in_pool); + INSERT_INTO_JSON_OBJECT(val, doc, transaction, tx_info.transaction); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, tx_info.height, height); + GET_FROM_JSON_OBJECT(val, tx_info.in_pool, in_pool); + GET_FROM_JSON_OBJECT(val, tx_info.transaction, transaction); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount_index, out.amount_index); + INSERT_INTO_JSON_OBJECT(val, doc, key, out.key); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, out.amount_index, amount_index); + GET_FROM_JSON_OBJECT(val, out.key, key); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount); + INSERT_INTO_JSON_OBJECT(val, doc, outputs, out.outputs); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, out.amount, amount); + GET_FROM_JSON_OBJECT(val, out.outputs, outputs); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, id, peer.id); + INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip); + INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port); + INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, peer.id, id); + GET_FROM_JSON_OBJECT(val, peer.ip, ip); + GET_FROM_JSON_OBJECT(val, peer.port, port); + GET_FROM_JSON_OBJECT(val, peer.last_seen, last_seen); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx); + INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash); + INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size); + INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee); + INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash); + INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height); + INSERT_INTO_JSON_OBJECT(val, doc, kept_by_block, tx.kept_by_block); + INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_hash, tx.last_failed_block_hash); + INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_height, tx.last_failed_block_height); + INSERT_INTO_JSON_OBJECT(val, doc, receive_time, tx.receive_time); + INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time); + INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed); + INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, tx.tx, tx); + GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size); + GET_FROM_JSON_OBJECT(val, tx.fee, fee); + GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash); + GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height); + GET_FROM_JSON_OBJECT(val, tx.kept_by_block, kept_by_block); + GET_FROM_JSON_OBJECT(val, tx.last_failed_block_hash, last_failed_block_hash); + GET_FROM_JSON_OBJECT(val, tx.last_failed_block_height, last_failed_block_height); + GET_FROM_JSON_OBJECT(val, tx.receive_time, receive_time); + GET_FROM_JSON_OBJECT(val, tx.last_relayed_time, last_relayed_time); + GET_FROM_JSON_OBJECT(val, tx.relayed, relayed); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, info.version); + INSERT_INTO_JSON_OBJECT(val, doc, enabled, info.enabled); + INSERT_INTO_JSON_OBJECT(val, doc, window, info.window); + INSERT_INTO_JSON_OBJECT(val, doc, votes, info.votes); + INSERT_INTO_JSON_OBJECT(val, doc, threshold, info.threshold); + INSERT_INTO_JSON_OBJECT(val, doc, voting, info.voting); + INSERT_INTO_JSON_OBJECT(val, doc, state, info.state); + INSERT_INTO_JSON_OBJECT(val, doc, earliest_height, info.earliest_height); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, info.version, version); + GET_FROM_JSON_OBJECT(val, info.enabled, enabled); + GET_FROM_JSON_OBJECT(val, info.window, window); + GET_FROM_JSON_OBJECT(val, info.votes, votes); + GET_FROM_JSON_OBJECT(val, info.threshold, threshold); + GET_FROM_JSON_OBJECT(val, info.voting, voting); + GET_FROM_JSON_OBJECT(val, info.state, state); + GET_FROM_JSON_OBJECT(val, info.earliest_height, earliest_height); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount); + INSERT_INTO_JSON_OBJECT(val, doc, total_count, out.total_count); + INSERT_INTO_JSON_OBJECT(val, doc, unlocked_count, out.unlocked_count); + INSERT_INTO_JSON_OBJECT(val, doc, recent_count, out.recent_count); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, out.amount, amount); + GET_FROM_JSON_OBJECT(val, out.total_count, total_count); + GET_FROM_JSON_OBJECT(val, out.unlocked_count, unlocked_count); + GET_FROM_JSON_OBJECT(val, out.recent_count, recent_count); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount); + INSERT_INTO_JSON_OBJECT(val, doc, index, out.index); +} + + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, out.amount, amount); + GET_FROM_JSON_OBJECT(val, out.index, index); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, key, out.key); + INSERT_INTO_JSON_OBJECT(val, doc, mask, out.mask); + INSERT_INTO_JSON_OBJECT(val, doc, unlocked, out.unlocked); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, out.key, key); + GET_FROM_JSON_OBJECT(val, out.mask, mask); + GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, code, err.code); + INSERT_INTO_JSON_OBJECT(val, doc, error_str, err.error_str); + INSERT_INTO_JSON_OBJECT(val, doc, message, err.message); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, error.code, code); + GET_FROM_JSON_OBJECT(val, error.error_str, error_str); + GET_FROM_JSON_OBJECT(val, error.message, message); +} + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, major_version, response.major_version); + INSERT_INTO_JSON_OBJECT(val, doc, minor_version, response.minor_version); + INSERT_INTO_JSON_OBJECT(val, doc, timestamp, response.timestamp); + INSERT_INTO_JSON_OBJECT(val, doc, prev_id, response.prev_id); + INSERT_INTO_JSON_OBJECT(val, doc, nonce, response.nonce); + INSERT_INTO_JSON_OBJECT(val, doc, height, response.height); + INSERT_INTO_JSON_OBJECT(val, doc, depth, response.depth); + INSERT_INTO_JSON_OBJECT(val, doc, hash, response.hash); + INSERT_INTO_JSON_OBJECT(val, doc, difficulty, response.difficulty); + INSERT_INTO_JSON_OBJECT(val, doc, reward, response.reward); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, response.major_version, major_version); + GET_FROM_JSON_OBJECT(val, response.minor_version, minor_version); + GET_FROM_JSON_OBJECT(val, response.timestamp, timestamp); + GET_FROM_JSON_OBJECT(val, response.prev_id, prev_id); + GET_FROM_JSON_OBJECT(val, response.nonce, nonce); + GET_FROM_JSON_OBJECT(val, response.height, height); + GET_FROM_JSON_OBJECT(val, response.depth, depth); + GET_FROM_JSON_OBJECT(val, response.hash, hash); + GET_FROM_JSON_OBJECT(val, response.difficulty, difficulty); + GET_FROM_JSON_OBJECT(val, response.reward, reward); +} + +void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type); + INSERT_INTO_JSON_OBJECT(val, doc, message, sig.message); + INSERT_INTO_JSON_OBJECT(val, doc, mixRing, sig.mixRing); + INSERT_INTO_JSON_OBJECT(val, doc, pseudoOuts, sig.pseudoOuts); + INSERT_INTO_JSON_OBJECT(val, doc, ecdhInfo, sig.ecdhInfo); + INSERT_INTO_JSON_OBJECT(val, doc, outPk, sig.outPk); + INSERT_INTO_JSON_OBJECT(val, doc, txnFee, sig.txnFee); + INSERT_INTO_JSON_OBJECT(val, doc, p, sig.p); +} + +void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, sig.type, type); + GET_FROM_JSON_OBJECT(val, sig.message, message); + GET_FROM_JSON_OBJECT(val, sig.mixRing, mixRing); + GET_FROM_JSON_OBJECT(val, sig.pseudoOuts, pseudoOuts); + GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, ecdhInfo); + GET_FROM_JSON_OBJECT(val, sig.outPk, outPk); + GET_FROM_JSON_OBJECT(val, sig.txnFee, txnFee); + GET_FROM_JSON_OBJECT(val, sig.p, p); +} + +void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, dest, key.dest); + INSERT_INTO_JSON_OBJECT(val, doc, mask, key.mask); +} + +void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + GET_FROM_JSON_OBJECT(val, key.dest, dest); + GET_FROM_JSON_OBJECT(val, key.mask, mask); +} + +void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, mask, tuple.mask); + INSERT_INTO_JSON_OBJECT(val, doc, amount, tuple.amount); +} + +void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, tuple.mask, mask); + GET_FROM_JSON_OBJECT(val, tuple.amount, amount); +} + +void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs); + INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs); +} + +void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs); + GET_FROM_JSON_OBJECT(val, sig.MGs, MGs); +} + +void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, asig, sig.asig); + + std::vector keyVector(sig.Ci, std::end(sig.Ci)); + INSERT_INTO_JSON_OBJECT(val, doc, Ci, keyVector); +} + +void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, sig.asig, asig); + + std::vector keyVector; + cryptonote::json::fromJsonValue(val["Ci"], keyVector); + if (!(keyVector.size() == 64)) + { + throw WRONG_TYPE("key64 (rct::key[64])"); + } + for (size_t i=0; i < 64; i++) + { + sig.Ci[i] = keyVector[i]; + } +} + +void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val) +{ + val.SetObject(); + + std::vector keyVector(sig.s0, std::end(sig.s0)); + INSERT_INTO_JSON_OBJECT(val, doc, s0, sig.s0); + + keyVector.assign(sig.s1, std::end(sig.s1)); + INSERT_INTO_JSON_OBJECT(val, doc, s1, sig.s1); + + INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee); +} + +void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + OBJECT_HAS_MEMBER_OR_THROW(val, "s0") + std::vector keyVector; + cryptonote::json::fromJsonValue(val["s0"], keyVector); + if (!(keyVector.size() == 64)) + { + throw WRONG_TYPE("key64 (rct::key[64])"); + } + for (size_t i=0; i < 64; i++) + { + sig.s0[i] = keyVector[i]; + } + + OBJECT_HAS_MEMBER_OR_THROW(val, "s1") + keyVector.clear(); + cryptonote::json::fromJsonValue(val["s1"], keyVector); + if (!(keyVector.size() == 64)) + { + throw WRONG_TYPE("key64 (rct::key[64])"); + } + for (size_t i=0; i < 64; i++) + { + sig.s1[i] = keyVector[i]; + } + + GET_FROM_JSON_OBJECT(val, sig.ee, ee); +} + +void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val) +{ + val.SetObject(); + + INSERT_INTO_JSON_OBJECT(val, doc, ss, sig.ss); + INSERT_INTO_JSON_OBJECT(val, doc, cc, sig.cc); +} + +void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("key64 (rct::key[64])"); + } + + GET_FROM_JSON_OBJECT(val, sig.ss, ss); + GET_FROM_JSON_OBJECT(val, sig.cc, cc); +} + +} // namespace json + +} // namespace cryptonote diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h new file mode 100644 index 000000000..8e0da9aa5 --- /dev/null +++ b/src/serialization/json_object.h @@ -0,0 +1,344 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "rapidjson/document.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "rpc/message_data_structs.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "common/sfinae_helpers.h" + +#define OBJECT_HAS_MEMBER_OR_THROW(val, key) \ + do \ + { \ + if (!val.HasMember(key)) \ + { \ + throw cryptonote::json::MISSING_KEY(key); \ + } \ + } while (0); + +#define INSERT_INTO_JSON_OBJECT(jsonVal, doc, key, source) \ + rapidjson::Value key##Val; \ + cryptonote::json::toJsonValue(doc, source, key##Val); \ + jsonVal.AddMember(#key, key##Val, doc.GetAllocator()); + +#define GET_FROM_JSON_OBJECT(source, dst, key) \ + OBJECT_HAS_MEMBER_OR_THROW(source, #key) \ + decltype(dst) dstVal##key; \ + cryptonote::json::fromJsonValue(source[#key], dstVal##key); \ + dst = dstVal##key; + +namespace cryptonote +{ + +namespace json +{ + +struct JSON_ERROR : public std::exception +{ + protected: + JSON_ERROR() { } + std::string m; + + public: + virtual ~JSON_ERROR() { } + + const char* what() const throw() + { + return m.c_str(); + } +}; + +struct MISSING_KEY : public JSON_ERROR +{ + MISSING_KEY(const char* key) + { + m = std::string("Key \"") + key + "\" missing from object."; + } +}; + +struct WRONG_TYPE : public JSON_ERROR +{ + WRONG_TYPE(const char* type) + { + m = std::string("Json value has incorrect type, expected: ") + type; + } +}; + +struct BAD_INPUT : public JSON_ERROR +{ + BAD_INPUT() + { + m = "An item failed to convert from json object to native object"; + } +}; + +struct PARSE_FAIL : public JSON_ERROR +{ + PARSE_FAIL() + { + m = "Failed to parse the json request"; + } +}; + + +// POD to json value +template +typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value) +{ + value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator()); +} + +template +typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Type& t) +{ + if (!val.IsString()) + { + throw WRONG_TYPE("string"); + } + + //TODO: handle failure to convert hex string to POD type + bool success = epee::string_tools::hex_to_pod(val.GetString(), t); + + if (!success) + { + throw BAD_INPUT(); + } +} + +void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, std::string& str); + +void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, bool& b); + +void toJsonValue(rapidjson::Document& doc, const uint8_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, uint8_t& i); + +void toJsonValue(rapidjson::Document& doc, const int8_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, int8_t& i); + +void toJsonValue(rapidjson::Document& doc, const uint16_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, uint16_t& i); + +void toJsonValue(rapidjson::Document& doc, const int32_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, int32_t& i); + +void toJsonValue(rapidjson::Document& doc, const uint32_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, uint32_t& i); + +void toJsonValue(rapidjson::Document& doc, const uint64_t& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, uint64_t& i); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error); + +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response); + +void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig); + +void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key); + +void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple); + +void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig); + +void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig); + +void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); + +void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); + + +template +typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val); + +template +typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map); + +template +typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val); + +template +typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec); + + +// ideally would like to have the below functions in the .cpp file, but +// unfortunately because of how templates work they have to be here. + +template +typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val) +{ + val.SetObject(); + + auto& al = doc.GetAllocator(); + + for (const auto& i : map) + { + rapidjson::Value k; + rapidjson::Value m; + toJsonValue(doc, i.first, k); + toJsonValue(doc, i.second, m); + val.AddMember(k, m, al); + } +} + +template +typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + auto itr = val.MemberBegin(); + + while (itr != val.MemberEnd()) + { + typename Map::key_type k; + typename Map::mapped_type m; + fromJsonValue(itr->name, k); + fromJsonValue(itr->value, m); + map.emplace(k, m); + ++itr; + } +} + +template +typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val) +{ + val.SetArray(); + + for (const auto& t : vec) + { + rapidjson::Value v; + toJsonValue(doc, t, v); + val.PushBack(v, doc.GetAllocator()); + } +} + +template +typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec) +{ + if (!val.IsArray()) + { + throw WRONG_TYPE("json array"); + } + + for (rapidjson::SizeType i=0; i < val.Size(); i++) + { + typename Vec::value_type v; + fromJsonValue(val[i], v); + vec.push_back(v); + } +} + +} // namespace json + +} // namespace cryptonote From 0299cb77ca18073daf3cf371f8da013fb596ae48 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 5 Sep 2017 12:20:40 -0400 Subject: [PATCH 4/4] Fix various oversights/bugs in ZMQ RPC server code - Add some RPC commands (and touch up a couple others) - some bounds checking - some better pointer management - const correctness and error handling -- Thanks @vtnerd for type help with serialization and CMake changes --- CMakeLists.txt | 8 ++ src/daemon/command_line_args.h | 6 - src/daemon/main.cpp | 1 - src/rpc/CMakeLists.txt | 2 + src/rpc/daemon_handler.cpp | 171 +++++++++++++++++------ src/rpc/daemon_handler.h | 6 +- src/rpc/daemon_messages.cpp | 152 ++++++++++++++++---- src/rpc/daemon_messages.h | 47 +++---- src/rpc/daemon_rpc_version.h | 6 +- src/rpc/message.cpp | 2 +- src/rpc/message_data_structs.h | 20 +++ src/rpc/zmq_server.cpp | 51 +++---- src/rpc/zmq_server.h | 9 +- src/serialization/json_object.cpp | 225 +++++++++++++++++++++--------- src/serialization/json_object.h | 55 ++++++-- 15 files changed, 536 insertions(+), 225 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62db856f4..971c097ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -689,8 +689,16 @@ endif() include(version.cmake) +find_path(ZMQ_INCLUDE_PATH zmq.hpp) find_library(ZMQ_LIB zmq) +if(NOT ZMQ_INCLUDE_PATH) + message(FATAL_ERROR "Could not find required header zmq.hpp") +endif() +if(NOT ZMQ_LIB) + message(FATAL_ERROR "Could not find require libzmq") +endif() + function (treat_warnings_as_errors dirs) foreach(dir ${ARGV}) set_property(DIRECTORY ${dir} diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 797285354..f19d5cc63 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -83,12 +83,6 @@ namespace daemon_args , std::to_string(config::testnet::ZMQ_RPC_DEFAULT_PORT) }; - const command_line::arg_descriptor arg_zmq_restricted_rpc = { - "zmq-restricted-rpc" - , "Restrict ZMQ RPC to view only commands" - , false - }; - } // namespace daemon_args #endif // DAEMON_COMMAND_LINE_ARGS_H diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 0d93a49ff..b35a864e9 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -92,7 +92,6 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); command_line::add_arg(core_settings, daemon_args::arg_zmq_testnet_rpc_bind_port); - command_line::add_arg(core_settings, daemon_args::arg_zmq_restricted_rpc); daemonizer::init_options(hidden_options, visible_options); daemonize::t_executor::init_options(core_settings); diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 717e3e627..b5c38b1a8 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -115,6 +115,8 @@ target_link_libraries(daemon_rpc_server ${Boost_THREAD_LIBRARY} ${ZMQ_LIB} ${EXTRA_LIBRARIES}) +target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) +target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) add_dependencies(rpc diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 6a287b229..53eeb5e76 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -81,6 +81,14 @@ namespace rpc return; } + if (it->second.size() != bwt.block.tx_hashes.size()) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "incorrect number of transactions retrieved for block"; + return; + } std::list txs; for (const auto& blob : it->second) { @@ -90,7 +98,7 @@ namespace rpc res.blocks.clear(); res.output_indices.clear(); res.status = Message::STATUS_FAILED; - res.error_details = "failed retrieving a requested block"; + res.error_details = "failed retrieving a requested transaction"; return; } } @@ -404,56 +412,117 @@ namespace rpc void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res) { - res.status = Message::STATUS_FAILED; - res.error_details = "RPC method not yet implemented."; + account_public_address adr; + if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address)) + { + res.error_details = "Failed, wrong address"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4; + + // if we couldn't detect threads, set it to a ridiculously high number + if(concurrency_count == 0) + { + concurrency_count = 257; + } + + // if there are more threads requested than the hardware supports + // then we fail and log that. + if(req.threads_count > concurrency_count) + { + res.error_details = "Failed, too many threads relative to CPU cores."; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + if(!m_core.get_miner().start(adr, static_cast(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + { + res.error_details = "Failed, mining not started"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + res.status = Message::STATUS_OK; + res.error_details = ""; + } void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res) { - res.height = m_core.get_current_blockchain_height(); + res.info.height = m_core.get_current_blockchain_height(); - res.target_height = m_core.get_target_blockchain_height(); + res.info.target_height = m_core.get_target_blockchain_height(); - if (res.height > res.target_height) + if (res.info.height > res.info.target_height) { - res.target_height = res.height; + res.info.target_height = res.info.height; } auto& chain = m_core.get_blockchain_storage(); - res.difficulty = chain.get_difficulty_for_next_block(); + res.info.difficulty = chain.get_difficulty_for_next_block(); - res.target = chain.get_difficulty_target(); + res.info.target = chain.get_difficulty_target(); - res.tx_count = chain.get_total_transactions() - res.height; //without coinbase + res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase - res.tx_pool_size = m_core.get_pool_transactions_count(); + res.info.tx_pool_size = m_core.get_pool_transactions_count(); - res.alt_blocks_count = chain.get_alternative_blocks_count(); + res.info.alt_blocks_count = chain.get_alternative_blocks_count(); uint64_t total_conn = m_p2p.get_connections_count(); - res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); - res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.info.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count; - res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); + res.info.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); - res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + res.info.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); - res.testnet = m_core.get_testnet(); + res.info.testnet = m_core.get_testnet(); + res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); + res.info.start_time = (uint64_t)m_core.get_start_time(); res.status = Message::STATUS_OK; + res.error_details = ""; } void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res) { - res.status = Message::STATUS_FAILED; - res.error_details = "RPC method not yet implemented."; + if(!m_core.get_miner().stop()) + { + res.error_details = "Failed, mining not stopped"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + res.status = Message::STATUS_OK; + res.error_details = ""; } void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res) { - res.status = Message::STATUS_FAILED; - res.error_details = "RPC method not yet implemented."; + const cryptonote::miner& lMiner = m_core.get_miner(); + res.active = lMiner.is_mining(); + res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); + + if ( lMiner.is_mining() ) { + res.speed = lMiner.get_speed(); + res.threads_count = lMiner.get_threads_count(); + const account_public_address& lMiningAdr = lMiner.get_mining_address(); + res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr); + } + + res.status = Message::STATUS_OK; + res.error_details = ""; } void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res) @@ -536,14 +605,31 @@ namespace rpc res.status = Message::STATUS_OK; } + void DaemonHandler::handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res) + { + res.headers.resize(req.heights.size()); + + for (size_t i=0; i < req.heights.size(); i++) + { + const crypto::hash block_hash = m_core.get_block_id_by_height(req.heights[i]); + + if (!getBlockHeaderByHash(block_hash, res.headers[i])) + { + res.status = Message::STATUS_FAILED; + res.error_details = "A requested block does not exist"; + return; + } + } + + res.status = Message::STATUS_OK; + } + void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res) { res.status = Message::STATUS_FAILED; res.error_details = "RPC method not yet implemented."; } - //TODO: this RPC call is marked for later implementation in the old RPC, - // need to sort out if it's necessary and what it should do. void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res) { res.status = Message::STATUS_FAILED; @@ -596,18 +682,6 @@ namespace rpc res.error_details = "RPC method not yet implemented."; } - void DaemonHandler::handle(const FastExit::Request& req, FastExit::Response& res) - { - res.status = Message::STATUS_FAILED; - res.error_details = "RPC method not yet implemented."; - } - - void DaemonHandler::handle(const OutPeers::Request& req, OutPeers::Response& res) - { - res.status = Message::STATUS_FAILED; - res.error_details = "RPC method not yet implemented."; - } - void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res) { res.status = Message::STATUS_FAILED; @@ -675,13 +749,22 @@ namespace rpc void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res) { - for (const auto& i : req.outputs) + try { - crypto::public_key key; - rct::key mask; - bool unlocked; - m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked); - res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked}); + for (const auto& i : req.outputs) + { + crypto::public_key key; + rct::key mask; + bool unlocked; + m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked); + res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked}); + } + } + catch (const std::exception& e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + return; } res.status = Message::STATUS_OK; @@ -689,7 +772,7 @@ namespace rpc void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res) { - res.version = DAEMON_RPC_VERSION; + res.version = DAEMON_RPC_VERSION_ZMQ; res.status = Message::STATUS_OK; } @@ -754,11 +837,15 @@ namespace rpc REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle); diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 9997956c7..0d356bad2 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -92,6 +92,8 @@ class DaemonHandler : public RpcHandler void handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res); + void handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res); + void handle(const GetBlock::Request& req, GetBlock::Response& res); void handle(const GetPeerList::Request& req, GetPeerList::Response& res); @@ -108,10 +110,6 @@ class DaemonHandler : public RpcHandler void handle(const StopDaemon::Request& req, StopDaemon::Response& res); - void handle(const FastExit::Request& req, FastExit::Response& res); - - void handle(const OutPeers::Request& req, OutPeers::Response& res); - void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res); void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res); diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index b999a0f18..640058df9 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -43,12 +43,16 @@ const char* const KeyImagesSpent::name = "key_images_spent"; const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts"; const char* const SendRawTx::name = "send_raw_tx"; +const char* const StartMining::name = "start_mining"; +const char* const StopMining::name = "stop_mining"; +const char* const MiningStatus::name = "mining_status"; const char* const GetInfo::name = "get_info"; const char* const SaveBC::name = "save_bc"; const char* const GetBlockHash::name = "get_block_hash"; const char* const GetLastBlockHeader::name = "get_last_block_header"; const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash"; const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height"; +const char* const GetBlockHeadersByHeight::name = "get_block_headers_by_height"; const char* const GetPeerList::name = "get_peer_list"; const char* const SetLogLevel::name = "set_log_level"; const char* const GetTransactionPool::name = "get_transaction_pool"; @@ -95,6 +99,7 @@ rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids); val.AddMember("start_height", start_height, al); + val.AddMember("prune", prune, al); return val; } @@ -103,6 +108,7 @@ void GetBlocksFast::Request::fromJson(rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, block_ids, block_ids); GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, prune, prune); } rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const @@ -332,11 +338,96 @@ rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const return val; } + void SendRawTx::Response::fromJson(rapidjson::Value& val) { GET_FROM_JSON_OBJECT(val, relayed, relayed); } +rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address); + INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining); + INSERT_INTO_JSON_OBJECT(val, doc, ignore_battery, ignore_battery); + + return val; +} + +void StartMining::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, miner_address, miner_address); + GET_FROM_JSON_OBJECT(val, threads_count, threads_count); + GET_FROM_JSON_OBJECT(val, do_background_mining, do_background_mining); + GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery); +} + +rapidjson::Value StartMining::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StartMining::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StopMining::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StopMining::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void MiningStatus::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, active, active); + INSERT_INTO_JSON_OBJECT(val, doc, speed, speed); + INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(val, doc, address, address); + INSERT_INTO_JSON_OBJECT(val, doc, is_background_mining_enabled, is_background_mining_enabled); + + return val; +} + +void MiningStatus::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, active, active); + GET_FROM_JSON_OBJECT(val, speed, speed); + GET_FROM_JSON_OBJECT(val, threads_count, threads_count); + GET_FROM_JSON_OBJECT(val, address, address); + GET_FROM_JSON_OBJECT(val, is_background_mining_enabled, is_background_mining_enabled); +} + rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const { @@ -353,38 +444,14 @@ rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const auto& al = doc.GetAllocator(); - INSERT_INTO_JSON_OBJECT(val, doc, height, height); - INSERT_INTO_JSON_OBJECT(val, doc, target_height, target_height); - INSERT_INTO_JSON_OBJECT(val, doc, difficulty, difficulty); - INSERT_INTO_JSON_OBJECT(val, doc, target, target); - INSERT_INTO_JSON_OBJECT(val, doc, tx_count, tx_count); - INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, tx_pool_size); - INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, alt_blocks_count); - INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, outgoing_connections_count); - INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, incoming_connections_count); - INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, white_peerlist_size); - INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, grey_peerlist_size); - INSERT_INTO_JSON_OBJECT(val, doc, testnet, testnet); - INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, top_block_hash); + INSERT_INTO_JSON_OBJECT(val, doc, info, info); return val; } void GetInfo::Response::fromJson(rapidjson::Value& val) { - GET_FROM_JSON_OBJECT(val, height, height); - GET_FROM_JSON_OBJECT(val, target_height, target_height); - GET_FROM_JSON_OBJECT(val, difficulty, difficulty); - GET_FROM_JSON_OBJECT(val, target, target); - GET_FROM_JSON_OBJECT(val, tx_count, tx_count); - GET_FROM_JSON_OBJECT(val, tx_pool_size, tx_pool_size); - GET_FROM_JSON_OBJECT(val, alt_blocks_count, alt_blocks_count); - GET_FROM_JSON_OBJECT(val, outgoing_connections_count, outgoing_connections_count); - GET_FROM_JSON_OBJECT(val, incoming_connections_count, incoming_connections_count); - GET_FROM_JSON_OBJECT(val, white_peerlist_size, white_peerlist_size); - GET_FROM_JSON_OBJECT(val, grey_peerlist_size, grey_peerlist_size); - GET_FROM_JSON_OBJECT(val, testnet, testnet); - GET_FROM_JSON_OBJECT(val, top_block_hash, top_block_hash); + GET_FROM_JSON_OBJECT(val, info, info); } @@ -544,6 +611,39 @@ void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val) } +rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, heights, heights); + + return val; +} + +void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, heights, heights); +} + +rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, headers, headers); + + return val; +} + +void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, headers, headers); +} + + rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index adcd7f7c6..685f66843 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -89,6 +89,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast); BEGIN_RPC_MESSAGE_REQUEST; RPC_MESSAGE_MEMBER(std::list, block_ids); RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(bool, prune); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; RPC_MESSAGE_MEMBER(std::vector, blocks); @@ -171,10 +172,11 @@ END_RPC_MESSAGE_CLASS; BEGIN_RPC_MESSAGE_CLASS(StartMining); BEGIN_RPC_MESSAGE_REQUEST; RPC_MESSAGE_MEMBER(std::string, miner_address); - RPC_MESSAGE_MEMBER(uint64_t, thread_count); + RPC_MESSAGE_MEMBER(uint64_t, threads_count); + RPC_MESSAGE_MEMBER(bool, do_background_mining); + RPC_MESSAGE_MEMBER(bool, ignore_battery); END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(bool, success); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; @@ -182,19 +184,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetInfo); BEGIN_RPC_MESSAGE_REQUEST; END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(uint64_t, height); - RPC_MESSAGE_MEMBER(uint64_t, target_height); - RPC_MESSAGE_MEMBER(uint64_t, difficulty); - RPC_MESSAGE_MEMBER(uint64_t, target); - RPC_MESSAGE_MEMBER(uint64_t, tx_count); - RPC_MESSAGE_MEMBER(uint64_t, tx_pool_size); - RPC_MESSAGE_MEMBER(uint64_t, alt_blocks_count); - RPC_MESSAGE_MEMBER(uint64_t, outgoing_connections_count); - RPC_MESSAGE_MEMBER(uint64_t, incoming_connections_count); - RPC_MESSAGE_MEMBER(uint64_t, white_peerlist_size); - RPC_MESSAGE_MEMBER(uint64_t, grey_peerlist_size); - RPC_MESSAGE_MEMBER(bool, testnet); - RPC_MESSAGE_MEMBER(crypto::hash, top_block_hash); + RPC_MESSAGE_MEMBER(DaemonInfo, info); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; @@ -202,7 +192,6 @@ BEGIN_RPC_MESSAGE_CLASS(StopMining); BEGIN_RPC_MESSAGE_REQUEST; END_RPC_MESSAGE_REQUEST; BEGIN_RPC_MESSAGE_RESPONSE; - RPC_MESSAGE_MEMBER(bool, success); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; @@ -212,8 +201,9 @@ BEGIN_RPC_MESSAGE_CLASS(MiningStatus); BEGIN_RPC_MESSAGE_RESPONSE; RPC_MESSAGE_MEMBER(bool, active); RPC_MESSAGE_MEMBER(uint64_t, speed); - RPC_MESSAGE_MEMBER(uint64_t, thread_count); + RPC_MESSAGE_MEMBER(uint64_t, threads_count); RPC_MESSAGE_MEMBER(std::string, address); + RPC_MESSAGE_MEMBER(bool, is_background_mining_enabled); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; @@ -273,6 +263,15 @@ BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersByHeight); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector, heights); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector, headers); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + BEGIN_RPC_MESSAGE_CLASS(GetBlock); BEGIN_RPC_MESSAGE_REQUEST; END_RPC_MESSAGE_REQUEST; @@ -334,20 +333,6 @@ BEGIN_RPC_MESSAGE_CLASS(StopDaemon); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; -BEGIN_RPC_MESSAGE_CLASS(FastExit); - BEGIN_RPC_MESSAGE_REQUEST; - END_RPC_MESSAGE_REQUEST; - BEGIN_RPC_MESSAGE_RESPONSE; - END_RPC_MESSAGE_RESPONSE; -END_RPC_MESSAGE_CLASS; - -BEGIN_RPC_MESSAGE_CLASS(OutPeers); - BEGIN_RPC_MESSAGE_REQUEST; - END_RPC_MESSAGE_REQUEST; - BEGIN_RPC_MESSAGE_RESPONSE; - END_RPC_MESSAGE_RESPONSE; -END_RPC_MESSAGE_CLASS; - BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph); BEGIN_RPC_MESSAGE_REQUEST; END_RPC_MESSAGE_REQUEST; diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h index b354b32c2..bb735fe7c 100644 --- a/src/rpc/daemon_rpc_version.h +++ b/src/rpc/daemon_rpc_version.h @@ -34,10 +34,10 @@ namespace cryptonote namespace rpc { -static const uint32_t DAEMON_RPC_VERSION_MINOR = 0; -static const uint32_t DAEMON_RPC_VERSION_MAJOR = 1; +static const uint32_t DAEMON_RPC_VERSION_ZMQ_MINOR = 0; +static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 1; -static const uint32_t DAEMON_RPC_VERSION = DAEMON_RPC_VERSION_MINOR + (DAEMON_RPC_VERSION_MAJOR << 16); +static const uint32_t DAEMON_RPC_VERSION_ZMQ = DAEMON_RPC_VERSION_ZMQ_MINOR + (DAEMON_RPC_VERSION_ZMQ_MAJOR << 16); } // namespace rpc diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 7d4499bcf..086820180 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -53,7 +53,7 @@ rapidjson::Value Message::toJson(rapidjson::Document& doc) const 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); + INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION_ZMQ); return val; } diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index c2492681d..00f1e0caa 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -164,6 +164,26 @@ namespace rpc uint64_t reward; }; + struct DaemonInfo + { + uint64_t height; + uint64_t target_height; + uint64_t difficulty; + uint64_t target; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + bool testnet; + crypto::hash top_block_hash; + uint64_t cumulative_difficulty; + uint64_t block_size_limit; + uint64_t start_time; + }; + } // namespace rpc } // namespace cryptonote diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index 8e1e47b38..afdff2328 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -45,10 +45,6 @@ ZmqServer::ZmqServer(RpcHandler& h) : ZmqServer::~ZmqServer() { - for (zmq::socket_t* socket : sockets) - { - delete socket; - } } void ZmqServer::serve() @@ -58,31 +54,36 @@ void ZmqServer::serve() { try { - for (zmq::socket_t* socket : sockets) + zmq::message_t message; + + if (!rep_socket) { - zmq::message_t message; + throw std::runtime_error("ZMQ RPC server reply socket is null"); + } + while (rep_socket->recv(&message)) + { + std::string message_string(reinterpret_cast(message.data()), message.size()); - while (socket->recv(&message)) - { - std::string message_string(reinterpret_cast(message.data()), message.size()); + MDEBUG(std::string("Received RPC request: \"") + message_string + "\""); - MDEBUG(std::string("Received RPC request: \"") + message_string + "\""); + std::string response = handler.handle(message_string); - std::string response = handler.handle(message_string); + zmq::message_t reply(response.size()); + memcpy((void *) reply.data(), response.c_str(), response.size()); - zmq::message_t reply(response.size()); - memcpy((void *) reply.data(), response.c_str(), response.size()); - - socket->send(reply); - MDEBUG(std::string("Sent RPC reply: \"") + response + "\""); - } + rep_socket->send(reply); + MDEBUG(std::string("Sent RPC reply: \"") + response + "\""); } } - catch (boost::thread_interrupted& e) + catch (const boost::thread_interrupted& e) { MDEBUG("ZMQ Server thread interrupted."); } + catch (const zmq::error_t& e) + { + MERROR(std::string("ZMQ error: ") + e.what()); + } boost::this_thread::interruption_point(); } } @@ -95,26 +96,20 @@ bool ZmqServer::addIPCSocket(std::string address, std::string port) bool ZmqServer::addTCPSocket(std::string address, std::string port) { - zmq::socket_t *new_socket = nullptr; try { std::string addr_prefix("tcp://"); - new_socket = new zmq::socket_t(context, ZMQ_REP); + rep_socket.reset(new zmq::socket_t(context, ZMQ_REP)); - new_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS); + rep_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS); std::string bind_address = addr_prefix + address + std::string(":") + port; - new_socket->bind(bind_address.c_str()); - sockets.push_back(new_socket); + rep_socket->bind(bind_address.c_str()); } - catch (std::exception& e) + catch (const std::exception& e) { MERROR(std::string("Error creating ZMQ Socket: ") + e.what()); - if (new_socket) - { - delete new_socket; - } return false; } return true; diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h index 964ce2071..c278ff759 100644 --- a/src/rpc/zmq_server.h +++ b/src/rpc/zmq_server.h @@ -29,12 +29,11 @@ #pragma once #include -#include -#include -#include "common/command_line.h" - #include #include +#include + +#include "common/command_line.h" #include "rpc_handler.h" @@ -75,7 +74,7 @@ class ZmqServer boost::thread run_thread; - std::vector sockets; + std::unique_ptr rep_socket; }; diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 50ef414f4..ead3fdd58 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -28,6 +28,7 @@ #include "json_object.h" +#include #include "string_tools.h" namespace cryptonote @@ -36,6 +37,74 @@ namespace cryptonote namespace json { +namespace +{ + template + constexpr bool precision_loss() + { + return + std::numeric_limits::is_signed != std::numeric_limits::is_signed || + std::numeric_limits::min() > std::numeric_limits::min() || + std::numeric_limits::max() < std::numeric_limits::max(); + } + + template + void convert_numeric(Source source, Type& i) + { + static_assert( + std::numeric_limits::is_signed == std::numeric_limits::is_signed, + "source and destination signs do not match" + ); + if (source < std::numeric_limits::min()) + { + throw WRONG_TYPE{"numeric underflow"}; + } + if (std::numeric_limits::max() < source) + { + throw WRONG_TYPE{"numeric overflow"}; + } + i = Type(source); + } + + template + void to_int(const rapidjson::Value& val, Type& i) + { + if (!val.IsInt()) + { + throw WRONG_TYPE{"integer"}; + } + convert_numeric(val.GetInt(), i); + } + template + void to_int64(const rapidjson::Value& val, Type& i) + { + if (!val.IsInt64()) + { + throw WRONG_TYPE{"integer"}; + } + convert_numeric(val.GetInt64(), i); + } + + template + void to_uint(const rapidjson::Value& val, Type& i) + { + if (!val.IsUint()) + { + throw WRONG_TYPE{"unsigned integer"}; + } + convert_numeric(val.GetUint(), i); + } + template + void to_uint64(const rapidjson::Value& val, Type& i) + { + if (!val.IsUint64()) + { + throw WRONG_TYPE{"unsigned integer"}; + } + convert_numeric(val.GetUint64(), i); + } +} + void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val) { val = rapidjson::Value(i.c_str(), doc.GetAllocator()); @@ -65,100 +134,81 @@ void fromJsonValue(const rapidjson::Value& val, bool& b) b = val.GetBool(); } -void toJsonValue(rapidjson::Document& doc, const uint8_t& i, rapidjson::Value& val) +void fromJsonValue(const rapidjson::Value& val, unsigned char& i) +{ + to_uint(val, i); +} + +void fromJsonValue(const rapidjson::Value& val, char& i) +{ + to_int(val, i); +} + +void fromJsonValue(const rapidjson::Value& val, signed char& i) +{ + to_int(val, i); +} + +void fromJsonValue(const rapidjson::Value& val, unsigned short& i) +{ + to_uint(val, i); +} + +void fromJsonValue(const rapidjson::Value& val, short& i) +{ + to_int(val, i); +} + +void toJsonValue(rapidjson::Document& doc, const unsigned int i, rapidjson::Value& val) { val = rapidjson::Value(i); } - -void fromJsonValue(const rapidjson::Value& val, uint8_t& i) +void fromJsonValue(const rapidjson::Value& val, unsigned int& i) { - if (!val.IsUint()) - { - throw WRONG_TYPE("unsigned integer"); - } - - i = (uint8_t)( val.GetUint() & 0xFF); + to_uint(val, i); } -void toJsonValue(rapidjson::Document& doc, const int8_t& i, rapidjson::Value& val) +void toJsonValue(rapidjson::Document& doc, const int i, rapidjson::Value& val) { val = rapidjson::Value(i); } - -void fromJsonValue(const rapidjson::Value& val, int8_t& i) +void fromJsonValue(const rapidjson::Value& val, int& i) { - if (!val.IsInt()) - { - throw WRONG_TYPE("integer"); - } - - i = (int8_t) ( val.GetInt() & 0xFF); + to_int(val, i); } -void toJsonValue(rapidjson::Document& doc, const uint16_t& i, rapidjson::Value& val) +void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val) { - val = rapidjson::Value(i); + static_assert(!precision_loss(), "precision loss"); + val = rapidjson::Value(std::uint64_t(i)); } - -void fromJsonValue(const rapidjson::Value& val, uint16_t& i) +void fromJsonValue(const rapidjson::Value& val, unsigned long long& i) { - if (!val.IsUint()) - { - throw WRONG_TYPE("unsigned integer"); - } - - i = (uint16_t) ( val.GetUint() & 0xFFFF); + to_uint64(val, i); } -void toJsonValue(rapidjson::Document& doc, const int32_t& i, rapidjson::Value& val) +void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val) { - val = rapidjson::Value(i); + static_assert(!precision_loss(), "precision loss"); + val = rapidjson::Value(std::int64_t(i)); } - -void fromJsonValue(const rapidjson::Value& val, int32_t& i) +void fromJsonValue(const rapidjson::Value& val, long long& i) { - if (!val.IsInt()) - { - throw WRONG_TYPE("signed integer"); - } - - i = val.GetInt(); + to_int64(val, i); } -void toJsonValue(rapidjson::Document& doc, const uint32_t& i, rapidjson::Value& val) +void fromJsonValue(const rapidjson::Value& val, unsigned long& i) { - val = rapidjson::Value(i); + to_uint64(val, i); } - -void fromJsonValue(const rapidjson::Value& val, uint32_t& i) +void fromJsonValue(const rapidjson::Value& val, long& i) { - if (!val.IsUint()) - { - throw WRONG_TYPE("unsigned integer"); - } - - i = val.GetUint(); -} - -void toJsonValue(rapidjson::Document& doc, const uint64_t& i, rapidjson::Value& val) -{ - val = rapidjson::Value(i); -} - - -void fromJsonValue(const rapidjson::Value& val, uint64_t& i) -{ - if (!val.IsUint64()) - { - throw WRONG_TYPE("unsigned integer"); - } - - i = val.GetUint64(); + to_int64(val, i); } void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val) @@ -1063,6 +1113,53 @@ 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, testnet, info.testnet); + INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); + INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); + INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit); + INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time); +} + +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info) +{ + if (!val.IsObject()) + { + throw WRONG_TYPE("json object"); + } + + GET_FROM_JSON_OBJECT(val, info.height, height); + GET_FROM_JSON_OBJECT(val, info.target_height, target_height); + GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty); + GET_FROM_JSON_OBJECT(val, info.target, target); + GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count); + GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size); + GET_FROM_JSON_OBJECT(val, info.alt_blocks_count, alt_blocks_count); + GET_FROM_JSON_OBJECT(val, info.outgoing_connections_count, outgoing_connections_count); + GET_FROM_JSON_OBJECT(val, info.incoming_connections_count, incoming_connections_count); + GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size); + GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size); + GET_FROM_JSON_OBJECT(val, info.testnet, testnet); + GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); + GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); + GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); + GET_FROM_JSON_OBJECT(val, info.start_time, start_time); +} + } // namespace json } // namespace cryptonote diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 8e0da9aa5..7b9519c48 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -107,16 +107,22 @@ struct PARSE_FAIL : public JSON_ERROR } }; +template +inline constexpr bool is_to_hex() +{ + return std::is_pod() && !std::is_integral(); +} + // POD to json value template -typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value) +typename std::enable_if()>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value) { value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator()); } template -typename std::enable_if::value, void>::type fromJsonValue(const rapidjson::Value& val, Type& t) +typename std::enable_if()>::type fromJsonValue(const rapidjson::Value& val, Type& t) { if (!val.IsString()) { @@ -138,23 +144,42 @@ void fromJsonValue(const rapidjson::Value& val, std::string& str); void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, bool& b); -void toJsonValue(rapidjson::Document& doc, const uint8_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, uint8_t& i); +// integers overloads for toJsonValue are not needed for standard promotions -void toJsonValue(rapidjson::Document& doc, const int8_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, int8_t& i); +void fromJsonValue(const rapidjson::Value& val, unsigned char& i); -void toJsonValue(rapidjson::Document& doc, const uint16_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, uint16_t& i); +void fromJsonValue(const rapidjson::Value& val, signed char& i); -void toJsonValue(rapidjson::Document& doc, const int32_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, int32_t& i); +void fromJsonValue(const rapidjson::Value& val, char& i); -void toJsonValue(rapidjson::Document& doc, const uint32_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, uint32_t& i); +void fromJsonValue(const rapidjson::Value& val, unsigned short& i); -void toJsonValue(rapidjson::Document& doc, const uint64_t& i, rapidjson::Value& val); -void fromJsonValue(const rapidjson::Value& val, uint64_t& i); +void fromJsonValue(const rapidjson::Value& val, short& i); + +void toJsonValue(rapidjson::Document& doc, const unsigned i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, unsigned& i); + +void toJsonValue(rapidjson::Document& doc, const int, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, int& i); + + +void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, unsigned long long& i); + +void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, long long& i); + +inline void toJsonValue(rapidjson::Document& doc, const unsigned long i, rapidjson::Value& val) { + toJsonValue(doc, static_cast(i), val); +} +void fromJsonValue(const rapidjson::Value& val, unsigned long& i); + +inline void toJsonValue(rapidjson::Document& doc, const long i, rapidjson::Value& val) { + toJsonValue(doc, static_cast(i), val); +} +void fromJsonValue(const rapidjson::Value& val, long& i); + +// end integers void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx); @@ -255,6 +280,8 @@ void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig); void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val); void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig); +void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val); +void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info); template typename std::enable_if::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val);