aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
authorThomas Winget <tewinget@gmail.com>2017-09-05 12:20:27 -0400
committerThomas Winget <tewinget@gmail.com>2017-09-05 12:20:27 -0400
commit77986023c37737e298fc7c3e9938cce0e10d8b80 (patch)
tree81e024d5aea2cdce81f93d155634da6e3c6a2f04 /src/rpc
parentRefactor some things into more composable (smaller) functions (diff)
downloadmonero-77986023c37737e298fc7c3e9938cce0e10d8b80.tar.xz
json serialization for rpc-relevant monero types
Structured {de-,}serialization methods for (many new) types which are used for requests or responses in the RPC. New types include RPC requests and responses, and structs which compose types within those. # Conflicts: # src/cryptonote_core/blockchain.cpp
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/CMakeLists.txt68
-rw-r--r--src/rpc/daemon_handler.cpp800
-rw-r--r--src/rpc/daemon_handler.h147
-rw-r--r--src/rpc/daemon_messages.cpp800
-rw-r--r--src/rpc/daemon_messages.h436
-rw-r--r--src/rpc/daemon_rpc_version.h44
-rw-r--r--src/rpc/message.cpp286
-rw-r--r--src/rpc/message.h131
-rw-r--r--src/rpc/message_data_structs.h169
-rw-r--r--src/rpc/rpc_handler.h54
-rw-r--r--src/rpc/zmq_server.cpp146
-rw-r--r--src/rpc/zmq_server.h84
12 files changed, 3164 insertions, 1 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index f6037d7e3..717e3e627 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -30,20 +30,61 @@ set(rpc_sources
core_rpc_server.cpp
rpc_args.cpp)
+set(daemon_messages_sources
+ message.cpp
+ daemon_messages.cpp)
+
+set(daemon_rpc_server_sources
+ daemon_handler.cpp
+ zmq_server.cpp)
+
+
set(rpc_headers
rpc_args.h)
-set(rpc_private_headers
+set(daemon_rpc_server_headers)
+
+
+set(rpc_daemon_private_headers
core_rpc_server.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
+set(daemon_messages_private_headers
+ message.h
+ daemon_messages.h)
+
+set(daemon_rpc_server_private_headers
+ message.h
+ daemon_messages.h
+ daemon_handler.h
+ rpc_handler.h
+ zmq_server.h)
+
+
monero_private_headers(rpc
${rpc_private_headers})
+
+monero_private_headers(daemon_rpc_server
+ ${daemon_rpc_server_private_headers})
+
+
monero_add_library(rpc
${rpc_sources}
${rpc_headers}
${rpc_private_headers})
+
+monero_add_library(daemon_messages
+ ${daemon_messages_sources}
+ ${daemon_messages_headers}
+ ${daemon_messages_private_headers})
+
+monero_add_library(daemon_rpc_server
+ ${daemon_rpc_server_sources}
+ ${daemon_rpc_server_headers}
+ ${daemon_rpc_server_private_headers})
+
+
target_link_libraries(rpc
PUBLIC
common
@@ -54,5 +95,30 @@ target_link_libraries(rpc
${Boost_THREAD_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
+
+target_link_libraries(daemon_messages
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ serialization
+ ${EXTRA_LIBRARIES})
+
+target_link_libraries(daemon_rpc_server
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ daemon_messages
+ serialization
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${ZMQ_LIB}
+ ${EXTRA_LIBRARIES})
+
+
add_dependencies(rpc
version)
+
+add_dependencies(daemon_rpc_server
+ version)
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
new file mode 100644
index 000000000..6a287b229
--- /dev/null
+++ b/src/rpc/daemon_handler.cpp
@@ -0,0 +1,800 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "daemon_handler.h"
+
+// likely included by daemon_handler.h's includes,
+// but including here for clarity
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_protocol/blobdatatype.h"
+#include "ringct/rctSigs.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res)
+ {
+ res.height = m_core.get_current_blockchain_height();
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res)
+ {
+ std::list<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;
+ }
+
+ 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 block";
+ return;
+ }
+ }
+
+ cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count];
+
+ // miner tx output indices
+ {
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+ indices.push_back(tx_indices);
+ }
+
+ // assume each block returned is returned with all its transactions
+ // in the correct order.
+ auto tx_it = txs.begin();
+ for (const crypto::hash& h : bwt.block.tx_hashes)
+ {
+ bwt.transactions.emplace(h, *tx_it);
+ tx_it++;
+
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(h, tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+
+ indices.push_back(tx_indices);
+ }
+
+ it++;
+ block_count++;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res)
+ {
+ res.start_height = req.start_height;
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Blockchain::find_blockchain_supplement() returned false";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
+ {
+ std::list<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)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res)
+ {
+ res.height = m_core.get_current_blockchain_height();
+
+ res.target_height = m_core.get_target_blockchain_height();
+
+ if (res.height > res.target_height)
+ {
+ res.target_height = res.height;
+ }
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ res.difficulty = chain.get_difficulty_for_next_block();
+
+ res.target = chain.get_difficulty_target();
+
+ res.tx_count = chain.get_total_transactions() - res.height; //without coinbase
+
+ res.tx_pool_size = m_core.get_pool_transactions_count();
+
+ res.alt_blocks_count = chain.get_alternative_blocks_count();
+
+ uint64_t total_conn = m_p2p.get_connections_count();
+ res.outgoing_connections_count = m_p2p.get_outgoing_connections_count();
+ res.incoming_connections_count = total_conn - res.outgoing_connections_count;
+
+ res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count();
+
+ res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count();
+
+ res.testnet = m_core.get_testnet();
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res)
+ {
+ if (!m_core.get_blockchain_storage().store_blockchain())
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error storing the blockchain";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ }
+ }
+
+ void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res)
+ {
+ if (m_core.get_current_blockchain_height() <= req.height)
+ {
+ res.hash = cryptonote::null_hash;
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "height given is higher than current chain height";
+ return;
+ }
+
+ res.hash = m_core.get_block_id_by_height(req.height);
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SubmitBlock::Request& req, SubmitBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_tail_id();
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res)
+ {
+ if (!getBlockHeaderByHash(req.hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ //TODO: this RPC call is marked for later implementation in the old RPC,
+ // need to sort out if it's necessary and what it should do.
+ void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res)
+ {
+ if (req.level < 0 || req.level > 4)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error: log level not valid";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ mlog_set_log_level(req.level);
+ }
+ }
+
+ void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
+ {
+ bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
+
+ if (!r) res.status = Message::STATUS_FAILED;
+ else res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const FastExit::Request& req, FastExit::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const OutPeers::Request& req, OutPeers::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res)
+ {
+ const Blockchain &blockchain = m_core.get_blockchain_storage();
+ uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version();
+ res.info.version = blockchain.get_current_hard_fork_version();
+ res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting);
+ res.info.state = blockchain.get_hard_fork_state();
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res)
+ {
+ std::map<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)
+ {
+ for (const auto& i : req.outputs)
+ {
+ crypto::public_key key;
+ rct::key mask;
+ bool unlocked;
+ m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked);
+ res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked});
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res)
+ {
+ res.version = DAEMON_RPC_VERSION;
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
+ {
+ res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks);
+ res.status = Message::STATUS_OK;
+ }
+
+ bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
+ {
+ block b;
+
+ if (!m_core.get_block_by_hash(hash_in, b))
+ {
+ return false;
+ }
+
+ header.hash = hash_in;
+ header.height = boost::get<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, SaveBC, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle);
+
+ // if none of the request types matches
+ if (resp_message == NULL)
+ {
+ return BAD_REQUEST(request_type, req_full.getID());
+ }
+
+ FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID());
+
+ const std::string response = resp_full.getJson();
+ delete resp_message;
+ resp_message = NULL;
+
+ MDEBUG("Returning RPC response: " << response);
+
+ return response;
+ }
+ catch (const std::exception& e)
+ {
+ if (resp_message)
+ {
+ delete resp_message;
+ }
+
+ return BAD_JSON(e.what());
+ }
+ }
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
new file mode 100644
index 000000000..9997956c7
--- /dev/null
+++ b/src/rpc/daemon_handler.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "daemon_messages.h"
+#include "daemon_rpc_version.h"
+#include "rpc_handler.h"
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_protocol/cryptonote_protocol_handler.h"
+#include "p2p/net_node.h"
+
+namespace
+{
+ typedef nodetool::node_server<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 GetBlock::Request& req, GetBlock::Response& res);
+
+ void handle(const GetPeerList::Request& req, GetPeerList::Response& res);
+
+ void handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res);
+
+ void handle(const SetLogLevel::Request& req, SetLogLevel::Response& res);
+
+ void handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res);
+
+ void handle(const GetConnections::Request& req, GetConnections::Response& res);
+
+ void handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res);
+
+ void handle(const StopDaemon::Request& req, StopDaemon::Response& res);
+
+ void handle(const FastExit::Request& req, FastExit::Response& res);
+
+ void handle(const OutPeers::Request& req, OutPeers::Response& res);
+
+ void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res);
+
+ void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res);
+
+ void handle(const HardForkInfo::Request& req, HardForkInfo::Response& res);
+
+ void handle(const GetBans::Request& req, GetBans::Response& res);
+
+ void handle(const SetBans::Request& req, SetBans::Response& res);
+
+ void handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res);
+
+ void handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res);
+
+ void handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res);
+
+ void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res);
+
+ void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res);
+
+ std::string handle(const std::string& request);
+
+ private:
+
+ bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response);
+
+ cryptonote::core& m_core;
+ t_p2p& m_p2p;
+};
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
new file mode 100644
index 000000000..b999a0f18
--- /dev/null
+++ b/src/rpc/daemon_messages.cpp
@@ -0,0 +1,800 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "daemon_messages.h"
+#include "serialization/json_object.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* const GetHeight::name = "get_height";
+const char* const GetBlocksFast::name = "get_blocks_fast";
+const char* const GetHashesFast::name = "get_hashes_fast";
+const char* const GetTransactions::name = "get_transactions";
+const char* const KeyImagesSpent::name = "key_images_spent";
+const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
+const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts";
+const char* const SendRawTx::name = "send_raw_tx";
+const char* const GetInfo::name = "get_info";
+const char* const SaveBC::name = "save_bc";
+const char* const GetBlockHash::name = "get_block_hash";
+const char* const GetLastBlockHeader::name = "get_last_block_header";
+const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash";
+const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height";
+const char* const GetPeerList::name = "get_peer_list";
+const char* const SetLogLevel::name = "set_log_level";
+const char* const GetTransactionPool::name = "get_transaction_pool";
+const char* const HardForkInfo::name = "hard_fork_info";
+const char* const GetOutputHistogram::name = "get_output_histogram";
+const char* const GetOutputKeys::name = "get_output_keys";
+const char* const GetRPCVersion::name = "get_rpc_version";
+const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate";
+
+
+
+
+rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetHeight::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("height", height, al);
+
+ return val;
+}
+
+void GetHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+
+rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids);
+ val.AddMember("start_height", start_height, al);
+
+ return val;
+}
+
+void GetBlocksFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, block_ids, block_ids);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+}
+
+rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetBlocksFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, blocks, blocks);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes);
+ val.AddMember("start_height", start_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+}
+
+rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hashes, hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+}
+
+
+rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes);
+
+ return val;
+}
+
+void GetTransactions::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes);
+}
+
+rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, txs, txs);
+ INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes);
+
+ return val;
+}
+
+void GetTransactions::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, txs, txs);
+ GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes);
+}
+
+
+rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void KeyImagesSpent::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status);
+
+ return val;
+}
+
+void KeyImagesSpent::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, spent_status, spent_status);
+}
+
+
+rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash);
+}
+
+rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, count, count);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, count, count);
+}
+
+rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs);
+}
+
+
+rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx, tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
+
+ return val;
+}
+
+void SendRawTx::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx, tx);
+ GET_FROM_JSON_OBJECT(val, relay, relay);
+}
+
+rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed);
+
+ return val;
+}
+
+void SendRawTx::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, relayed, relayed);
+}
+
+
+rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetInfo::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+ INSERT_INTO_JSON_OBJECT(val, doc, target_height, target_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, difficulty, difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, target, target);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_count, tx_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, tx_pool_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, alt_blocks_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, outgoing_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, incoming_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, white_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, grey_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, testnet, testnet);
+ INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, top_block_hash);
+
+ return val;
+}
+
+void GetInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+ GET_FROM_JSON_OBJECT(val, target_height, target_height);
+ GET_FROM_JSON_OBJECT(val, difficulty, difficulty);
+ GET_FROM_JSON_OBJECT(val, target, target);
+ GET_FROM_JSON_OBJECT(val, tx_count, tx_count);
+ GET_FROM_JSON_OBJECT(val, tx_pool_size, tx_pool_size);
+ GET_FROM_JSON_OBJECT(val, alt_blocks_count, alt_blocks_count);
+ GET_FROM_JSON_OBJECT(val, outgoing_connections_count, outgoing_connections_count);
+ GET_FROM_JSON_OBJECT(val, incoming_connections_count, incoming_connections_count);
+ GET_FROM_JSON_OBJECT(val, white_peerlist_size, white_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, grey_peerlist_size, grey_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, testnet, testnet);
+ GET_FROM_JSON_OBJECT(val, top_block_hash, top_block_hash);
+}
+
+
+rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+
+rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetPeerList::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list);
+ INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list);
+
+ return val;
+}
+
+void GetPeerList::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, white_list, white_list);
+ GET_FROM_JSON_OBJECT(val, gray_list, gray_list);
+}
+
+
+rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("level", level, al);
+
+ return val;
+}
+
+void SetLogLevel::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, level, level);
+}
+
+rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void SetLogLevel::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetTransactionPool::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void GetTransactionPool::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, transactions, transactions);
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+
+rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void HardForkInfo::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, info, info);
+
+ return val;
+}
+
+void HardForkInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, info, info);
+}
+
+
+rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked);
+ INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff);
+
+ return val;
+}
+
+void GetOutputHistogram::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, min_count, min_count);
+ GET_FROM_JSON_OBJECT(val, max_count, max_count);
+ GET_FROM_JSON_OBJECT(val, unlocked, unlocked);
+ GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff);
+}
+
+rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram);
+
+ return val;
+}
+
+void GetOutputHistogram::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, histogram, histogram);
+}
+
+
+rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs);
+
+ return val;
+}
+
+void GetOutputKeys::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, outputs, outputs);
+}
+
+rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, keys, keys);
+
+ return val;
+}
+
+void GetOutputKeys::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, keys, keys);
+}
+
+
+rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetRPCVersion::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb);
+}
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
new file mode 100644
index 000000000..adcd7f7c6
--- /dev/null
+++ b/src/rpc/daemon_messages.h
@@ -0,0 +1,436 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "message.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "rpc/message_data_structs.h"
+#include "rpc/daemon_rpc_version.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+
+#define BEGIN_RPC_MESSAGE_CLASS(classname) \
+class classname \
+{ \
+ public: \
+ static const char* const name;
+
+#define BEGIN_RPC_MESSAGE_REQUEST \
+ class Request : public Message \
+ { \
+ public: \
+ Request() { } \
+ ~Request() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define BEGIN_RPC_MESSAGE_RESPONSE \
+ class Response : public Message \
+ { \
+ public: \
+ Response() { } \
+ ~Response() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define END_RPC_MESSAGE_REQUEST };
+#define END_RPC_MESSAGE_RESPONSE };
+#define END_RPC_MESSAGE_CLASS };
+
+#define COMMA() ,
+
+// NOTE: when using a type with multiple template parameters,
+// replace any comma in the template specifier with the macro
+// above, or the preprocessor will eat the comma in a bad way.
+#define RPC_MESSAGE_MEMBER(type, name) type name;
+
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+BEGIN_RPC_MESSAGE_CLASS(GetHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, block_ids);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ 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, thread_count);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, success);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ RPC_MESSAGE_MEMBER(uint64_t, target_height);
+ RPC_MESSAGE_MEMBER(uint64_t, difficulty);
+ RPC_MESSAGE_MEMBER(uint64_t, target);
+ RPC_MESSAGE_MEMBER(uint64_t, tx_count);
+ RPC_MESSAGE_MEMBER(uint64_t, tx_pool_size);
+ RPC_MESSAGE_MEMBER(uint64_t, alt_blocks_count);
+ RPC_MESSAGE_MEMBER(uint64_t, outgoing_connections_count);
+ RPC_MESSAGE_MEMBER(uint64_t, incoming_connections_count);
+ RPC_MESSAGE_MEMBER(uint64_t, white_peerlist_size);
+ RPC_MESSAGE_MEMBER(uint64_t, grey_peerlist_size);
+ RPC_MESSAGE_MEMBER(bool, testnet);
+ RPC_MESSAGE_MEMBER(crypto::hash, top_block_hash);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopMining);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, success);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(MiningStatus);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, active);
+ RPC_MESSAGE_MEMBER(uint64_t, speed);
+ RPC_MESSAGE_MEMBER(uint64_t, thread_count);
+ RPC_MESSAGE_MEMBER(std::string, address);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SaveBC);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockTemplate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SubmitBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetLastBlockHeader);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetPeerList);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<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(FastExit);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(OutPeers);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(HardForkInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint8_t, version);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(hard_fork_info, info);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(FlushTransactionPool);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputHistogram);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<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..b354b32c2
--- /dev/null
+++ b/src/rpc/daemon_rpc_version.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+static const uint32_t DAEMON_RPC_VERSION_MINOR = 0;
+static const uint32_t DAEMON_RPC_VERSION_MAJOR = 1;
+
+static const uint32_t DAEMON_RPC_VERSION = DAEMON_RPC_VERSION_MINOR + (DAEMON_RPC_VERSION_MAJOR << 16);
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
new file mode 100644
index 000000000..7d4499bcf
--- /dev/null
+++ b/src/rpc/message.cpp
@@ -0,0 +1,286 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "message.h"
+#include "daemon_rpc_version.h"
+#include "serialization/json_object.h"
+
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* Message::STATUS_OK = "OK";
+const char* Message::STATUS_RETRY = "Retry";
+const char* Message::STATUS_FAILED = "Failed";
+const char* Message::STATUS_BAD_REQUEST = "Invalid request type";
+const char* Message::STATUS_BAD_JSON = "Malformed json";
+
+rapidjson::Value Message::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("status", rapidjson::StringRef(status.c_str()), al);
+ val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al);
+ INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION);
+
+ return val;
+}
+
+void Message::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, status, status);
+ GET_FROM_JSON_OBJECT(val, error_details, error_details);
+ GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version);
+}
+
+
+FullMessage::FullMessage(const std::string& request, Message* message)
+{
+ doc.SetObject();
+
+ doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator());
+ doc.AddMember("params", message->toJson(doc), doc.GetAllocator());
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator());
+}
+
+FullMessage::FullMessage(Message* message)
+{
+ doc.SetObject();
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", "2.0", doc.GetAllocator());
+
+ if (message->status == Message::STATUS_OK)
+ {
+ doc.AddMember("response", message->toJson(doc), doc.GetAllocator());
+ }
+ else
+ {
+ cryptonote::rpc::error err;
+
+ err.error_str = message->status;
+ err.message = message->error_details;
+
+ INSERT_INTO_JSON_OBJECT(doc, doc, error, err);
+ }
+}
+
+FullMessage::FullMessage(const std::string& json_string, bool request)
+{
+ doc.Parse(json_string.c_str());
+ if (doc.HasParseError())
+ {
+ throw cryptonote::json::PARSE_FAIL();
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "jsonrpc")
+
+ if (request)
+ {
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "params")
+ }
+ else
+ {
+ if (!doc.HasMember("response") && !doc.HasMember("error"))
+ {
+ throw cryptonote::json::MISSING_KEY("error/response");
+ }
+ }
+}
+
+std::string FullMessage::getJson()
+{
+
+ if (!doc.HasMember("id"))
+ {
+ doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator());
+ }
+
+ rapidjson::StringBuffer buf;
+
+ rapidjson::Writer<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..c2492681d
--- /dev/null
+++ b/src/rpc/message_data_structs.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2016-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include "crypto/hash.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "ringct/rctSigs.h"
+
+#include <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;
+ };
+
+} // 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..8e1e47b38
--- /dev/null
+++ b/src/rpc/zmq_server.cpp
@@ -0,0 +1,146 @@
+// Copyright (c) 2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "zmq_server.h"
+#include <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()
+{
+ for (zmq::socket_t* socket : sockets)
+ {
+ delete socket;
+ }
+}
+
+void ZmqServer::serve()
+{
+
+ while (1)
+ {
+ try
+ {
+ for (zmq::socket_t* socket : sockets)
+ {
+ zmq::message_t message;
+
+ while (socket->recv(&message))
+ {
+ std::string message_string(reinterpret_cast<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());
+
+ socket->send(reply);
+ MDEBUG(std::string("Sent RPC reply: \"") + response + "\"");
+ }
+
+ }
+ }
+ catch (boost::thread_interrupted& e)
+ {
+ MDEBUG("ZMQ Server thread interrupted.");
+ }
+ boost::this_thread::interruption_point();
+ }
+}
+
+bool ZmqServer::addIPCSocket(std::string address, std::string port)
+{
+ MERROR("ZmqServer::addIPCSocket not yet implemented!");
+ return false;
+}
+
+bool ZmqServer::addTCPSocket(std::string address, std::string port)
+{
+ zmq::socket_t *new_socket = nullptr;
+ try
+ {
+ std::string addr_prefix("tcp://");
+
+ new_socket = new zmq::socket_t(context, ZMQ_REP);
+
+ new_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS);
+
+ std::string bind_address = addr_prefix + address + std::string(":") + port;
+ new_socket->bind(bind_address.c_str());
+ sockets.push_back(new_socket);
+ }
+ catch (std::exception& e)
+ {
+ MERROR(std::string("Error creating ZMQ Socket: ") + e.what());
+ if (new_socket)
+ {
+ delete new_socket;
+ }
+ return false;
+ }
+ return true;
+}
+
+void ZmqServer::run()
+{
+ running = true;
+ run_thread = boost::thread(boost::bind(&ZmqServer::serve, this));
+}
+
+void ZmqServer::stop()
+{
+ if (!running) return;
+
+ stop_signal = true;
+
+ run_thread.interrupt();
+ run_thread.join();
+
+ running = false;
+
+ return;
+}
+
+
+} // namespace cryptonote
+
+} // namespace rpc
diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h
new file mode 100644
index 000000000..964ce2071
--- /dev/null
+++ b/src/rpc/zmq_server.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <boost/thread/thread.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include "common/command_line.h"
+
+#include <zmq.hpp>
+#include <string>
+
+#include "rpc_handler.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1;
+static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000;
+
+class ZmqServer
+{
+ public:
+
+ ZmqServer(RpcHandler& h);
+
+ ~ZmqServer();
+
+ static void init_options(boost::program_options::options_description& desc);
+
+ void serve();
+
+ bool addIPCSocket(std::string address, std::string port);
+ bool addTCPSocket(std::string address, std::string port);
+
+ void run();
+ void stop();
+
+ private:
+ RpcHandler& handler;
+
+ volatile bool stop_signal;
+ volatile bool running;
+
+ zmq::context_t context;
+
+ boost::thread run_thread;
+
+ std::vector<zmq::socket_t*> sockets;
+};
+
+
+} // namespace cryptonote
+
+} // namespace rpc