diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/CMakeLists.txt | 70 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 38 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 33 | ||||
-rw-r--r-- | src/rpc/daemon_handler.cpp | 887 | ||||
-rw-r--r-- | src/rpc/daemon_handler.h | 145 | ||||
-rw-r--r-- | src/rpc/daemon_messages.cpp | 900 | ||||
-rw-r--r-- | src/rpc/daemon_messages.h | 421 | ||||
-rw-r--r-- | src/rpc/daemon_rpc_version.h | 44 | ||||
-rw-r--r-- | src/rpc/message.cpp | 286 | ||||
-rw-r--r-- | src/rpc/message.h | 131 | ||||
-rw-r--r-- | src/rpc/message_data_structs.h | 189 | ||||
-rw-r--r-- | src/rpc/rpc_handler.h | 54 | ||||
-rw-r--r-- | src/rpc/zmq_server.cpp | 141 | ||||
-rw-r--r-- | src/rpc/zmq_server.h | 83 |
15 files changed, 3415 insertions, 9 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index f6037d7e3..b5c38b1a8 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,32 @@ 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}) +target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) +target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH}) + + add_dependencies(rpc version) + +add_dependencies(daemon_rpc_server + version) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index a5de36118..3cc9bf3f0 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(); @@ -205,14 +205,17 @@ namespace cryptonote } size_t txidx = 0; ntxes += bd.second.size(); - for(const auto& t: bd.second) + for (std::list<cryptonote::blobdata>::iterator i = bd.second.begin(); i != bd.second.end(); ++i) { + unpruned_size += i->size(); if (req.prune) - res.blocks.back().txs.push_back(get_pruned_tx_blob(t)); + res.blocks.back().txs.push_back(get_pruned_tx_blob(std::move(*i))); else - res.blocks.back().txs.push_back(t); + res.blocks.back().txs.push_back(std::move(*i)); + i->clear(); + i->shrink_to_fit(); pruned_size += res.blocks.back().txs.back().size(); - unpruned_size += t.size(); + res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); if (!r) @@ -507,11 +510,12 @@ namespace cryptonote e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end(); if (e.in_pool) { - e.block_height = std::numeric_limits<uint64_t>::max(); + e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max(); } else { e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash); + e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height); } // fill up old style responses too, in case an old wallet asks @@ -945,7 +949,7 @@ namespace cryptonote LOG_ERROR("Failed to find tx pub key in blockblob"); return false; } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) if(res.reserved_offset + req.reserve_size > block_blob.size()) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1725,6 +1729,26 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + if (!m_core.get_txpool_backlog(res.backlog)) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Failed to get txpool backlog"; + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_port = { "rpc-bind-port" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 1d1d9da66..dbbe07972 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -125,6 +125,7 @@ namespace cryptonote MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted) + MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG) END_JSON_RPC_MAP() END_URI_MAP2() @@ -182,6 +183,7 @@ namespace cryptonote bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp); + bool on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp); //----------------------- private: diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index c413f9af8..99430bf20 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 13 +#define CORE_RPC_VERSION_MINOR 14 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -215,6 +215,7 @@ namespace cryptonote std::string as_json; bool in_pool; uint64_t block_height; + uint64_t block_timestamp; std::vector<uint64_t> output_indices; BEGIN_KV_SERIALIZE_MAP() @@ -223,6 +224,7 @@ namespace cryptonote KV_SERIALIZE(as_json) KV_SERIALIZE(in_pool) KV_SERIALIZE(block_height) + KV_SERIALIZE(block_timestamp) KV_SERIALIZE(output_indices) END_KV_SERIALIZE_MAP() }; @@ -1071,6 +1073,33 @@ namespace cryptonote }; }; + struct tx_backlog_entry + { + uint64_t blob_size; + uint64_t fee; + uint64_t time_in_pool; + }; + + struct COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector<tx_backlog_entry> backlog; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog) + END_KV_SERIALIZE_MAP() + }; + }; + struct txpool_histo { uint32_t txs; @@ -1087,6 +1116,7 @@ namespace cryptonote uint64_t bytes_total; uint32_t bytes_min; uint32_t bytes_max; + uint32_t bytes_med; uint64_t fee_total; uint64_t oldest; uint32_t txs_total; @@ -1100,6 +1130,7 @@ namespace cryptonote KV_SERIALIZE(bytes_total) KV_SERIALIZE(bytes_min) KV_SERIALIZE(bytes_max) + KV_SERIALIZE(bytes_med) KV_SERIALIZE(fee_total) KV_SERIALIZE(oldest) KV_SERIALIZE(txs_total) diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp new file mode 100644 index 000000000..53eeb5e76 --- /dev/null +++ b/src/rpc/daemon_handler.cpp @@ -0,0 +1,887 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon_handler.h" + +// likely included by daemon_handler.h's includes, +// but including here for clarity +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "ringct/rctSigs.h" + +namespace cryptonote +{ + +namespace rpc +{ + + void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res) + { + res.height = m_core.get_current_blockchain_height(); + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res) + { + std::list<std::pair<blobdata, std::list<blobdata> > > blocks; + + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::find_blockchain_supplement() returned false"; + return; + } + + res.blocks.resize(blocks.size()); + res.output_indices.resize(blocks.size()); + + //TODO: really need to switch uses of std::list to std::vector unless + // it's a huge performance concern + + auto it = blocks.begin(); + + uint64_t block_count = 0; + while (it != blocks.end()) + { + cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count]; + + if (!parse_and_validate_block_from_blob(it->first, bwt.block)) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "failed retrieving a requested block"; + return; + } + + if (it->second.size() != bwt.block.tx_hashes.size()) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "incorrect number of transactions retrieved for block"; + return; + } + std::list<transaction> txs; + for (const auto& blob : it->second) + { + txs.resize(txs.size() + 1); + if (!parse_and_validate_tx_from_blob(blob, txs.back())) + { + res.blocks.clear(); + res.output_indices.clear(); + res.status = Message::STATUS_FAILED; + res.error_details = "failed retrieving a requested transaction"; + return; + } + } + + cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count]; + + // miner tx output indices + { + cryptonote::rpc::tx_output_indices tx_indices; + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices); + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + indices.push_back(tx_indices); + } + + // assume each block returned is returned with all its transactions + // in the correct order. + auto tx_it = txs.begin(); + for (const crypto::hash& h : bwt.block.tx_hashes) + { + bwt.transactions.emplace(h, *tx_it); + tx_it++; + + cryptonote::rpc::tx_output_indices tx_indices; + bool r = m_core.get_tx_outputs_gindexs(h, tx_indices); + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + + indices.push_back(tx_indices); + } + + it++; + block_count++; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res) + { + res.start_height = req.start_height; + + auto& chain = m_core.get_blockchain_storage(); + + if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Blockchain::find_blockchain_supplement() returned false"; + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res) + { + std::list<cryptonote::transaction> found_txs; + std::list<crypto::hash> missed_hashes; + + bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes); + + // TODO: consider fixing core::get_transactions to not hide exceptions + if (!r) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_transactions() returned false (exception caught there)"; + return; + } + + size_t num_found = found_txs.size(); + + // std::list is annoying + std::vector<cryptonote::transaction> found_txs_vec + { + std::make_move_iterator(std::begin(found_txs)), + std::make_move_iterator(std::end(found_txs)) + }; + + std::vector<crypto::hash> missed_vec + { + std::make_move_iterator(std::begin(missed_hashes)), + std::make_move_iterator(std::end(missed_hashes)) + }; + + std::vector<uint64_t> heights(num_found); + std::vector<bool> in_pool(num_found, false); + std::vector<crypto::hash> found_hashes(num_found); + + for (size_t i=0; i < num_found; i++) + { + found_hashes[i] = get_transaction_hash(found_txs_vec[i]); + heights[i] = m_core.get_blockchain_storage().get_db().get_tx_block_height(found_hashes[i]); + } + + // if any missing from blockchain, check in tx pool + if (!missed_vec.empty()) + { + std::list<cryptonote::transaction> pool_txs; + + m_core.get_pool_transactions(pool_txs); + + for (const auto& tx : pool_txs) + { + crypto::hash h = get_transaction_hash(tx); + + auto itr = std::find(missed_vec.begin(), missed_vec.end(), h); + + if (itr != missed_vec.end()) + { + found_hashes.push_back(h); + found_txs_vec.push_back(tx); + heights.push_back(std::numeric_limits<uint64_t>::max()); + in_pool.push_back(true); + missed_vec.erase(itr); + } + } + } + + for (size_t i=0; i < found_hashes.size(); i++) + { + cryptonote::rpc::transaction_info info; + info.height = heights[i]; + info.in_pool = in_pool[i]; + info.transaction = std::move(found_txs_vec[i]); + + res.txs.emplace(found_hashes[i], std::move(info)); + } + + res.missed_hashes = std::move(missed_vec); + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res) + { + res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT); + + std::vector<bool> chain_spent_status; + std::vector<bool> pool_spent_status; + + m_core.are_key_images_spent(req.key_images, chain_spent_status); + m_core.are_key_images_spent_in_pool(req.key_images, pool_spent_status); + + if ((chain_spent_status.size() != req.key_images.size()) || (pool_spent_status.size() != req.key_images.size())) + { + res.status = Message::STATUS_FAILED; + res.error_details = "tx_pool::have_key_images_as_spent() gave vectors of wrong size(s)."; + return; + } + + for(size_t i=0; i < req.key_images.size(); i++) + { + if ( chain_spent_status[i] ) + { + res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_BLOCKCHAIN; + } + else if ( pool_spent_status[i] ) + { + res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_POOL; + } + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res) + { + if (!m_core.get_tx_outputs_gindexs(req.tx_hash, res.output_indices)) + { + res.status = Message::STATUS_FAILED; + res.error_details = "core::get_tx_outputs_gindexs() returned false"; + return; + } + + res.status = Message::STATUS_OK; + + } + + //TODO: handle "restricted" RPC + void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res) + { + auto& chain = m_core.get_blockchain_storage(); + + try + { + for (const uint64_t& amount : req.amounts) + { + std::vector<uint64_t> indices = chain.get_random_outputs(amount, req.count); + + outputs_for_amount ofa; + + ofa.resize(indices.size()); + + for (size_t i = 0; i < indices.size(); i++) + { + crypto::public_key key = chain.get_output_key(amount, indices[i]); + ofa[i].amount_index = indices[i]; + ofa[i].key = key; + } + + amount_with_random_outputs amt; + amt.amount = amount; + amt.outputs = ofa; + + res.amounts_with_outputs.push_back(amt); + } + + res.status = Message::STATUS_OK; + } + catch (const std::exception& e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + } + } + + void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res) + { + auto tx_blob = cryptonote::tx_to_blob(req.tx); + + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + + if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed) + { + if (tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + } + else + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + } + res.status = Message::STATUS_FAILED; + res.error_details = ""; + + if (tvc.m_low_mixin) + { + res.error_details = "mixin too low"; + } + if (tvc.m_double_spend) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "double spend"; + } + if (tvc.m_invalid_input) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "invalid input"; + } + if (tvc.m_invalid_output) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "invalid output"; + } + if (tvc.m_too_big) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "too big"; + } + if (tvc.m_overspend) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "overspend"; + } + if (tvc.m_fee_too_low) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "fee too low"; + } + if (tvc.m_not_rct) + { + if (!res.error_details.empty()) res.error_details += " and "; + res.error_details = "tx is not ringct"; + } + if (res.error_details.empty()) + { + res.error_details = "an unknown issue was found with the transaction"; + } + + return; + } + + if(!tvc.m_should_be_relayed || !req.relay) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + res.error_details = "Not relayed"; + res.relayed = false; + res.status = Message::STATUS_OK; + + return; + } + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + m_core.get_protocol()->relay_transactions(r, fake_context); + + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + res.status = Message::STATUS_OK; + res.relayed = true; + + return; + } + + void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res) + { + account_public_address adr; + if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address)) + { + res.error_details = "Failed, wrong address"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4; + + // if we couldn't detect threads, set it to a ridiculously high number + if(concurrency_count == 0) + { + concurrency_count = 257; + } + + // if there are more threads requested than the hardware supports + // then we fail and log that. + if(req.threads_count > concurrency_count) + { + res.error_details = "Failed, too many threads relative to CPU cores."; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + { + res.error_details = "Failed, mining not started"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + res.status = Message::STATUS_OK; + res.error_details = ""; + + } + + void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res) + { + res.info.height = m_core.get_current_blockchain_height(); + + res.info.target_height = m_core.get_target_blockchain_height(); + + if (res.info.height > res.info.target_height) + { + res.info.target_height = res.info.height; + } + + auto& chain = m_core.get_blockchain_storage(); + + res.info.difficulty = chain.get_difficulty_for_next_block(); + + res.info.target = chain.get_difficulty_target(); + + res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase + + res.info.tx_pool_size = m_core.get_pool_transactions_count(); + + res.info.alt_blocks_count = chain.get_alternative_blocks_count(); + + uint64_t total_conn = m_p2p.get_connections_count(); + res.info.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count; + + res.info.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); + + res.info.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + + res.info.testnet = m_core.get_testnet(); + res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); + res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); + res.info.start_time = (uint64_t)m_core.get_start_time(); + + res.status = Message::STATUS_OK; + res.error_details = ""; + } + + void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res) + { + if(!m_core.get_miner().stop()) + { + res.error_details = "Failed, mining not stopped"; + LOG_PRINT_L0(res.error_details); + res.status = Message::STATUS_FAILED; + return; + } + + res.status = Message::STATUS_OK; + res.error_details = ""; + } + + void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res) + { + const cryptonote::miner& lMiner = m_core.get_miner(); + res.active = lMiner.is_mining(); + res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); + + if ( lMiner.is_mining() ) { + res.speed = lMiner.get_speed(); + res.threads_count = lMiner.get_threads_count(); + const account_public_address& lMiningAdr = lMiner.get_mining_address(); + res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr); + } + + res.status = Message::STATUS_OK; + res.error_details = ""; + } + + void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res) + { + if (!m_core.get_blockchain_storage().store_blockchain()) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Error storing the blockchain"; + } + else + { + res.status = Message::STATUS_OK; + } + } + + void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res) + { + if (m_core.get_current_blockchain_height() <= req.height) + { + res.hash = 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 GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res) + { + res.headers.resize(req.heights.size()); + + for (size_t i=0; i < req.heights.size(); i++) + { + const crypto::hash block_hash = m_core.get_block_id_by_height(req.heights[i]); + + if (!getBlockHeaderByHash(block_hash, res.headers[i])) + { + res.status = Message::STATUS_FAILED; + res.error_details = "A requested block does not exist"; + return; + } + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res) + { + if (req.level < 0 || req.level > 4) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Error: log level not valid"; + } + else + { + res.status = Message::STATUS_OK; + mlog_set_log_level(req.level); + } + } + + void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res) + { + bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images); + + if (!r) res.status = Message::STATUS_FAILED; + else res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res) + { + const Blockchain &blockchain = m_core.get_blockchain_storage(); + uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version(); + res.info.version = blockchain.get_current_hard_fork_version(); + res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting); + res.info.state = blockchain.get_hard_fork_state(); + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res) + { + res.status = Message::STATUS_FAILED; + res.error_details = "RPC method not yet implemented."; + } + + void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res) + { + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t> > histogram; + try + { + histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff); + } + catch (const std::exception &e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + return; + } + + res.histogram.clear(); + res.histogram.reserve(histogram.size()); + for (const auto &i: histogram) + { + if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0)) + res.histogram.emplace_back(output_amount_count{i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)}); + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res) + { + try + { + for (const auto& i : req.outputs) + { + crypto::public_key key; + rct::key mask; + bool unlocked; + m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked); + res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked}); + } + } + catch (const std::exception& e) + { + res.status = Message::STATUS_FAILED; + res.error_details = e.what(); + return; + } + + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res) + { + res.version = DAEMON_RPC_VERSION_ZMQ; + res.status = Message::STATUS_OK; + } + + void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) + { + res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks); + res.status = Message::STATUS_OK; + } + + bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header) + { + block b; + + if (!m_core.get_block_by_hash(hash_in, b)) + { + return false; + } + + header.hash = hash_in; + header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height; + + header.major_version = b.major_version; + header.minor_version = b.minor_version; + header.timestamp = b.timestamp; + header.nonce = b.nonce; + header.prev_id = b.prev_id; + + header.depth = m_core.get_current_blockchain_height() - header.height - 1; + + header.reward = 0; + for (const auto& out : b.miner_tx.vout) + { + header.reward += out.amount; + } + + header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height); + + return true; + } + + std::string DaemonHandler::handle(const std::string& request) + { + MDEBUG("Handling RPC request: " << request); + + Message* resp_message = NULL; + + try + { + FullMessage req_full(request, true); + + rapidjson::Value& req_json = req_full.getMessage(); + + const std::string request_type = req_full.getRequestType(); + + // create correct Message subclass and call handle() on it + REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle); + + // if none of the request types matches + if (resp_message == NULL) + { + return BAD_REQUEST(request_type, req_full.getID()); + } + + FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID()); + + const std::string response = resp_full.getJson(); + delete resp_message; + resp_message = NULL; + + MDEBUG("Returning RPC response: " << response); + + return response; + } + catch (const std::exception& e) + { + if (resp_message) + { + delete resp_message; + } + + return BAD_JSON(e.what()); + } + } + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h new file mode 100644 index 000000000..0d356bad2 --- /dev/null +++ b/src/rpc/daemon_handler.h @@ -0,0 +1,145 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "daemon_messages.h" +#include "daemon_rpc_version.h" +#include "rpc_handler.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" + +namespace +{ + typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > t_p2p; +} // anonymous namespace + +namespace cryptonote +{ + +namespace rpc +{ + +class DaemonHandler : public RpcHandler +{ + public: + + DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { } + + ~DaemonHandler() { } + + void handle(const GetHeight::Request& req, GetHeight::Response& res); + + void handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res); + + void handle(const GetHashesFast::Request& req, GetHashesFast::Response& res); + + void handle(const GetTransactions::Request& req, GetTransactions::Response& res); + + void handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res); + + void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res); + + void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res); + + void handle(const SendRawTx::Request& req, SendRawTx::Response& res); + + void handle(const StartMining::Request& req, StartMining::Response& res); + + void handle(const GetInfo::Request& req, GetInfo::Response& res); + + void handle(const StopMining::Request& req, StopMining::Response& res); + + void handle(const MiningStatus::Request& req, MiningStatus::Response& res); + + void handle(const SaveBC::Request& req, SaveBC::Response& res); + + void handle(const GetBlockHash::Request& req, GetBlockHash::Response& res); + + void handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res); + + void handle(const SubmitBlock::Request& req, SubmitBlock::Response& res); + + void handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res); + + void handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res); + + void handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res); + + void handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res); + + void handle(const GetBlock::Request& req, GetBlock::Response& res); + + void handle(const GetPeerList::Request& req, GetPeerList::Response& res); + + void handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res); + + void handle(const SetLogLevel::Request& req, SetLogLevel::Response& res); + + void handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res); + + void handle(const GetConnections::Request& req, GetConnections::Response& res); + + void handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res); + + void handle(const StopDaemon::Request& req, StopDaemon::Response& res); + + void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res); + + void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res); + + void handle(const HardForkInfo::Request& req, HardForkInfo::Response& res); + + void handle(const GetBans::Request& req, GetBans::Response& res); + + void handle(const SetBans::Request& req, SetBans::Response& res); + + void handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res); + + void handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res); + + void handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res); + + void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res); + + void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res); + + std::string handle(const std::string& request); + + private: + + bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response); + + cryptonote::core& m_core; + t_p2p& m_p2p; +}; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp new file mode 100644 index 000000000..640058df9 --- /dev/null +++ b/src/rpc/daemon_messages.cpp @@ -0,0 +1,900 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon_messages.h" +#include "serialization/json_object.h" + +namespace cryptonote +{ + +namespace rpc +{ + +const char* const GetHeight::name = "get_height"; +const char* const GetBlocksFast::name = "get_blocks_fast"; +const char* const GetHashesFast::name = "get_hashes_fast"; +const char* const GetTransactions::name = "get_transactions"; +const char* const KeyImagesSpent::name = "key_images_spent"; +const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; +const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts"; +const char* const SendRawTx::name = "send_raw_tx"; +const char* const StartMining::name = "start_mining"; +const char* const StopMining::name = "stop_mining"; +const char* const MiningStatus::name = "mining_status"; +const char* const GetInfo::name = "get_info"; +const char* const SaveBC::name = "save_bc"; +const char* const GetBlockHash::name = "get_block_hash"; +const char* const GetLastBlockHeader::name = "get_last_block_header"; +const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash"; +const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height"; +const char* const GetBlockHeadersByHeight::name = "get_block_headers_by_height"; +const char* const GetPeerList::name = "get_peer_list"; +const char* const SetLogLevel::name = "set_log_level"; +const char* const GetTransactionPool::name = "get_transaction_pool"; +const char* const HardForkInfo::name = "hard_fork_info"; +const char* const GetOutputHistogram::name = "get_output_histogram"; +const char* const GetOutputKeys::name = "get_output_keys"; +const char* const GetRPCVersion::name = "get_rpc_version"; +const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate"; + + + + +rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetHeight::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + val.AddMember("height", height, al); + + return val; +} + +void GetHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + + +rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids); + val.AddMember("start_height", start_height, al); + val.AddMember("prune", prune, al); + + return val; +} + +void GetBlocksFast::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, block_ids, block_ids); + GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, prune, prune); +} + +rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks); + val.AddMember("start_height", start_height, al); + val.AddMember("current_height", current_height, al); + INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); + + return val; +} + +void GetBlocksFast::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, blocks, blocks); + GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, current_height, current_height); + GET_FROM_JSON_OBJECT(val, output_indices, output_indices); +} + + +rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes); + val.AddMember("start_height", start_height, al); + + return val; +} + +void GetHashesFast::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes); + GET_FROM_JSON_OBJECT(val, start_height, start_height); +} + +rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes); + val.AddMember("start_height", start_height, al); + val.AddMember("current_height", current_height, al); + + return val; +} + +void GetHashesFast::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hashes, hashes); + GET_FROM_JSON_OBJECT(val, start_height, start_height); + GET_FROM_JSON_OBJECT(val, current_height, current_height); +} + + +rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes); + + return val; +} + +void GetTransactions::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes); +} + +rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const +{ + rapidjson::Value val(rapidjson::kObjectType); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, txs, txs); + INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes); + + return val; +} + +void GetTransactions::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, txs, txs); + GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes); +} + + +rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); + + return val; +} + +void KeyImagesSpent::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, key_images, key_images); +} + +rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status); + + return val; +} + +void KeyImagesSpent::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, spent_status, spent_status); +} + + +rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash); + + return val; +} + +void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash); +} + +rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices); + + return val; +} + +void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, output_indices, output_indices); +} + + +rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); + INSERT_INTO_JSON_OBJECT(val, doc, count, count); + + return val; +} + +void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts, amounts); + GET_FROM_JSON_OBJECT(val, count, count); +} + +rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs); + + return val; +} + +void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs); +} + + +rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, tx, tx); + INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); + + return val; +} + +void SendRawTx::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx, tx); + GET_FROM_JSON_OBJECT(val, relay, relay); +} + +rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed); + + return val; +} + + +void SendRawTx::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, relayed, relayed); +} + +rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address); + INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining); + INSERT_INTO_JSON_OBJECT(val, doc, ignore_battery, ignore_battery); + + return val; +} + +void StartMining::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, miner_address, miner_address); + GET_FROM_JSON_OBJECT(val, threads_count, threads_count); + GET_FROM_JSON_OBJECT(val, do_background_mining, do_background_mining); + GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery); +} + +rapidjson::Value StartMining::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StartMining::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StopMining::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void StopMining::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void MiningStatus::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, active, active); + INSERT_INTO_JSON_OBJECT(val, doc, speed, speed); + INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count); + INSERT_INTO_JSON_OBJECT(val, doc, address, address); + INSERT_INTO_JSON_OBJECT(val, doc, is_background_mining_enabled, is_background_mining_enabled); + + return val; +} + +void MiningStatus::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, active, active); + GET_FROM_JSON_OBJECT(val, speed, speed); + GET_FROM_JSON_OBJECT(val, threads_count, threads_count); + GET_FROM_JSON_OBJECT(val, address, address); + GET_FROM_JSON_OBJECT(val, is_background_mining_enabled, is_background_mining_enabled); +} + + +rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetInfo::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, info, info); + + return val; +} + +void GetInfo::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, info, info); +} + + +rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void SaveBC::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void SaveBC::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, height); + + return val; +} + +void GetBlockHash::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + +rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); + + return val; +} + +void GetBlockHash::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hash, hash); +} + + +rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, hash, hash); + + return val; +} + +void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, hash, hash); +} + +rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, height, height); + + return val; +} + +void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, height, height); +} + +rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, header, header); + + return val; +} + +void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, header, header); +} + + +rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, heights, heights); + + return val; +} + +void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, heights, heights); +} + +rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, headers, headers); + + return val; +} + +void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, headers, headers); +} + + +rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + return val; +} + +void GetPeerList::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list); + INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list); + + return val; +} + +void GetPeerList::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, white_list, white_list); + GET_FROM_JSON_OBJECT(val, gray_list, gray_list); +} + + +rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + val.AddMember("level", level, al); + + return val; +} + +void SetLogLevel::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, level, level); +} + +rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void SetLogLevel::Response::fromJson(rapidjson::Value& val) +{ +} + + +rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetTransactionPool::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions); + INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images); + + return val; +} + +void GetTransactionPool::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, transactions, transactions); + GET_FROM_JSON_OBJECT(val, key_images, key_images); +} + + +rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, version); + + return val; +} + +void HardForkInfo::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, version, version); +} + +rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, info, info); + + return val; +} + +void HardForkInfo::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, info, info); +} + + +rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts); + INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count); + INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count); + INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked); + INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff); + + return val; +} + +void GetOutputHistogram::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, amounts, amounts); + GET_FROM_JSON_OBJECT(val, min_count, min_count); + GET_FROM_JSON_OBJECT(val, max_count, max_count); + GET_FROM_JSON_OBJECT(val, unlocked, unlocked); + GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff); +} + +rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram); + + return val; +} + +void GetOutputHistogram::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, histogram, histogram); +} + + +rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs); + + return val; +} + +void GetOutputKeys::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, outputs, outputs); +} + +rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, keys, keys); + + return val; +} + +void GetOutputKeys::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, keys, keys); +} + + +rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const +{ + return Message::toJson(doc); +} + +void GetRPCVersion::Request::fromJson(rapidjson::Value& val) +{ +} + +rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, version, version); + + return val; +} + +void GetRPCVersion::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, version, version); +} + +rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks); + + return val; +} + +void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks); +} + +rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + auto& al = doc.GetAllocator(); + + INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb); + + return val; +} + +void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb); +} + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h new file mode 100644 index 000000000..685f66843 --- /dev/null +++ b/src/rpc/daemon_messages.h @@ -0,0 +1,421 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "message.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/message_data_structs.h" +#include "rpc/daemon_rpc_version.h" +#include "cryptonote_basic/cryptonote_basic.h" + +#define BEGIN_RPC_MESSAGE_CLASS(classname) \ +class classname \ +{ \ + public: \ + static const char* const name; + +#define BEGIN_RPC_MESSAGE_REQUEST \ + class Request : public Message \ + { \ + public: \ + Request() { } \ + ~Request() { } \ + rapidjson::Value toJson(rapidjson::Document& doc) const; \ + void fromJson(rapidjson::Value& val); + +#define BEGIN_RPC_MESSAGE_RESPONSE \ + class Response : public Message \ + { \ + public: \ + Response() { } \ + ~Response() { } \ + rapidjson::Value toJson(rapidjson::Document& doc) const; \ + void fromJson(rapidjson::Value& val); + +#define END_RPC_MESSAGE_REQUEST }; +#define END_RPC_MESSAGE_RESPONSE }; +#define END_RPC_MESSAGE_CLASS }; + +#define COMMA() , + +// NOTE: when using a type with multiple template parameters, +// replace any comma in the template specifier with the macro +// above, or the preprocessor will eat the comma in a bad way. +#define RPC_MESSAGE_MEMBER(type, name) type name; + + +namespace cryptonote +{ + +namespace rpc +{ + +BEGIN_RPC_MESSAGE_CLASS(GetHeight); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::list<crypto::hash>, block_ids); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(bool, prune); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_with_transactions>, blocks); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(uint64_t, current_height); + RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_output_indices>, output_indices); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetHashesFast); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::list<crypto::hash>, known_hashes); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::list<crypto::hash>, hashes); + RPC_MESSAGE_MEMBER(uint64_t, start_height); + RPC_MESSAGE_MEMBER(uint64_t, current_height); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetTransactions); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, tx_hashes); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::unordered_map<crypto::hash COMMA() cryptonote::rpc::transaction_info>, txs); + RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, missed_hashes); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(KeyImagesSpent); + enum STATUS { + UNSPENT = 0, + SPENT_IN_BLOCKCHAIN = 1, + SPENT_IN_POOL = 2, + }; + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<crypto::key_image>, key_images); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<uint64_t>, spent_status); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetTxGlobalOutputIndices); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(crypto::hash, tx_hash); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<uint64_t>, output_indices); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + + +BEGIN_RPC_MESSAGE_CLASS(GetRandomOutputsForAmounts); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts); + RPC_MESSAGE_MEMBER(uint64_t, count); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<amount_with_random_outputs>, amounts_with_outputs); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SendRawTx); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(cryptonote::transaction, tx); + RPC_MESSAGE_MEMBER(bool, relay); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, relayed); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StartMining); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::string, miner_address); + RPC_MESSAGE_MEMBER(uint64_t, threads_count); + RPC_MESSAGE_MEMBER(bool, do_background_mining); + RPC_MESSAGE_MEMBER(bool, ignore_battery); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetInfo); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(DaemonInfo, info); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopMining); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(MiningStatus); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(bool, active); + RPC_MESSAGE_MEMBER(uint64_t, speed); + RPC_MESSAGE_MEMBER(uint64_t, threads_count); + RPC_MESSAGE_MEMBER(std::string, address); + RPC_MESSAGE_MEMBER(bool, is_background_mining_enabled); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SaveBC); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHash); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(crypto::hash, hash); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockTemplate); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SubmitBlock); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetLastBlockHeader); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHash); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(crypto::hash, hash); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, height); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersByHeight); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<uint64_t>, heights); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::BlockHeaderResponse>, headers); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlock); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetPeerList); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<peer>, white_list); + RPC_MESSAGE_MEMBER(std::vector<peer>, gray_list); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetLogHashRate); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetLogLevel); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(int8_t, level); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetTransactionPool); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::tx_in_pool>, transactions); + RPC_MESSAGE_MEMBER(key_images_with_tx_hashes, key_images); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetConnections); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersRange); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopDaemon); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(StopSaveGraph); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(HardForkInfo); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint8_t, version); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(hard_fork_info, info); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetBans); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(SetBans); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(FlushTransactionPool); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetOutputHistogram); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts); + RPC_MESSAGE_MEMBER(uint64_t, min_count); + RPC_MESSAGE_MEMBER(uint64_t, max_count); + RPC_MESSAGE_MEMBER(bool, unlocked); + RPC_MESSAGE_MEMBER(uint64_t, recent_cutoff); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<output_amount_count>, histogram); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetOutputKeys); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::vector<output_amount_and_index>, outputs); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(std::vector<output_key_mask_unlocked>, keys); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion); + BEGIN_RPC_MESSAGE_REQUEST; + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint32_t, version); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(uint64_t, num_grace_blocks); + END_RPC_MESSAGE_REQUEST; + BEGIN_RPC_MESSAGE_RESPONSE; + RPC_MESSAGE_MEMBER(uint64_t, estimated_fee_per_kb); + END_RPC_MESSAGE_RESPONSE; +END_RPC_MESSAGE_CLASS; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h new file mode 100644 index 000000000..bb735fe7c --- /dev/null +++ b/src/rpc/daemon_rpc_version.h @@ -0,0 +1,44 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace cryptonote +{ + +namespace rpc +{ + +static const uint32_t DAEMON_RPC_VERSION_ZMQ_MINOR = 0; +static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 1; + +static const uint32_t DAEMON_RPC_VERSION_ZMQ = DAEMON_RPC_VERSION_ZMQ_MINOR + (DAEMON_RPC_VERSION_ZMQ_MAJOR << 16); + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp new file mode 100644 index 000000000..086820180 --- /dev/null +++ b/src/rpc/message.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "message.h" +#include "daemon_rpc_version.h" +#include "serialization/json_object.h" + +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +namespace cryptonote +{ + +namespace rpc +{ + +const char* Message::STATUS_OK = "OK"; +const char* Message::STATUS_RETRY = "Retry"; +const char* Message::STATUS_FAILED = "Failed"; +const char* Message::STATUS_BAD_REQUEST = "Invalid request type"; +const char* Message::STATUS_BAD_JSON = "Malformed json"; + +rapidjson::Value Message::toJson(rapidjson::Document& doc) const +{ + rapidjson::Value val(rapidjson::kObjectType); + + auto& al = doc.GetAllocator(); + + val.AddMember("status", rapidjson::StringRef(status.c_str()), al); + val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al); + INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION_ZMQ); + + return val; +} + +void Message::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, status, status); + GET_FROM_JSON_OBJECT(val, error_details, error_details); + GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version); +} + + +FullMessage::FullMessage(const std::string& request, Message* message) +{ + doc.SetObject(); + + doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator()); + doc.AddMember("params", message->toJson(doc), doc.GetAllocator()); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator()); +} + +FullMessage::FullMessage(Message* message) +{ + doc.SetObject(); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", "2.0", doc.GetAllocator()); + + if (message->status == Message::STATUS_OK) + { + doc.AddMember("response", message->toJson(doc), doc.GetAllocator()); + } + else + { + cryptonote::rpc::error err; + + err.error_str = message->status; + err.message = message->error_details; + + INSERT_INTO_JSON_OBJECT(doc, doc, error, err); + } +} + +FullMessage::FullMessage(const std::string& json_string, bool request) +{ + doc.Parse(json_string.c_str()); + if (doc.HasParseError()) + { + throw cryptonote::json::PARSE_FAIL(); + } + + OBJECT_HAS_MEMBER_OR_THROW(doc, "jsonrpc") + + if (request) + { + OBJECT_HAS_MEMBER_OR_THROW(doc, "method") + OBJECT_HAS_MEMBER_OR_THROW(doc, "params") + } + else + { + if (!doc.HasMember("response") && !doc.HasMember("error")) + { + throw cryptonote::json::MISSING_KEY("error/response"); + } + } +} + +std::string FullMessage::getJson() +{ + + if (!doc.HasMember("id")) + { + doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator()); + } + + rapidjson::StringBuffer buf; + + rapidjson::Writer<rapidjson::StringBuffer> writer(buf); + + doc.Accept(writer); + + return std::string(buf.GetString(), buf.GetSize()); +} + +std::string FullMessage::getRequestType() const +{ + OBJECT_HAS_MEMBER_OR_THROW(doc, "method") + return doc["method"].GetString(); +} + +rapidjson::Value& FullMessage::getMessage() +{ + if (doc.HasMember("params")) + { + return doc["params"]; + } + else if (doc.HasMember("response")) + { + return doc["response"]; + } + + //else + OBJECT_HAS_MEMBER_OR_THROW(doc, "error") + return doc["error"]; + +} + +rapidjson::Value FullMessage::getMessageCopy() +{ + rapidjson::Value& val = getMessage(); + + return rapidjson::Value(val, doc.GetAllocator()); +} + +rapidjson::Value& FullMessage::getID() +{ + OBJECT_HAS_MEMBER_OR_THROW(doc, "id") + return doc["id"]; +} + +void FullMessage::setID(rapidjson::Value& id) +{ + auto itr = doc.FindMember("id"); + if (itr != doc.MemberEnd()) + { + itr->value = id; + } + else + { + doc.AddMember("id", id, doc.GetAllocator()); + } +} + +cryptonote::rpc::error FullMessage::getError() +{ + cryptonote::rpc::error err; + err.use = false; + if (doc.HasMember("error")) + { + GET_FROM_JSON_OBJECT(doc, err, error); + err.use = true; + } + + return err; +} + +FullMessage FullMessage::requestMessage(const std::string& request, Message* message) +{ + return FullMessage(request, message); +} + +FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id) +{ + auto mes = requestMessage(request, message); + mes.setID(id); + return mes; +} + +FullMessage FullMessage::responseMessage(Message* message) +{ + return FullMessage(message); +} + +FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id) +{ + auto mes = responseMessage(message); + mes.setID(id); + return mes; +} + +FullMessage* FullMessage::timeoutMessage() +{ + auto *full_message = new FullMessage(); + + auto& doc = full_message->doc; + auto& al = full_message->doc.GetAllocator(); + + doc.SetObject(); + + // required by JSON-RPC 2.0 spec + doc.AddMember("jsonrpc", "2.0", al); + + cryptonote::rpc::error err; + + err.error_str = "RPC request timed out."; + INSERT_INTO_JSON_OBJECT(doc, doc, err, err); + + return full_message; +} + +// convenience functions for bad input +std::string BAD_REQUEST(const std::string& request) +{ + Message fail; + fail.status = Message::STATUS_BAD_REQUEST; + fail.error_details = std::string("\"") + request + "\" is not a valid request."; + + FullMessage fail_response = FullMessage::responseMessage(&fail); + + return fail_response.getJson(); +} + +std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id) +{ + Message fail; + fail.status = Message::STATUS_BAD_REQUEST; + fail.error_details = std::string("\"") + request + "\" is not a valid request."; + + FullMessage fail_response = FullMessage::responseMessage(&fail, id); + + return fail_response.getJson(); +} + +std::string BAD_JSON(const std::string& error_details) +{ + Message fail; + fail.status = Message::STATUS_BAD_JSON; + fail.error_details = error_details; + + FullMessage fail_response = FullMessage::responseMessage(&fail); + + return fail_response.getJson(); +} + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message.h b/src/rpc/message.h new file mode 100644 index 000000000..d1abe3fbe --- /dev/null +++ b/src/rpc/message.h @@ -0,0 +1,131 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "rapidjson/document.h" +#include "rpc/message_data_structs.h" +#include <string> + +/* I normally hate using macros, but in this case it would be untenably + * verbose to not use a macro. This macro saves the trouble of explicitly + * writing the below if block for every single RPC call. + */ +#define REQ_RESP_TYPES_MACRO( runtime_str, type, reqjson, resp_message_ptr, handler) \ + \ + if (runtime_str == type::name) \ + { \ + type::Request reqvar; \ + type::Response *respvar = new type::Response(); \ + \ + reqvar.fromJson(reqjson); \ + \ + handler(reqvar, *respvar); \ + \ + resp_message_ptr = respvar; \ + } + +namespace cryptonote +{ + +namespace rpc +{ + + class Message + { + public: + static const char* STATUS_OK; + static const char* STATUS_RETRY; + static const char* STATUS_FAILED; + static const char* STATUS_BAD_REQUEST; + static const char* STATUS_BAD_JSON; + + Message() : status(STATUS_OK) { } + + virtual ~Message() { } + + virtual rapidjson::Value toJson(rapidjson::Document& doc) const; + + virtual void fromJson(rapidjson::Value& val); + + std::string status; + std::string error_details; + uint32_t rpc_version; + }; + + class FullMessage + { + public: + ~FullMessage() { } + + FullMessage(FullMessage&& rhs) noexcept : doc(std::move(rhs.doc)) { } + + FullMessage(const std::string& json_string, bool request=false); + + std::string getJson(); + + std::string getRequestType() const; + + rapidjson::Value& getMessage(); + + rapidjson::Value getMessageCopy(); + + rapidjson::Value& getID(); + + void setID(rapidjson::Value& id); + + cryptonote::rpc::error getError(); + + static FullMessage requestMessage(const std::string& request, Message* message); + static FullMessage requestMessage(const std::string& request, Message* message, rapidjson::Value& id); + + static FullMessage responseMessage(Message* message); + static FullMessage responseMessage(Message* message, rapidjson::Value& id); + + static FullMessage* timeoutMessage(); + private: + + FullMessage() = default; + + FullMessage(const std::string& request, Message* message); + FullMessage(Message* message); + + rapidjson::Document doc; + }; + + + // convenience functions for bad input + std::string BAD_REQUEST(const std::string& request); + std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id); + + std::string BAD_JSON(const std::string& error_details); + + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h new file mode 100644 index 000000000..00f1e0caa --- /dev/null +++ b/src/rpc/message_data_structs.h @@ -0,0 +1,189 @@ +// Copyright (c) 2016-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "crypto/hash.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "ringct/rctSigs.h" + +#include <unordered_map> +#include <vector> + +namespace cryptonote +{ + +namespace rpc +{ + + struct block_with_transactions + { + cryptonote::block block; + std::unordered_map<crypto::hash, cryptonote::transaction> transactions; + }; + + typedef std::vector<uint64_t> tx_output_indices; + + typedef std::vector<tx_output_indices> block_output_indices; + + struct transaction_info + { + cryptonote::transaction transaction; + bool in_pool; + uint64_t height; + }; + + struct output_key_and_amount_index + { + uint64_t amount_index; + crypto::public_key key; + }; + + typedef std::vector<output_key_and_amount_index> outputs_for_amount; + + struct amount_with_random_outputs + { + uint64_t amount; + outputs_for_amount outputs; + }; + + struct peer + { + uint64_t id; + uint32_t ip; + uint16_t port; + uint64_t last_seen; + }; + + struct tx_in_pool + { + cryptonote::transaction tx; + crypto::hash tx_hash; + uint64_t blob_size; + uint64_t fee; + crypto::hash max_used_block_hash; + uint64_t max_used_block_height; + bool kept_by_block; + crypto::hash last_failed_block_hash; + uint64_t last_failed_block_height; + uint64_t receive_time; + uint64_t last_relayed_time; + bool relayed; + bool do_not_relay; + }; + + typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes; + + struct output_amount_count + { + uint64_t amount; + uint64_t total_count; + uint64_t unlocked_count; + uint64_t recent_count; + }; + + struct output_amount_and_index + { + uint64_t amount; + uint64_t index; + }; + + struct output_key_mask_unlocked + { + crypto::public_key key; + rct::key mask; + bool unlocked; + }; + + struct hard_fork_info + { + uint8_t version; + bool enabled; + uint32_t window; + uint32_t votes; + uint32_t threshold; + uint8_t voting; + uint32_t state; + uint64_t earliest_height; + }; + + //required by JSON-RPC 2.0 spec + struct error + { + // not really using code, maybe later. + error() : use(false), code(1) { } + + bool use; // do not serialize + + int32_t code; + + // not required by spec, but int error codes aren't perfect + std::string error_str; + + std::string message; + + //TODO: data member? not required, may want later. + }; + + struct BlockHeaderResponse + { + uint64_t major_version; + uint64_t minor_version; + uint64_t timestamp; + crypto::hash prev_id; + uint32_t nonce; + uint64_t height; + uint64_t depth; + crypto::hash hash; + uint64_t difficulty; + uint64_t reward; + }; + + struct DaemonInfo + { + uint64_t height; + uint64_t target_height; + uint64_t difficulty; + uint64_t target; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + bool testnet; + crypto::hash top_block_hash; + uint64_t cumulative_difficulty; + uint64_t block_size_limit; + uint64_t start_time; + }; + +} // namespace rpc + +} // namespace cryptonote diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h new file mode 100644 index 000000000..d75180199 --- /dev/null +++ b/src/rpc/rpc_handler.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> + +namespace cryptonote +{ + +namespace rpc +{ + + +class RpcHandler +{ + public: + + virtual std::string handle(const std::string& request) = 0; + + RpcHandler() { } + + virtual ~RpcHandler() { } +}; + + +} // rpc + +} // cryptonote diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp new file mode 100644 index 000000000..afdff2328 --- /dev/null +++ b/src/rpc/zmq_server.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "zmq_server.h" +#include <boost/chrono/chrono.hpp> + +namespace cryptonote +{ + +namespace rpc +{ + +ZmqServer::ZmqServer(RpcHandler& h) : + handler(h), + stop_signal(false), + running(false), + context(DEFAULT_NUM_ZMQ_THREADS) // TODO: make this configurable +{ +} + +ZmqServer::~ZmqServer() +{ +} + +void ZmqServer::serve() +{ + + while (1) + { + try + { + zmq::message_t message; + + if (!rep_socket) + { + throw std::runtime_error("ZMQ RPC server reply socket is null"); + } + while (rep_socket->recv(&message)) + { + std::string message_string(reinterpret_cast<const char *>(message.data()), message.size()); + + MDEBUG(std::string("Received RPC request: \"") + message_string + "\""); + + std::string response = handler.handle(message_string); + + zmq::message_t reply(response.size()); + memcpy((void *) reply.data(), response.c_str(), response.size()); + + rep_socket->send(reply); + MDEBUG(std::string("Sent RPC reply: \"") + response + "\""); + + } + } + catch (const boost::thread_interrupted& e) + { + MDEBUG("ZMQ Server thread interrupted."); + } + catch (const zmq::error_t& e) + { + MERROR(std::string("ZMQ error: ") + e.what()); + } + boost::this_thread::interruption_point(); + } +} + +bool ZmqServer::addIPCSocket(std::string address, std::string port) +{ + MERROR("ZmqServer::addIPCSocket not yet implemented!"); + return false; +} + +bool ZmqServer::addTCPSocket(std::string address, std::string port) +{ + try + { + std::string addr_prefix("tcp://"); + + rep_socket.reset(new zmq::socket_t(context, ZMQ_REP)); + + rep_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS); + + std::string bind_address = addr_prefix + address + std::string(":") + port; + rep_socket->bind(bind_address.c_str()); + } + catch (const std::exception& e) + { + MERROR(std::string("Error creating ZMQ Socket: ") + e.what()); + return false; + } + return true; +} + +void ZmqServer::run() +{ + running = true; + run_thread = boost::thread(boost::bind(&ZmqServer::serve, this)); +} + +void ZmqServer::stop() +{ + if (!running) return; + + stop_signal = true; + + run_thread.interrupt(); + run_thread.join(); + + running = false; + + return; +} + + +} // namespace cryptonote + +} // namespace rpc diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h new file mode 100644 index 000000000..c278ff759 --- /dev/null +++ b/src/rpc/zmq_server.h @@ -0,0 +1,83 @@ +// Copyright (c) 2016, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <boost/thread/thread.hpp> +#include <zmq.hpp> +#include <string> +#include <memory> + +#include "common/command_line.h" + +#include "rpc_handler.h" + +namespace cryptonote +{ + +namespace rpc +{ + +static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1; +static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000; + +class ZmqServer +{ + public: + + ZmqServer(RpcHandler& h); + + ~ZmqServer(); + + static void init_options(boost::program_options::options_description& desc); + + void serve(); + + bool addIPCSocket(std::string address, std::string port); + bool addTCPSocket(std::string address, std::string port); + + void run(); + void stop(); + + private: + RpcHandler& handler; + + volatile bool stop_signal; + volatile bool running; + + zmq::context_t context; + + boost::thread run_thread; + + std::unique_ptr<zmq::socket_t> rep_socket; +}; + + +} // namespace cryptonote + +} // namespace rpc |