GET_OUTPUT_HISTOGRAM

This commit is contained in:
Sean Darcy 2021-11-15 10:38:37 +11:00
parent 4013cb5115
commit 8b3d2371bb
7 changed files with 85 additions and 86 deletions

View File

@ -1198,24 +1198,21 @@ bool rpc_command_executor::flush_txpool(std::string txid)
bool rpc_command_executor::output_histogram(const std::vector<uint64_t> &amounts, uint64_t min_count, uint64_t max_count)
{
GET_OUTPUT_HISTOGRAM::request req{};
GET_OUTPUT_HISTOGRAM::response res{};
req.amounts = amounts;
req.min_count = min_count;
req.max_count = max_count;
req.unlocked = false;
req.recent_cutoff = 0;
if (!invoke<GET_OUTPUT_HISTOGRAM>(std::move(req), res, "Failed to retrieve output histogram"))
return false;
std::sort(res.histogram.begin(), res.histogram.end(),
auto maybe_histogram= try_running([this, &amounts, min_count, max_count]
{ return invoke<GET_OUTPUT_HISTOGRAM>(
json{{"amounts", amounts},
{"min_count", min_count},
{"max_count", max_count},
{"unlocked", false},
{"recent_cutoff", 0}});
}, "Failed to retrieve output histogram");
if (!maybe_histogram)
return false;
std::vector<GET_OUTPUT_HISTOGRAM::entry> histogram = (*maybe_histogram)["histogram"];
std::sort(histogram.begin(), histogram.end(),
[](const auto& e1, const auto& e2)->bool { return e1.total_instances < e2.total_instances; });
for (const auto &e: res.histogram)
{
for (const auto &e: histogram)
tools::msg_writer() << e.total_instances << " " << cryptonote::print_money(e.amount);
}
return true;
}

View File

@ -1791,39 +1791,45 @@ namespace cryptonote::rpc {
: STATUS_OK;
}
//------------------------------------------------------------------------------------------------------------------------------
GET_OUTPUT_HISTOGRAM::response core_rpc_server::invoke(GET_OUTPUT_HISTOGRAM::request&& req, rpc_context context)
void core_rpc_server::invoke(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_context context)
{
GET_OUTPUT_HISTOGRAM::response res{};
PERF_TIMER(on_get_output_histogram);
if (!context.admin && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION)
if (!context.admin && get_output_histogram.request.recent_cutoff > 0 && get_output_histogram.request.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION)
{
res.status = "Recent cutoff is too old";
return res;
get_output_histogram.response["status"] = "Recent cutoff is too old";
return;
}
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, req.min_count);
histogram = m_core.get_blockchain_storage().get_output_histogram(
get_output_histogram.request.amounts,
get_output_histogram.request.unlocked,
get_output_histogram.request.recent_cutoff,
get_output_histogram.request.min_count
);
}
catch (const std::exception &e)
{
res.status = "Failed to get output histogram";
return res;
get_output_histogram.response["status"] = "Failed to get output histogram";
return;
}
res.histogram.clear();
res.histogram.reserve(histogram.size());
for (const auto &i: histogram)
std::vector<GET_OUTPUT_HISTOGRAM::entry> response_histogram;
response_histogram.reserve(histogram.size());
for (const auto &[amount, histogram_tuple]: histogram)
{
if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0))
res.histogram.push_back(GET_OUTPUT_HISTOGRAM::entry(i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)));
auto& [total_instances, unlocked_instances, recent_instances] = histogram_tuple;
if (total_instances >= get_output_histogram.request.min_count && (total_instances <= get_output_histogram.request.max_count || get_output_histogram.request.max_count == 0))
response_histogram.push_back(GET_OUTPUT_HISTOGRAM::entry{amount, total_instances, unlocked_instances, recent_instances});
}
res.status = STATUS_OK;
return res;
get_output_histogram.response["histogram"] = response_histogram;
get_output_histogram.response["status"] = STATUS_OK;
return;
}
//------------------------------------------------------------------------------------------------------------------------------
void core_rpc_server::invoke(GET_VERSION& version, rpc_context context)

View File

