GET_CHECKPOINTS

This commit is contained in:
Sean Darcy 2021-11-08 14:54:43 +11:00
parent 95f6c02d55
commit eedb23c545
14 changed files with 84 additions and 149 deletions

View file

@ -39,6 +39,7 @@ target_link_libraries(blockchain_db
lmdb
filesystem
Boost::thread
nlohmann_json::nlohmann_json
extra)
target_compile_definitions(blockchain_db PRIVATE

View file

@ -37,5 +37,6 @@ target_link_libraries(checkpoints
cryptonote_basic
Boost::program_options
Boost::serialization
nlohmann_json::nlohmann_json
filesystem
extra)

View file

@ -36,6 +36,7 @@
#include "epee/serialization/keyvalue_serialization.h"
#include "cryptonote_core/service_node_rules.h"
#include <vector>
#include <nlohmann/json.hpp>
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@ -54,7 +55,20 @@ namespace cryptonote
if (result) MINFO ("CHECKPOINT PASSED FOR HEIGHT " << height << " " << block_hash);
else MWARNING("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH " << block_hash << "GIVEN HASH: " << hash);
return result;
}
};
void to_json(nlohmann::json& j, const checkpoint_t& c)
{
j = nlohmann::json
{
{"version", c.version},
{"type", c.type},
{"height", c.height},
{"block_hash", tools::type_to_hex(c.block_hash)},
{"signatures", c.signatures},
{"prev_height", c.prev_height},
};
};
height_to_hash const HARDCODED_MAINNET_CHECKPOINTS[] =
{

View file

@ -79,6 +79,8 @@ namespace cryptonote
FIELD(prev_height)
END_SERIALIZE()
};
void to_json(nlohmann::json& j, const checkpoint_t& c);
struct height_to_hash
{

View file

@ -44,4 +44,5 @@ target_link_libraries(cryptonote_basic
Boost::program_options
Boost::serialization
filesystem
nlohmann_json::nlohmann_json
extra)

View file

@ -45,6 +45,7 @@
#include "ringct/rctTypes.h"
#include "device/device.hpp"
#include "txtypes.h"
#include <nlohmann/json.hpp>
namespace service_nodes
{
@ -65,6 +66,15 @@ namespace service_nodes
FIELD(signature)
END_SERIALIZE()
};
inline void to_json(nlohmann::json& j, const quorum_signature& s)
{
j = nlohmann::json
{
{"voter_index", s.voter_index},
{"signature", tools::type_to_hex(s.signature)},
};
};
};
namespace cryptonote

View file

