Skip to content

Commit

Permalink
Merge pull request oxen-io#1310 from jagerman/rpc-last-quorum-of-each
Browse files Browse the repository at this point in the history
RPC: get latest quorum improvements/fixes
  • Loading branch information
Doy-lee authored Oct 11, 2020
2 parents 3d063cd + 920966c commit 2c48efe
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 21 deletions.
6 changes: 6 additions & 0 deletions src/cryptonote_core/service_node_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,12 @@ namespace service_nodes
return get_pulse_entropy_for_next_block(db, top_block, pulse_round);
}

std::vector<crypto::hash> get_pulse_entropy_for_next_block(cryptonote::BlockchainDB const &db,
uint8_t pulse_round)
{
return get_pulse_entropy_for_next_block(db, db.get_top_block(), pulse_round);
}

service_nodes::quorum generate_pulse_quorum(cryptonote::network_type nettype,
crypto::public_key const &block_leader,
uint8_t hf_version,
Expand Down
3 changes: 3 additions & 0 deletions src/cryptonote_core/service_node_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,9 @@ namespace service_nodes
// The pulse entropy is generated for the next block after the top_block passed in.
std::vector<crypto::hash> get_pulse_entropy_for_next_block(cryptonote::BlockchainDB const &db, cryptonote::block const &top_block, uint8_t pulse_round);
std::vector<crypto::hash> get_pulse_entropy_for_next_block(cryptonote::BlockchainDB const &db, crypto::hash const &top_hash, uint8_t pulse_round);
// Same as above, but uses the current blockchain top block and defaults to round 0 if not
// specified.
std::vector<crypto::hash> get_pulse_entropy_for_next_block(cryptonote::BlockchainDB const &db, uint8_t pulse_round = 0);

payout service_node_info_to_payout(crypto::public_key const &key, service_node_info const &info);

Expand Down
74 changes: 58 additions & 16 deletions src/rpc/core_rpc_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <type_traits>
#include <variant>
#include <lokimq/base64.h>
#include "crypto/crypto.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/loki_name_system.h"
#include "cryptonote_core/pulse.h"
Expand Down Expand Up @@ -2644,12 +2645,33 @@ namespace cryptonote { namespace rpc {
throw rpc_error{ERROR_WRONG_PARAM,
"Quorum type specifies an invalid value: " + std::to_string(req.quorum_type)};

auto requested_type = [&req](service_nodes::quorum_type type) {
return req.quorum_type == GET_QUORUM_STATE::ALL_QUORUMS_SENTINEL_VALUE ||
req.quorum_type == static_cast<uint8_t>(type);
};

bool latest = false;
uint64_t latest_ob = 0, latest_cp = 0, latest_bl = 0;
uint64_t start = req.start_height, end = req.end_height;
uint64_t curr_height = m_core.get_blockchain_storage().get_current_blockchain_height();
if (start == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE &&
end == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE)
{
start = m_core.get_blockchain_storage().get_current_blockchain_height() - 1;
end = start + 1;
latest = true;
// Our start block for the latest quorum of each type depends on the type being requested:
// obligations: top block
// checkpoint: last block with height divisible by CHECKPOINT_INTERVAL (=4)
// blink: last block with height divisible by BLINK_QUORUM_INTERVAL (=5)
// pulse: current height (i.e. top block height + 1)
uint64_t top_height = curr_height - 1;
latest_ob = top_height;
latest_cp = std::min(start, top_height - top_height % service_nodes::CHECKPOINT_INTERVAL);
latest_bl = std::min(start, top_height - top_height % service_nodes::BLINK_QUORUM_INTERVAL);
if (requested_type(service_nodes::quorum_type::checkpointing))
start = std::min(start, latest_cp);
if (requested_type(service_nodes::quorum_type::blink))
start = std::min(start, latest_bl);
end = curr_height;
}
else if (start == GET_QUORUM_STATE::HEIGHT_SENTINEL_VALUE)
{
Expand All @@ -2663,16 +2685,14 @@ namespace cryptonote { namespace rpc {
else
{
if (end > start) end++;
else
{
if (end != 0)
end--;
}
else if (end != 0) end--;
}

uint64_t curr_height = m_core.get_blockchain_storage().get_current_blockchain_height();
start = std::min(curr_height, start);
end = std::min(curr_height, end);
// We can also provide the pulse quorum for the current block being produced, so if asked for
// that make a note.
bool add_curr_pulse = (latest || end > curr_height) && requested_type(service_nodes::quorum_type::pulse);
end = std::min(curr_height, end);

uint64_t count = (start > end) ? start - end : end - start;
if (!context.admin && count > GET_QUORUM_STATE::MAX_COUNT)
Expand Down Expand Up @@ -2700,18 +2720,21 @@ namespace cryptonote { namespace rpc {
for (int quorum_int = (int)start_quorum_iterator; quorum_int <= (int)end_quorum_iterator; quorum_int++)
{
auto type = static_cast<service_nodes::quorum_type>(quorum_int);
if (latest)
{ // Latest quorum requested, so skip if this is isn't the latest height for *this* quorum type
if (type == service_nodes::quorum_type::obligations && height != latest_ob) continue;
if (type == service_nodes::quorum_type::checkpointing && height != latest_cp) continue;
if (type == service_nodes::quorum_type::blink && height != latest_bl) continue;
if (type == service_nodes::quorum_type::pulse) continue;
}
if (std::shared_ptr<const service_nodes::quorum> quorum = m_core.get_quorum(type, height, true /*include_old*/))
{
GET_QUORUM_STATE::quorum_for_height entry = {};
auto& entry = res.quorums.emplace_back();
entry.height = height;
entry.quorum_type = static_cast<uint8_t>(quorum_int);
entry.quorum.validators = hexify(quorum->validators);
entry.quorum.workers = hexify(quorum->workers);

entry.quorum.validators.reserve(quorum->validators.size());
entry.quorum.workers.reserve(quorum->workers.size());
for (crypto::public_key const &key : quorum->validators) entry.quorum.validators.push_back(epee::string_tools::pod_to_hex(key));
for (crypto::public_key const &key : quorum->workers) entry.quorum.workers.push_back(epee::string_tools::pod_to_hex(key));

res.quorums.push_back(entry);
at_least_one_succeeded = true;
}
}
Expand All @@ -2721,6 +2744,25 @@ namespace cryptonote { namespace rpc {
else height--;
}

if (uint8_t hf_version; add_curr_pulse
&& (hf_version = m_core.get_hard_fork_version(curr_height)) >= network_version_16_pulse)
{
auto entropy = service_nodes::get_pulse_entropy_for_next_block(m_core.get_blockchain_storage().get_db());
auto& sn_list = m_core.get_service_node_list();
auto quorum = generate_pulse_quorum(m_core.get_nettype(), sn_list.get_block_leader().key, hf_version, sn_list.active_service_nodes_infos(), entropy, 0);
if (!quorum.validators.empty())
{
auto& entry = res.quorums.emplace_back();
entry.height = curr_height;
entry.quorum_type = static_cast<uint8_t>(service_nodes::quorum_type::pulse);

entry.quorum.validators = hexify(quorum.validators);
entry.quorum.workers = hexify(quorum.workers);

at_least_one_succeeded = true;
}
}

if (!at_least_one_succeeded)
throw rpc_error{ERROR_WRONG_PARAM, "Failed to query any quorums at all"};

Expand Down
10 changes: 5 additions & 5 deletions src/rpc/core_rpc_server_commands_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1822,7 +1822,7 @@ namespace rpc {


LOKI_RPC_DOC_INTROSPECT
// Get the quorum state which is the list of public keys of the nodes who are voting, and the list of public keys of the nodes who are being tested.
// Accesses the list of public keys of the nodes who are participating or being tested in a quorum.
struct GET_QUORUM_STATE : PUBLIC
{
static constexpr auto names() { return NAMES("get_quorum_state"); }
Expand All @@ -1832,17 +1832,17 @@ namespace rpc {
static constexpr uint8_t ALL_QUORUMS_SENTINEL_VALUE = 255;
struct request
{
uint64_t start_height; // (Optional): Start height, omit both start and end height to request the latest quorum
uint64_t start_height; // (Optional): Start height, omit both start and end height to request the latest quorum. Note that "latest" means different heights for different types of quorums as not all quorums exist at every block heights.
uint64_t end_height; // (Optional): End height, omit both start and end height to request the latest quorum
uint8_t quorum_type; // (Optional): Set value to request a specific quorum, 0 = Obligation, 1 = Checkpointing, 255 = all quorums, default is all quorums;
uint8_t quorum_type; // (Optional): Set value to request a specific quorum, 0 = Obligation, 1 = Checkpointing, 2 = Blink, 3 = Pulse, 255 = all quorums, default is all quorums. For Pulse quorums, requesting the blockchain height (or latest) returns the primary pulse quorum responsible for the next block; for heights with blocks this returns the actual quorum, which may be a backup quorum if the primary quorum did not produce in time.

KV_MAP_SERIALIZABLE
};

struct quorum_t
{
std::vector<std::string> validators; // Public key of the service node
std::vector<std::string> workers; // Public key of the service node
std::vector<std::string> validators; // List of service node public keys in the quorum. For obligations quorums these are the testing nodes; for checkpoint and blink these are the participating nodes (there are no workers); for Pulse blink quorums these are the block signers.
std::vector<std::string> workers; // Public key of the quorum workers. For obligations quorums these are the nodes being tested; for Pulse quorums this is the block producer. Checkpoint and Blink quorums do not populate this field.

KV_MAP_SERIALIZABLE

Expand Down

0 comments on commit 2c48efe

Please sign in to comment.