aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/CMakeLists.txt8
-rw-r--r--src/rpc/core_rpc_server.cpp201
-rw-r--r--src/rpc/core_rpc_server.h5
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h79
-rw-r--r--src/rpc/daemon_handler.cpp50
-rw-r--r--src/rpc/daemon_handler.h4
-rw-r--r--src/rpc/daemon_messages.cpp124
-rw-r--r--src/rpc/daemon_messages.h27
-rw-r--r--src/rpc/message_data_structs.h8
-rw-r--r--src/rpc/rpc_handler.cpp69
-rw-r--r--src/rpc/rpc_handler.h17
-rw-r--r--src/rpc/zmq_server.cpp1
12 files changed, 398 insertions, 195 deletions
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index 4f8f96524..d2c4a33cb 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -31,6 +31,7 @@ set(rpc_base_sources
set(rpc_sources
core_rpc_server.cpp
+ rpc_handler.cpp
instanciations)
set(daemon_messages_sources
@@ -45,7 +46,8 @@ set(daemon_rpc_server_sources
set(rpc_base_headers
rpc_args.h)
-set(rpc_headers)
+set(rpc_headers
+ rpc_handler.cpp)
set(daemon_rpc_server_headers)
@@ -63,7 +65,6 @@ set(daemon_rpc_server_private_headers
message.h
daemon_messages.h
daemon_handler.h
- rpc_handler.h
zmq_server.h)
@@ -111,6 +112,7 @@ target_link_libraries(rpc
common
cryptonote_core
cryptonote_protocol
+ version
${Boost_REGEX_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
@@ -120,6 +122,7 @@ target_link_libraries(daemon_messages
LINK_PRIVATE
cryptonote_core
cryptonote_protocol
+ version
serialization
${EXTRA_LIBRARIES})
@@ -128,6 +131,7 @@ target_link_libraries(daemon_rpc_server
rpc
cryptonote_core
cryptonote_protocol
+ version
daemon_messages
serialization
${Boost_CHRONO_LIBRARY}
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index aa9d3d64b..cefba39f7 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -45,6 +45,7 @@ using namespace epee;
#include "storages/http_abstract_invoke.h"
#include "crypto/hash.h"
#include "rpc/rpc_args.h"
+#include "rpc/rpc_handler.h"
#include "core_rpc_server_error_codes.h"
#include "p2p/net_node.h"
#include "version.h"
@@ -90,12 +91,10 @@ namespace cryptonote
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
, const bool restricted
- , const network_type nettype
, const std::string& port
)
{
m_restricted = restricted;
- m_nettype = nettype;
m_net_server.set_threads_prefix("RPC");
auto rpc_config = cryptonote::rpc_args::process(vm);
@@ -183,17 +182,19 @@ namespace cryptonote
res.target = m_core.get_blockchain_storage().get_difficulty_target();
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
res.tx_pool_size = m_core.get_pool_transactions_count();
- res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count();
- 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.rpc_connections_count = get_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.mainnet = m_nettype == MAINNET;
- res.testnet = m_nettype == TESTNET;
- res.stagenet = m_nettype == STAGENET;
- res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
+ res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count();
+ uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count();
+ res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count();
+ res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count);
+ res.rpc_connections_count = m_restricted ? 0 : get_connections_count();
+ res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count();
+ res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count();
+
+ cryptonote::network_type net_type = nettype();
+ res.mainnet = net_type == MAINNET;
+ res.testnet = net_type == TESTNET;
+ res.stagenet = net_type == STAGENET;
+ res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
@@ -201,14 +202,18 @@ namespace cryptonote
res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
- res.bootstrap_daemon_address = m_bootstrap_daemon_address;
- res.height_without_bootstrap = res.height;
+ res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address;
+ res.height_without_bootstrap = m_restricted ? 0 : res.height;
+ if (m_restricted)
+ res.was_bootstrap_ever_used = false;
+ else
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
}
- res.database_size = m_core.get_blockchain_storage().get_db().get_database_size();
- res.update_available = m_core.is_update_available();
+ res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size();
+ res.update_available = m_restricted ? false : m_core.is_update_available();
+ res.version = m_restricted ? "" : MONERO_VERSION;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -700,7 +705,7 @@ namespace cryptonote
res.status = "Failed";
res.reason = "";
if ((res.low_mixin = tvc.m_low_mixin))
- add_reason(res.reason, "ring size too small");
+ add_reason(res.reason, "bad ring size");
if ((res.double_spend = tvc.m_double_spend))
add_reason(res.reason, "double spend");
if ((res.invalid_input = tvc.m_invalid_input))
@@ -749,7 +754,7 @@ namespace cryptonote
PERF_TIMER(on_start_mining);
CHECK_CORE_READY();
cryptonote::address_parse_info info;
- if(!get_account_address_from_str(info, m_nettype, req.miner_address))
+ if(!get_account_address_from_str(info, nettype(), req.miner_address))
{
res.status = "Failed, wrong address";
LOG_PRINT_L0(res.status);
@@ -801,7 +806,14 @@ namespace cryptonote
bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res)
{
PERF_TIMER(on_stop_mining);
- if(!m_core.get_miner().stop())
+ cryptonote::miner &miner= m_core.get_miner();
+ if(!miner.is_mining())
+ {
+ res.status = "Mining never started";
+ LOG_PRINT_L0(res.status);
+ return true;
+ }
+ if(!miner.stop())
{
res.status = "Failed, mining not stopped";
LOG_PRINT_L0(res.status);
@@ -823,7 +835,7 @@ namespace cryptonote
res.speed = lMiner.get_speed();
res.threads_count = lMiner.get_threads_count();
const account_public_address& lMiningAdr = lMiner.get_mining_address();
- res.address = get_account_address_as_str(m_nettype, false, lMiningAdr);
+ res.address = get_account_address_as_str(nettype(), false, lMiningAdr);
}
res.status = CORE_RPC_STATUS_OK;
@@ -1010,7 +1022,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= h)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Too big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Requested block height: ") + std::to_string(h) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
}
res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h));
return true;
@@ -1018,7 +1030,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
// equivalent of strstr, but with arbitrary bytes (ie, NULs)
// This does not differentiate between "not found" and "found at offset 0"
- uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen)
+ size_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen)
{
const void* buf = start_buff;
const void* end=(const char*)buf+buflen;
@@ -1056,7 +1068,7 @@ namespace cryptonote
cryptonote::address_parse_info info;
- if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, m_nettype, req.wallet_address))
+ if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, nettype(), req.wallet_address))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS;
error_resp.message = "Failed to parse wallet address";
@@ -1069,7 +1081,7 @@ namespace cryptonote
return false;
}
- block b = AUTO_VAL_INIT(b);
+ block b;
cryptonote::blobdata blob_reserve;
blob_reserve.resize(req.reserve_size, 0);
if(!m_core.get_block_template(b, info.address, res.difficulty, res.height, res.expected_reward, blob_reserve))
@@ -1140,7 +1152,7 @@ namespace cryptonote
// Fixing of high orphan issue for most pools
// Thanks Boolberry!
- block b = AUTO_VAL_INIT(b);
+ block b;
if(!parse_and_validate_block_from_blob(blockblob, b))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
@@ -1208,7 +1220,7 @@ namespace cryptonote
error_resp.message = "Wrong block blob";
return false;
}
- block b = AUTO_VAL_INIT(b);
+ block b;
if(!parse_and_validate_block_from_blob(blockblob, b))
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
@@ -1457,7 +1469,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
@@ -1502,7 +1514,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
block_hash = m_core.get_block_id_by_height(req.height);
@@ -1575,32 +1587,39 @@ namespace cryptonote
res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
res.tx_pool_size = m_core.get_pool_transactions_count();
- res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count();
- 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.rpc_connections_count = get_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.mainnet = m_nettype == MAINNET;
- res.testnet = m_nettype == TESTNET;
- res.stagenet = m_nettype == STAGENET;
- res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
+ res.alt_blocks_count = m_restricted ? 0 : m_core.get_blockchain_storage().get_alternative_blocks_count();
+ uint64_t total_conn = m_restricted ? 0 : m_p2p.get_connections_count();
+ res.outgoing_connections_count = m_restricted ? 0 : m_p2p.get_outgoing_connections_count();
+ res.incoming_connections_count = m_restricted ? 0 : (total_conn - res.outgoing_connections_count);
+ res.rpc_connections_count = m_restricted ? 0 : get_connections_count();
+ res.white_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_white_peers_count();
+ res.grey_peerlist_size = m_restricted ? 0 : m_p2p.get_peerlist_manager().get_gray_peers_count();
+
+ cryptonote::network_type net_type = nettype();
+ res.mainnet = net_type == MAINNET;
+ res.testnet = net_type == TESTNET;
+ res.stagenet = net_type == STAGENET;
+ res.nettype = net_type == MAINNET ? "mainnet" : net_type == TESTNET ? "testnet" : net_type == STAGENET ? "stagenet" : "fakechain";
+
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK;
- res.start_time = (uint64_t)m_core.get_start_time();
+ res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
- res.bootstrap_daemon_address = m_bootstrap_daemon_address;
- res.height_without_bootstrap = res.height;
+ res.bootstrap_daemon_address = m_restricted ? "" : m_bootstrap_daemon_address;
+ res.height_without_bootstrap = m_restricted ? 0 : res.height;
+ if (m_restricted)
+ res.was_bootstrap_ever_used = false;
+ else
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
}
- res.database_size = m_core.get_blockchain_storage().get_db().get_database_size();
- res.update_available = m_core.is_update_available();
+ res.database_size = m_restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size();
+ res.update_available = m_restricted ? false : m_core.is_update_available();
+ res.version = m_restricted ? "" : MONERO_VERSION;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -2117,68 +2136,62 @@ namespace cryptonote
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts)
{
- static struct D
- {
- boost::mutex mutex;
- std::vector<uint64_t> cached_distribution;
- uint64_t cached_from, cached_to, cached_start_height, cached_base;
- bool cached;
- D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
- } d;
- boost::unique_lock<boost::mutex> lock(d.mutex);
-
- if (d.cached && amount == 0 && d.cached_from == req.from_height && d.cached_to == req_to_height)
- {
- res.distributions.push_back({amount, d.cached_start_height, req.binary, d.cached_distribution, d.cached_base});
- if (!req.cumulative)
- {
- auto &distribution = res.distributions.back().distribution;
- for (size_t n = distribution.size() - 1; n > 0; --n)
- distribution[n] -= distribution[n-1];
- distribution[0] -= d.cached_base;
- }
- continue;
- }
-
- std::vector<uint64_t> distribution;
- uint64_t start_height, base;
- if (!m_core.get_output_distribution(amount, req.from_height, req_to_height, start_height, distribution, base))
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ if (!data)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Failed to get rct distribution";
+ error_resp.message = "Failed to get output distribution";
return false;
}
- if (req_to_height > 0 && req_to_height >= req.from_height)
- {
- uint64_t offset = std::max(req.from_height, start_height);
- if (offset <= req_to_height && req_to_height - offset + 1 < distribution.size())
- distribution.resize(req_to_height - offset + 1);
- }
- if (amount == 0)
- {
- d.cached_from = req.from_height;
- d.cached_to = req_to_height;
- d.cached_distribution = distribution;
- d.cached_start_height = start_height;
- d.cached_base = base;
- d.cached = true;
- }
+ res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Failed to get output distribution";
+ return false;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res)
+ {
+ PERF_TIMER(on_get_output_distribution_bin);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r))
+ return r;
+
+ res.status = "Failed";
- if (!req.cumulative)
+ if (!req.binary)
+ {
+ res.status = "Binary only call";
+ return false;
+ }
+ try
+ {
+ // 0 is placeholder for the whole chain
+ const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
+ for (uint64_t amount: req.amounts)
+ {
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ if (!data)
{
- for (size_t n = distribution.size() - 1; n > 0; --n)
- distribution[n] -= distribution[n-1];
- distribution[0] -= base;
+ res.status = "Failed to get output distribution";
+ return false;
}
- res.distributions.push_back({amount, start_height, req.binary, std::move(distribution), base});
+ res.distributions.push_back({std::move(*data), amount, "", req.binary, req.compress});
}
}
catch (const std::exception &e)
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Failed to get output distribution";
+ res.status = "Failed to get output distribution";
return false;
}
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 3ba882b23..6a616e2e0 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -70,10 +70,9 @@ namespace cryptonote
bool init(
const boost::program_options::variables_map& vm,
const bool restricted,
- const network_type nettype,
const std::string& port
);
- network_type nettype() const { return m_nettype; }
+ network_type nettype() const { return m_core.get_nettype(); }
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
@@ -117,6 +116,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
+ MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC("get_block_count", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
@@ -187,6 +187,7 @@ namespace cryptonote
bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res);
bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res);
+ bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res);
//json_rpc
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index b229841d6..d54bfbf8d 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -33,6 +33,41 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
#include "crypto/hash.h"
+#include "rpc/rpc_handler.h"
+#include "common/varint.h"
+#include "common/perf_timer.h"
+
+namespace
+{
+ template<typename T>
+ std::string compress_integer_array(const std::vector<T> &v)
+ {
+ std::string s;
+ s.resize(v.size() * (sizeof(T) * 8 / 7 + 1));
+ char *ptr = (char*)s.data();
+ for (const T &t: v)
+ tools::write_varint(ptr, t);
+ s.resize(ptr - s.data());
+ return s;
+ }
+
+ template<typename T>
+ std::vector<T> decompress_integer_array(const std::string &s)
+ {
+ std::vector<T> v;
+ v.reserve(s.size());
+ int read = 0;
+ const std::string::const_iterator end = s.end();
+ for (std::string::const_iterator i = s.begin(); i != end; std::advance(i, read))
+ {
+ T t;
+ read = tools::read_varint(std::string::const_iterator(i), s.end(), t);
+ CHECK_AND_ASSERT_THROW_MES(read > 0 && read <= 256, "Error decompressing data");
+ v.push_back(t);
+ }
+ return v;
+ }
+}
namespace cryptonote
{
@@ -49,7 +84,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
-#define CORE_RPC_VERSION_MINOR 1
+#define CORE_RPC_VERSION_MINOR 2
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -696,9 +731,11 @@ namespace cryptonote
struct request
{
std::vector<get_outputs_out> outputs;
+ bool get_txid;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outputs)
+ KV_SERIALIZE_OPT(get_txid, true)
END_KV_SERIALIZE_MAP()
};
@@ -891,6 +928,7 @@ namespace cryptonote
bool was_bootstrap_ever_used;
uint64_t database_size;
bool update_available;
+ std::string version;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -925,6 +963,7 @@ namespace cryptonote
KV_SERIALIZE(was_bootstrap_ever_used)
KV_SERIALIZE(database_size)
KV_SERIALIZE(update_available)
+ KV_SERIALIZE(version)
END_KV_SERIALIZE_MAP()
};
};
@@ -2221,6 +2260,7 @@ namespace cryptonote
uint64_t to_height;
bool cumulative;
bool binary;
+ bool compress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
@@ -2228,26 +2268,49 @@ namespace cryptonote
KV_SERIALIZE_OPT(to_height, (uint64_t)0)
KV_SERIALIZE_OPT(cumulative, false)
KV_SERIALIZE_OPT(binary, true)
+ KV_SERIALIZE_OPT(compress, false)
END_KV_SERIALIZE_MAP()
};
struct distribution
{
+ rpc::output_distribution_data data;
uint64_t amount;
- uint64_t start_height;
+ std::string compressed_data;
bool binary;
- std::vector<uint64_t> distribution;
- uint64_t base;
+ bool compress;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
- KV_SERIALIZE(start_height)
+ KV_SERIALIZE_N(data.start_height, "start_height")
KV_SERIALIZE(binary)
+ KV_SERIALIZE(compress)
if (this_ref.binary)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(distribution)
+ {
+ if (is_store)
+ {
+ if (this_ref.compress)
+ {
+ const_cast<std::string&>(this_ref.compressed_data) = compress_integer_array(this_ref.data.distribution);
+ KV_SERIALIZE(compressed_data)
+ }
+ else
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
+ }
+ else
+ {
+ if (this_ref.compress)
+ {
+ KV_SERIALIZE(compressed_data)
+ const_cast<std::vector<uint64_t>&>(this_ref.data.distribution) = decompress_integer_array<uint64_t>(this_ref.compressed_data);
+ }
+ else
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(data.distribution, "distribution")
+ }
+ }
else
- KV_SERIALIZE(distribution)
- KV_SERIALIZE(base)
+ KV_SERIALIZE_N(data.distribution, "distribution")
+ KV_SERIALIZE_N(data.base, "base")
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 9d3b09b68..e2885dbb5 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -34,6 +34,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/blobdatatype.h"
#include "ringct/rctSigs.h"
+#include "version.h"
namespace cryptonote
{
@@ -437,6 +438,7 @@ namespace rpc
res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.start_time = (uint64_t)m_core.get_start_time();
+ res.info.version = MONERO_VERSION;
res.status = Message::STATUS_OK;
res.error_details = "";
@@ -724,12 +726,53 @@ namespace rpc
res.status = Message::STATUS_OK;
}
- void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
+ void DaemonHandler::handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res)
{
- res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
+ res.hard_fork_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
+ res.estimated_base_fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
+
+ if (res.hard_fork_version < HF_VERSION_PER_BYTE_FEE)
+ {
+ res.size_scale = 1024; // per KiB fee
+ res.fee_mask = 1;
+ }
+ else
+ {
+ res.size_scale = 1; // per byte fee
+ res.fee_mask = Blockchain::get_fee_quantization_mask();
+ }
res.status = Message::STATUS_OK;
}
+ void DaemonHandler::handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res)
+ {
+ try
+ {
+ res.distributions.reserve(req.amounts.size());
+
+ const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
+ for (std::uint64_t amount : req.amounts)
+ {
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ if (!data)
+ {
+ res.distributions.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Failed to get output distribution";
+ return;
+ }
+ res.distributions.push_back(output_distribution{std::move(*data), amount, req.cumulative});
+ }
+ res.status = Message::STATUS_OK;
+ }
+ catch (const std::exception& e)
+ {
+ res.distributions.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ }
+ }
+
bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
{
block b;
@@ -804,7 +847,8 @@ namespace rpc
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);
+ REQ_RESP_TYPES_MACRO(request_type, GetFeeEstimate, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputDistribution, req_json, resp_message, handle);
// if none of the request types matches
if (resp_message == NULL)
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
index 5f9687511..2c8ac3867 100644
--- a/src/rpc/daemon_handler.h
+++ b/src/rpc/daemon_handler.h
@@ -126,7 +126,9 @@ class DaemonHandler : public RpcHandler
void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res);
- void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res);
+ void handle(const GetFeeEstimate::Request& req, GetFeeEstimate::Response& res);
+
+ void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res);
std::string handle(const std::string& request);
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
index 56f6f6a8c..7c7442014 100644
--- a/src/rpc/daemon_messages.cpp
+++ b/src/rpc/daemon_messages.cpp
@@ -59,7 +59,8 @@ 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";
+const char* const GetFeeEstimate::name = "get_dynamic_fee_estimate";
+const char* const GetOutputDistribution::name = "get_output_distribution";
@@ -176,8 +177,6 @@ rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) cons
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes);
return val;
@@ -192,8 +191,6 @@ rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) con
{
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);
@@ -211,8 +208,6 @@ 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;
@@ -227,8 +222,6 @@ rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) cons
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status);
return val;
@@ -244,8 +237,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document&
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash);
return val;
@@ -260,8 +251,6 @@ rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document&
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
return val;
@@ -276,8 +265,6 @@ 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);
@@ -294,8 +281,6 @@ 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;
@@ -311,8 +296,6 @@ rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address);
INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining);
@@ -371,8 +354,6 @@ rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, active, active);
INSERT_INTO_JSON_OBJECT(val, doc, speed, speed);
INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
@@ -405,8 +386,6 @@ rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, info, info);
return val;
@@ -422,8 +401,6 @@ rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
return val;
}
@@ -435,8 +412,6 @@ rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
return val;
}
@@ -449,8 +424,6 @@ 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;
@@ -465,8 +438,6 @@ 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;
@@ -482,8 +453,6 @@ rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) c
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
return val;
}
@@ -495,8 +464,6 @@ rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc)
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, header, header);
return val;
@@ -512,8 +479,6 @@ rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc)
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
return val;
@@ -528,8 +493,6 @@ rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, header, header);
return val;
@@ -545,8 +508,6 @@ rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& do
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, height, height);
return val;
@@ -561,8 +522,6 @@ rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& d
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, header, header);
return val;
@@ -578,8 +537,6 @@ rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& d
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, heights, heights);
return val;
@@ -594,8 +551,6 @@ rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document&
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, headers, headers);
return val;
@@ -611,8 +566,6 @@ rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
return val;
}
@@ -624,8 +577,6 @@ 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);
@@ -678,8 +629,6 @@ rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc)
{
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);
@@ -697,8 +646,6 @@ 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;
@@ -713,8 +660,6 @@ 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;
@@ -730,8 +675,6 @@ rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) c
{
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);
@@ -754,8 +697,6 @@ rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc)
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
-
INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram);
return val;
@@ -771,8 +712,6 @@ 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;
@@ -787,8 +726,6 @@ 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;
@@ -813,8 +750,6 @@ 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;
@@ -825,38 +760,75 @@ void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
GET_FROM_JSON_OBJECT(val, version, version);
}
-rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const
+rapidjson::Value GetFeeEstimate::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)
+void GetFeeEstimate::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
+rapidjson::Value GetFeeEstimate::Response::toJson(rapidjson::Document& doc) const
{
auto val = Message::toJson(doc);
- auto& al = doc.GetAllocator();
+ INSERT_INTO_JSON_OBJECT(val, doc, estimated_base_fee, estimated_base_fee);
+ INSERT_INTO_JSON_OBJECT(val, doc, fee_mask, fee_mask);
+ INSERT_INTO_JSON_OBJECT(val, doc, size_scale, size_scale);
+ INSERT_INTO_JSON_OBJECT(val, doc, hard_fork_version, hard_fork_version);
+
+ return val;
+}
+
+void GetFeeEstimate::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, estimated_base_fee, estimated_base_fee);
+ GET_FROM_JSON_OBJECT(val, fee_mask, fee_mask);
+ GET_FROM_JSON_OBJECT(val, size_scale, size_scale);
+ GET_FROM_JSON_OBJECT(val, hard_fork_version, hard_fork_version);
+}
+
+rapidjson::Value GetOutputDistribution::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
- INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb);
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, from_height, from_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, to_height, to_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, cumulative, cumulative);
return val;
}
-void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val)
+void GetOutputDistribution::Request::fromJson(rapidjson::Value& val)
{
- GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb);
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, from_height, from_height);
+ GET_FROM_JSON_OBJECT(val, to_height, to_height);
+ GET_FROM_JSON_OBJECT(val, cumulative, cumulative);
}
+rapidjson::Value GetOutputDistribution::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, status, status);
+ INSERT_INTO_JSON_OBJECT(val, doc, distributions, distributions);
+
+ return val;
+}
+
+void GetOutputDistribution::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, status, status);
+ GET_FROM_JSON_OBJECT(val, distributions, distributions);
+}
} // namespace rpc
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index 8488bab0b..d2014247c 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -28,6 +28,9 @@
#pragma once
+#include <unordered_map>
+#include <vector>
+
#include "message.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "rpc/message_data_structs.h"
@@ -62,8 +65,6 @@ class classname \
#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.
@@ -118,7 +119,8 @@ BEGIN_RPC_MESSAGE_CLASS(GetTransactions);
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);
+ using txes_map = std::unordered_map<crypto::hash, transaction_info>;
+ RPC_MESSAGE_MEMBER(txes_map, txs);
RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, missed_hashes);
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
@@ -407,12 +409,27 @@ BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion);
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
-BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate);
+BEGIN_RPC_MESSAGE_CLASS(GetFeeEstimate);
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);
+ RPC_MESSAGE_MEMBER(uint64_t, estimated_base_fee);
+ RPC_MESSAGE_MEMBER(uint64_t, fee_mask);
+ RPC_MESSAGE_MEMBER(uint32_t, size_scale);
+ RPC_MESSAGE_MEMBER(uint8_t, hard_fork_version);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputDistribution);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts);
+ RPC_MESSAGE_MEMBER(uint64_t, from_height);
+ RPC_MESSAGE_MEMBER(uint64_t, to_height);
+ RPC_MESSAGE_MEMBER(bool, cumulative);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<output_distribution>, distributions);
END_RPC_MESSAGE_RESPONSE;
END_RPC_MESSAGE_CLASS;
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index cf15ade1c..e09b6749e 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -31,6 +31,7 @@
#include "crypto/hash.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "ringct/rctSigs.h"
+#include "rpc/rpc_handler.h"
#include <unordered_map>
#include <vector>
@@ -190,8 +191,15 @@ namespace rpc
uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t start_time;
+ std::string version;
};
+ struct output_distribution
+ {
+ output_distribution_data data;
+ uint64_t amount;
+ bool cumulative;
+ };
} // namespace rpc
} // namespace cryptonote
diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp
new file mode 100644
index 000000000..63664bf8b
--- /dev/null
+++ b/src/rpc/rpc_handler.cpp
@@ -0,0 +1,69 @@
+
+#include <algorithm>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include "cryptonote_core/cryptonote_core.h"
+
+namespace cryptonote
+{
+namespace rpc
+{
+ namespace
+ {
+ output_distribution_data
+ process_distribution(bool cumulative, std::uint64_t start_height, std::vector<std::uint64_t> distribution, std::uint64_t base)
+ {
+ if (!cumulative && !distribution.empty())
+ {
+ for (std::size_t n = distribution.size() - 1; 0 < n; --n)
+ distribution[n] -= distribution[n - 1];
+ distribution[0] -= base;
+ }
+
+ return {std::move(distribution), start_height, base};
+ }
+ }
+
+ boost::optional<output_distribution_data>
+ RpcHandler::get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative)
+ {
+ static struct D
+ {
+ boost::mutex mutex;
+ std::vector<std::uint64_t> cached_distribution;
+ std::uint64_t cached_from, cached_to, cached_start_height, cached_base;
+ bool cached;
+ D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
+ } d;
+ const boost::unique_lock<boost::mutex> lock(d.mutex);
+
+ if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to == to_height)
+ return process_distribution(cumulative, d.cached_start_height, d.cached_distribution, d.cached_base);
+
+ std::vector<std::uint64_t> distribution;
+ std::uint64_t start_height, base;
+ if (!f(amount, from_height, to_height, start_height, distribution, base))
+ return boost::none;
+
+ if (to_height > 0 && to_height >= from_height)
+ {
+ const std::uint64_t offset = std::max(from_height, start_height);
+ if (offset <= to_height && to_height - offset + 1 < distribution.size())
+ distribution.resize(to_height - offset + 1);
+ }
+
+ if (amount == 0)
+ {
+ d.cached_from = from_height;
+ d.cached_to = to_height;
+ d.cached_distribution = distribution;
+ d.cached_start_height = start_height;
+ d.cached_base = base;
+ d.cached = true;
+ }
+
+ return process_distribution(cumulative, start_height, std::move(distribution), base);
+ }
+} // rpc
+} // cryptonote
diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h
index 64bade5a8..e0d520408 100644
--- a/src/rpc/rpc_handler.h
+++ b/src/rpc/rpc_handler.h
@@ -28,24 +28,35 @@
#pragma once
+#include <boost/optional/optional.hpp>
+#include <cstdint>
#include <string>
+#include <vector>
namespace cryptonote
{
+class core;
namespace rpc
{
+struct output_distribution_data
+{
+ std::vector<std::uint64_t> distribution;
+ std::uint64_t start_height;
+ std::uint64_t base;
+};
class RpcHandler
{
public:
+ RpcHandler() { }
+ virtual ~RpcHandler() { }
virtual std::string handle(const std::string& request) = 0;
- RpcHandler() { }
-
- virtual ~RpcHandler() { }
+ static boost::optional<output_distribution_data>
+ get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative);
};
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
index edd3e6669..a2ff76668 100644
--- a/src/rpc/zmq_server.cpp
+++ b/src/rpc/zmq_server.cpp
@@ -27,7 +27,6 @@
// 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
{