@ -259,58 +259,46 @@ json rpc_command_executor::invoke(
bool rpc_command_executor::print_checkpoints(uint64_t start_height, uint64_t end_height, bool print_json)
{
GET_CHECKPOINTS::request req{start_height, end_height};
if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE &&
req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
uint32_t count;
if (start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE &&
end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
{
req.count = GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT;
count = GET_CHECKPOINTS::NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT;
}
else if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE ||
req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
else if (start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE ||
end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
{
req.count = 1;
count = 1;
}
// Otherwise, neither heights are set to HEIGHT_SENTINEL_VALUE, so get all the checkpoints between start and end
GET_CHECKPOINTS::response res{};
if (!invoke<GET_CHECKPOINTS>(std::move(req), res, "Failed to query blockchain checkpoints"))
auto maybe_checkpoints = try_running([&] { return invoke<GET_CHECKPOINTS>(json{{"start_height", start_height}, {"end_height", end_height}, {"count", count}}); }, "Failed to query blockchain checkpoints");
if (!maybe_checkpoints)
return false;
auto checkpoints = *maybe_checkpoints;
std::string entry;
if (print_json) entry.append("{\n\"checkpoints\": [");
for (size_t i = 0; i < res.checkpoints.size(); i++)
{
GET_CHECKPOINTS::checkpoint_serialized &checkpoint = res.checkpoints[i];
if (print_json)
{
entry.append("\n");
entry.append(epee::serialization::store_t_to_json(checkpoint));
entry.append(",\n");
}
else
if (print_json)
entry.append(checkpoints.dump());
else {
for (size_t i = 0; i < checkpoints.size(); i++)
{
entry.append("[");
entry.append(std::to_string(i));
entry.append("]");
entry.append(" Type: ");
entry.append(checkpoint.type);
entry.append(checkpoints[i]["type"]);
entry.append(" Height: ");
entry.append(std::to_string(checkpoint.height));
entry.append(checkpoints[i]["height"]);
entry.append(" Hash: ");
entry.append(checkpoint.block_hash);
entry.append(checkpoints[i]["block_hash"]);
entry.append("\n");
}
}
if (print_json)
{
entry.append("]\n}");
}
else
{
if (entry.empty())
entry.append("No Checkpoints");
}

View file

@ -42,6 +42,7 @@ target_link_libraries(device
Boost::serialization
PRIVATE
version
nlohmann_json::nlohmann_json
extra)
option(HWDEVICE_DEBUG "Enable hardware wallet debugging (requires also using a debug build of the Ledger wallet)" OFF)

View file

@ -35,4 +35,5 @@ target_link_libraries(multisig
ringct
cryptonote_basic
common
nlohmann_json::nlohmann_json
extra)

View file

@ -40,6 +40,7 @@ target_link_libraries(ringct_basic
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
nlohmann_json::nlohmann_json
extra)
add_library(ringct
@ -53,4 +54,5 @@ target_link_libraries(ringct
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
nlohmann_json::nlohmann_json
extra)

View file

@ -3078,45 +3078,38 @@ namespace cryptonote::rpc {
}
}
//------------------------------------------------------------------------------------------------------------------------------
GET_CHECKPOINTS::response core_rpc_server::invoke(GET_CHECKPOINTS::request&& req, rpc_context context)
void core_rpc_server::invoke(GET_CHECKPOINTS& get_checkpoints, rpc_context context)
{
GET_CHECKPOINTS::response res{};
if (use_bootstrap_daemon_if_necessary<GET_CHECKPOINTS>(req, res))
return res;
if (!context.admin)
check_quantity_limit(req.count, GET_CHECKPOINTS::MAX_COUNT);
check_quantity_limit(get_checkpoints.request.count, GET_CHECKPOINTS::MAX_COUNT);
res.status = STATUS_OK;
get_checkpoints.response["status"] = STATUS_OK;
BlockchainDB const &db = m_core.get_blockchain_storage().get_db();
std::vector<checkpoint_t> checkpoints;
if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE &&
req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
if (get_checkpoints.request.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE &&
get_checkpoints.request.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
{
checkpoint_t top_checkpoint;
if (db.get_top_checkpoint(top_checkpoint))
checkpoints = db.get_checkpoints_range(top_checkpoint.height, 0, req.count);
checkpoints = db.get_checkpoints_range(top_checkpoint.height, 0, get_checkpoints.request.count);
}
else if (req.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
else if (get_checkpoints.request.start_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
{
checkpoints = db.get_checkpoints_range(req.end_height, 0, req.count);
checkpoints = db.get_checkpoints_range(get_checkpoints.request.end_height, 0, get_checkpoints.request.count);
}
else if (req.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
else if (get_checkpoints.request.end_height == GET_CHECKPOINTS::HEIGHT_SENTINEL_VALUE)
{
checkpoints = db.get_checkpoints_range(req.start_height, UINT64_MAX, req.count);
checkpoints = db.get_checkpoints_range(get_checkpoints.request.start_height, UINT64_MAX, get_checkpoints.request.count);
}
else
{
checkpoints = db.get_checkpoints_range(req.start_height, req.end_height);
checkpoints = db.get_checkpoints_range(get_checkpoints.request.start_height, get_checkpoints.request.end_height);
}
res.checkpoints.reserve(checkpoints.size());
for (checkpoint_t const &checkpoint : checkpoints)
res.checkpoints.push_back(checkpoint);
get_checkpoints.response["checkpoints"] = checkpoints;
return res;
return;
}
//------------------------------------------------------------------------------------------------------------------------------
void core_rpc_server::invoke(GET_SN_STATE_CHANGES& get_sn_state_changes, rpc_context context)

View file

@ -248,6 +248,7 @@ namespace cryptonote::rpc {
void invoke(GET_BLOCK_HEADER_BY_HASH& get_block_header_by_hash, rpc_context context);
void invoke(GETBANS& get_bans, rpc_context context);
void invoke(SETBANS& set_bans, rpc_context context);
void invoke(GET_CHECKPOINTS& get_checkpoints, 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);
@ -276,7 +277,6 @@ namespace cryptonote::rpc {
GET_SERVICE_KEYS::response invoke(GET_SERVICE_KEYS::request&& req, rpc_context context);
GET_SERVICE_PRIVKEYS::response invoke(GET_SERVICE_PRIVKEYS::request&& req, rpc_context context);
GET_STAKING_REQUIREMENT::response invoke(GET_STAKING_REQUIREMENT::request&& req, rpc_context context);
GET_CHECKPOINTS::response invoke(GET_CHECKPOINTS::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

@ -66,7 +66,8 @@ void from_json(const nlohmann::json& j, block_header_response& h)
j.at("miner_tx_hash").get_to(h.miner_tx_hash);
j.at("tx_hashes").get_to(h.tx_hashes);
j.at("service_node_winner").get_to(h.service_node_winner);
}
};
KV_SERIALIZE_MAP_CODE_BEGIN(STATUS)
KV_SERIALIZE(status)
@ -163,6 +164,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_OUTPUT_HISTOGRAM::request)
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);
@ -370,36 +372,6 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_SERVICE_NODE_BLACKLISTED_KEY_IMAGES::response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::request)
KV_SERIALIZE_OPT(start_height, HEIGHT_SENTINEL_VALUE)
KV_SERIALIZE_OPT(end_height, HEIGHT_SENTINEL_VALUE)
KV_SERIALIZE_OPT(count, NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::quorum_signature_serialized)
KV_SERIALIZE(voter_index);
KV_SERIALIZE(signature);
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::checkpoint_serialized)
KV_SERIALIZE(version);
KV_SERIALIZE(type);
KV_SERIALIZE(height);
KV_SERIALIZE(block_hash);
KV_SERIALIZE(signatures);
KV_SERIALIZE(prev_height);
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_CHECKPOINTS::response)
KV_SERIALIZE(checkpoints)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(ONS_NAMES_TO_OWNERS::request_entry)
KV_SERIALIZE(name_hash)
KV_SERIALIZE(types)

View file

@ -2102,8 +2102,18 @@ namespace cryptonote::rpc {
};
};
OXEN_RPC_DOC_INTROSPECT
// Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints.
/// Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to retrieve the latest "count" checkpoints.
///
/// Inputs:
///
/// - \p start_height Optional: Get the first count checkpoints starting from this height. Specify both start and end to get the checkpoints inbetween.
/// - \p end_height Optional: Get the first count checkpoints before end height. Specify both start and end to get the checkpoints inbetween.
/// - \p count Optional: Number of checkpoints to query.
///
/// Output values available from a public RPC endpoint:
///
/// - \p status generic RPC error code; "OK" means the request was successful.
/// - \p checkpoints Array of requested checkpoints
struct GET_CHECKPOINTS : PUBLIC
{
static constexpr auto names() { return NAMES("get_checkpoints"); }
@ -2111,75 +2121,14 @@ namespace cryptonote::rpc {
static constexpr size_t MAX_COUNT = 256;
static constexpr uint32_t NUM_CHECKPOINTS_TO_QUERY_BY_DEFAULT = 60;
static constexpr uint64_t HEIGHT_SENTINEL_VALUE = std::numeric_limits<uint64_t>::max() - 1;
struct request
struct request_parameters
{
uint64_t start_height; // Optional: Get the first count checkpoints starting from this height. Specify both start and end to get the checkpoints inbetween.
uint64_t end_height; // Optional: Get the first count checkpoints before end height. Specify both start and end to get the checkpoints inbetween.
uint32_t count; // Optional: Number of checkpoints to query.
} request;
KV_MAP_SERIALIZABLE
};
struct quorum_signature_serialized
{
uint16_t voter_index; // Index of the voter in the relevant quorum
std::string signature; // The signature generated by the voter in the quorum
quorum_signature_serialized() = default;
quorum_signature_serialized(service_nodes::quorum_signature const &entry)
: voter_index(entry.voter_index)
, signature(tools::type_to_hex(entry.signature)) { }
KV_MAP_SERIALIZABLE
BEGIN_SERIALIZE() // NOTE: For store_t_to_json
FIELD(voter_index)
FIELD(signature)
END_SERIALIZE()
};
struct checkpoint_serialized
{
uint8_t version;
std::string type; // Either "Hardcoded" or "ServiceNode" for checkpoints generated by Service Nodes or declared in the code
uint64_t height; // The height the checkpoint is relevant for
std::string block_hash; // The block hash the checkpoint is specifying
std::vector<quorum_signature_serialized> signatures; // Signatures from Service Nodes who agree on the block hash
uint64_t prev_height; // The previous height the checkpoint is based off
checkpoint_serialized() = default;
checkpoint_serialized(checkpoint_t const &checkpoint)
: version(checkpoint.version)
, type(checkpoint_t::type_to_string(checkpoint.type))
, height(checkpoint.height)
, block_hash(tools::type_to_hex(checkpoint.block_hash))
, prev_height(checkpoint.prev_height)
{
signatures.reserve(checkpoint.signatures.size());
for (service_nodes::quorum_signature const &entry : checkpoint.signatures)
signatures.push_back(entry);
}
KV_MAP_SERIALIZABLE
BEGIN_SERIALIZE() // NOTE: For store_t_to_json
FIELD(version)
FIELD(type)
FIELD(height)
FIELD(block_hash)
FIELD(signatures)
FIELD(prev_height)
END_SERIALIZE()
};
struct response
{
std::vector<checkpoint_serialized> checkpoints; // Array of requested checkpoints
std::string status; // Generic RPC error code. "OK" is the success value.
bool untrusted; // If the result is obtained using bootstrap mode, and therefore not trusted `true`, or otherwise `false`.
KV_MAP_SERIALIZABLE
};
};
/// Query hardcoded/service node checkpoints stored for the blockchain. Omit all arguments to