@ -253,6 +253,7 @@ namespace cryptonote::rpc {
void invoke(GET_SERVICE_NODE_REGISTRATION_CMD_RAW& get_service_node_registration_cmd_raw, rpc_context context);
void invoke(GET_QUORUM_STATE& get_quorum_state, rpc_context context);
void invoke(GET_ALTERNATE_CHAINS& get_alternate_chains, rpc_context context);
void invoke(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_context context);
// Deprecated Monero NIH binary endpoints:
GET_ALT_BLOCKS_HASHES_BIN::response invoke(GET_ALT_BLOCKS_HASHES_BIN::request&& req, rpc_context context);
@ -267,7 +268,6 @@ namespace cryptonote::rpc {
GET_OUTPUT_DISTRIBUTION::response invoke(GET_OUTPUT_DISTRIBUTION::request&& req, rpc_context context, bool binary = false);
// FIXME: unconverted JSON RPC endpoints:
GET_OUTPUT_HISTOGRAM::response invoke(GET_OUTPUT_HISTOGRAM::request&& req, rpc_context context);
GET_SERVICE_NODE_REGISTRATION_CMD::response invoke(GET_SERVICE_NODE_REGISTRATION_CMD::request&& req, rpc_context context);
ONS_NAMES_TO_OWNERS::response invoke(ONS_NAMES_TO_OWNERS::request&& req, rpc_context context);
ONS_OWNERS_TO_NAMES::response invoke(ONS_OWNERS_TO_NAMES::request&& req, rpc_context context);

View File

@ -509,4 +509,12 @@ namespace cryptonote::rpc {
get_values(in, "height", get_block.request.height);
get_values(in, "fill_pow_hash", get_block.request.fill_pow_hash);
}
void parse_request(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_input in) {
get_values(in, "amounts", get_output_histogram.request.amounts);
get_values(in, "min_count", get_output_histogram.request.min_count);
get_values(in, "max_count", get_output_histogram.request.max_count);
get_values(in, "unlocked", get_output_histogram.request.unlocked);
get_values(in, "recent_cutoff", get_output_histogram.request.recent_cutoff);
}
}

View File

@ -44,4 +44,5 @@ namespace cryptonote::rpc {
void parse_request(GET_BLOCK_HEADERS_RANGE& get_block_headers_range, rpc_input in);
void parse_request(GET_BLOCK_HEADER_BY_HEIGHT& get_block_header_by_height, rpc_input in);
void parse_request(GET_BLOCK& get_block, rpc_input in);
void parse_request(GET_OUTPUT_HISTOGRAM& get_output_histogram, rpc_input in);
}

View File

@ -98,6 +98,23 @@ void from_json(const nlohmann::json& j, GET_ALTERNATE_CHAINS::chain_info& c)
j.at("main_chain_parent_block").get_to(c.main_chain_parent_block);
}
void to_json(nlohmann::json& j, const GET_OUTPUT_HISTOGRAM::entry& e)
{
j = nlohmann::json{
{"amount", e.amount},
{"total_instances", e.total_instances},
{"unlocked_instances", e.unlocked_instances},
{"recent_instances", e.recent_instances},
};
}
void from_json(const nlohmann::json& j, GET_OUTPUT_HISTOGRAM::entry& e)
{
j.at("amount").get_to(e.amount);
j.at("total_instances").get_to(e.total_instances);
j.at("unlocked_instances").get_to(e.unlocked_instances);
j.at("recent_instances").get_to(e.recent_instances);
};
KV_SERIALIZE_MAP_CODE_BEGIN(STATUS)
KV_SERIALIZE(status)
KV_SERIALIZE_MAP_CODE_END()
@ -132,29 +149,6 @@ KV_SERIALIZE_MAP_CODE_BEGIN(block_header_response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::request)
KV_SERIALIZE(amounts);
KV_SERIALIZE(min_count);
KV_SERIALIZE(max_count);
KV_SERIALIZE(unlocked);
KV_SERIALIZE(recent_cutoff);
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::entry)
KV_SERIALIZE(amount);
KV_SERIALIZE(total_instances);
KV_SERIALIZE(unlocked_instances);
KV_SERIALIZE(recent_instances);
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::response)
KV_SERIALIZE(status)
KV_SERIALIZE(histogram)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_DISTRIBUTION::request)
KV_SERIALIZE(amounts)
KV_SERIALIZE_OPT(from_height, (uint64_t)0)

View File

@ -399,7 +399,6 @@ namespace cryptonote::rpc {
/// Outputs
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// untrusted ('true'), or when the daemon is fully synced ('false').
/// - \p spent_status array of status codes returned in the same order as the `key_images` input.
/// Each value is one of:
/// - \p 0 the key image is unspent
@ -434,7 +433,6 @@ namespace cryptonote::rpc {
/// Output values available from a public RPC endpoint:
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// untrusted ('true'), or when the daemon is fully synced ('false').
/// - \p outs List of outkey information; if `as_tuple` is not set then these are dicts containing
/// keys:
/// - \p key The public key of the output.
@ -473,7 +471,6 @@ namespace cryptonote::rpc {
/// Output values available from a public RPC endpoint:
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// untrusted ('true'), or when the daemon is fully synced ('false').
/// - \p reason String containing additional information on why a transaction failed.
/// - \p blink_status Set to the result of submitting this transaction to the Blink quorum. 1
/// means the quorum rejected the transaction; 2 means the quorum accepted it; 3 means there was
@ -627,7 +624,6 @@ namespace cryptonote::rpc {
/// - \p block_size_median Median block size of latest 100 blocks.
/// - \p ons_counts ONS registration counts, as a three-element list: [session, wallet, lokinet]
/// - \p offline Indicates that the node is offline, if true. Omitted for online nodes.
/// not trusted (`true`). Omitted for non-bootstrap responses.
/// - \p database_size Current size of Blockchain data. Over public RPC this is rounded up to the
/// next-largest GB value.
/// - \p version Current version of this daemon, as a string. For a public node this will just be
@ -958,7 +954,6 @@ namespace cryptonote::rpc {
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// - \p tx_hashes List of transaction hashes,
/// trusted (`true`), or when the daemon is fully synced (`false`).
struct GET_TRANSACTION_POOL_HASHES : PUBLIC, LEGACY, NO_ARGS
{
static constexpr auto names() { return NAMES("get_transaction_pool_hashes"); }
@ -1001,7 +996,6 @@ namespace cryptonote::rpc {
/// `histo_98pc`.
/// - \p histo_98pc See `histo` for details.
/// - \p histo_max See `histo` for details.
/// trusted (`true`), or when the daemon is fully synced (`false`).
struct GET_TRANSACTION_POOL_STATS : PUBLIC, LEGACY
{
static constexpr auto names() { return NAMES("get_transaction_pool_stats"); }
@ -1180,7 +1174,6 @@ namespace cryptonote::rpc {
/// Output values available from a public RPC endpoint:
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// untrusted ('true'), or when the daemon is fully synced ('false').
/// - \p version The major block version for the fork.
/// - \p enabled Indicates whether the hard fork is enforced on the blockchain (that is, whether
/// the blockchain height is at or above the requested hardfork).
@ -1285,23 +1278,37 @@ namespace cryptonote::rpc {
} request;
};
OXEN_RPC_DOC_INTROSPECT
// Get a histogram of output amounts. For all amounts (possibly filtered by parameters),
// gives the number of outputs on the chain for that amount. RingCT outputs counts as 0 amount.
/// Get a histogram of output amounts. For all amounts (possibly filtered by parameters),
/// gives the number of outputs on the chain for that amount. RingCT outputs counts as 0 amount.
///
/// Inputs:
///
/// - \p amounts list of amounts in Atomic Units.
/// - \p min_count The minimum amounts you are requesting.
/// - \p max_count The maximum amounts you are requesting.
/// - \p unlocked Look for locked only.
/// - \p recent_cutoff
///
/// Output values available from a public RPC endpoint:
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// - \p histogram List of histogram entries. Each element is structured as follows:
/// - \p uint64_t amount Output amount in atomic units.
/// - \p uint64_t total_instances
/// - \p uint64_t unlocked_instances
/// - \p uint64_t recent_instances
struct GET_OUTPUT_HISTOGRAM : PUBLIC
{
static constexpr auto names() { return NAMES("get_output_histogram"); }
struct request
struct request_parameters
{
std::vector<uint64_t> amounts; // list of amounts in Atomic Units.
uint64_t min_count; // The minimum amounts you are requesting.
uint64_t max_count; // The maximum amounts you are requesting.
bool unlocked; // Look for locked only.
uint64_t recent_cutoff;
KV_MAP_SERIALIZABLE
};
} request;
struct entry
{
@ -1309,22 +1316,10 @@ namespace cryptonote::rpc {
uint64_t total_instances;
uint64_t unlocked_instances;
uint64_t recent_instances;
KV_MAP_SERIALIZABLE
entry(uint64_t amount, uint64_t total_instances, uint64_t unlocked_instances, uint64_t recent_instances):
amount(amount), total_instances(total_instances), unlocked_instances(unlocked_instances), recent_instances(recent_instances) {}
entry() = default;
};
struct response
{
std::string status; // General RPC error code. "OK" means everything looks good.
std::vector<entry> histogram; // List of histogram entries:
KV_MAP_SERIALIZABLE
};
};
void to_json(nlohmann::json& j, const GET_OUTPUT_HISTOGRAM::entry& c);
void from_json(const nlohmann::json& j, GET_OUTPUT_HISTOGRAM::entry& c);
/// Get current RPC protocol version.
///
@ -1334,7 +1329,6 @@ namespace cryptonote::rpc {
///
/// - \p status General RPC status string. `"OK"` means everything looks good.
/// - \p version RPC current version.
/// trusted (`true`), or when the daemon is fully synced
struct GET_VERSION : PUBLIC, NO_ARGS
{
static constexpr auto names() { return NAMES("get_version"); }
@ -1387,7 +1381,6 @@ namespace cryptonote::rpc {
/// - \p blink_fee_fixed Fixed blink fee in addition to the per-output and per-byte amounts. The
/// portion of the overall blink fee above the overall base fee is burned.
/// - \p quantization_mask
/// trusted (`true`), or when the daemon is fully synced (`false`).
struct GET_BASE_FEE_ESTIMATE : PUBLIC
{
static constexpr auto names() { return NAMES("get_fee_estimate"); }