Merge pull request #1432 from jagerman/ss-updates

Storage server RPC updates
This commit is contained in:
Jason Rhinelander 2021-04-18 22:19:13 -03:00 committed by GitHub
commit eda03d1590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 226 additions and 355 deletions

View File

@ -6284,8 +6284,8 @@ struct service_node_proof_serialized_old
service_node_proof_serialized_old(const service_nodes::proof_info &info)
: timestamp{native_to_little(info.proof->timestamp)},
ip{native_to_little(info.proof->public_ip)},
storage_port{native_to_little(info.proof->storage_port)},
storage_lmq_port{native_to_little(info.proof->storage_lmq_port)},
storage_https_port{native_to_little(info.proof->storage_https_port)},
storage_omq_port{native_to_little(info.proof->storage_omq_port)},
quorumnet_port{native_to_little(info.proof->qnet_port)},
version{native_to_little_container(info.proof->version)},
pubkey_ed25519{info.proof->pubkey_ed25519}
@ -6297,8 +6297,8 @@ struct service_node_proof_serialized_old
if (info.proof->timestamp > info.effective_timestamp)
info.effective_timestamp = info.proof->timestamp;
info.proof->public_ip = little_to_native(ip);
info.proof->storage_port = little_to_native(storage_port);
info.proof->storage_lmq_port = little_to_native(storage_lmq_port);
info.proof->storage_https_port = little_to_native(storage_https_port);
info.proof->storage_omq_port = little_to_native(storage_omq_port);
info.proof->qnet_port = little_to_native(quorumnet_port);
info.proof->version = little_to_native_container(version);
info.proof->storage_server_version = {0, 0, 0};
@ -6315,10 +6315,10 @@ struct service_node_proof_serialized_old
uint64_t timestamp;
uint32_t ip;
uint16_t storage_port;
uint16_t storage_https_port;
uint16_t quorumnet_port;
std::array<uint16_t, 3> version;
uint16_t storage_lmq_port;
uint16_t storage_omq_port;
crypto::ed25519_public_key pubkey_ed25519;
};
static_assert(sizeof(service_node_proof_serialized_old) == 56, "service node serialization struct has unexpected size and/or padding");

View File

@ -182,7 +182,7 @@ namespace cryptonote
};
static const command_line::arg_descriptor<bool> arg_service_node = {
"service-node"
, "Run as a service node, options 'service-node-public-ip' and 'storage-server-port' must be set"
, "Run as a service node, option 'service-node-public-ip' must be set"
};
static const command_line::arg_descriptor<std::string> arg_public_ip = {
"service-node-public-ip"
@ -192,11 +192,7 @@ namespace cryptonote
"service node."
};
static const command_line::arg_descriptor<uint16_t> arg_storage_server_port = {
"storage-server-port"
, "The port on which this service node's storage server is accessible. A listening "
"storage server is required for service nodes. (This option is specified "
"automatically when using Loki Launcher.)"
, 0};
"storage-server-port", "Deprecated option, ignored.", 0};
static const command_line::arg_descriptor<uint16_t, false, true, 2> arg_quorumnet_port = {
"quorumnet-port"
, "The port on which this service node listen for direct connections from other "
@ -211,7 +207,7 @@ namespace cryptonote
val;
}
};
static const command_line::arg_descriptor<bool> arg_lmq_quorumnet_public{
static const command_line::arg_descriptor<bool> arg_omq_quorumnet_public{
"lmq-public-quorumnet",
"Allow the curve-enabled quorumnet address (for a Service Node) to be used for public RPC commands as if passed to --lmq-curve-public. "
"Note that even without this option the quorumnet port can be used for RPC commands by --lmq-admin and --lmq-user pubkeys.",
@ -364,7 +360,7 @@ namespace cryptonote
command_line::add_arg(desc, integration_test::arg_hardforks_override);
command_line::add_arg(desc, integration_test::arg_pipe_name);
#endif
command_line::add_arg(desc, arg_lmq_quorumnet_public);
command_line::add_arg(desc, arg_omq_quorumnet_public);
miner::init_options(desc);
BlockchainDB::init_options(desc);
@ -396,19 +392,12 @@ namespace cryptonote
if (m_service_node) {
/// TODO: parse these options early, before we start p2p server etc?
m_storage_port = command_line::get_arg(vm, arg_storage_server_port);
m_quorumnet_port = command_line::get_arg(vm, arg_quorumnet_port);
bool storage_ok = true;
if (m_storage_port == 0 && m_nettype != DEVNET) {
MERROR("Please specify the port on which the storage server is listening with: '--" << arg_storage_server_port.name << " <port>'");
storage_ok = false;
}
bool args_okay = true;
if (m_quorumnet_port == 0) {
MERROR("Quorumnet port cannot be 0; please specify a valid port to listen on with: '--" << arg_quorumnet_port.name << " <port>'");
storage_ok = false;
args_okay = false;
}
const std::string pub_ip = command_line::get_arg(vm, arg_public_ip);
@ -416,7 +405,7 @@ namespace cryptonote
{
if (!epee::string_tools::get_ip_int32_from_string(m_sn_public_ip, pub_ip)) {
MERROR("Unable to parse IPv4 public address from: " << pub_ip);
storage_ok = false;
args_okay = false;
}
if (!epee::net_utils::is_ip_public(m_sn_public_ip)) {
@ -424,25 +413,21 @@ namespace cryptonote
MWARNING("Address given for public-ip is not public; allowing it because dev-allow-local-ips was specified. This service node WILL NOT WORK ON THE PUBLIC OXEN NETWORK!");
} else {
MERROR("Address given for public-ip is not public: " << epee::string_tools::get_ip_string_from_int32(m_sn_public_ip));
storage_ok = false;
args_okay = false;
}
}
}
else
{
MERROR("Please specify an IPv4 public address which the service node & storage server is accessible from with: '--" << arg_public_ip.name << " <ip address>'");
storage_ok = false;
args_okay = false;
}
if (!storage_ok) {
MERROR("IMPORTANT: All service node operators are now required to run the oxen storage "
<< "server and provide the public ip and ports on which it can be accessed on the internet.");
if (!args_okay) {
MERROR("IMPORTANT: One or more required service node-related configuration settings/options were omitted or invalid; "
<< "please fix them and restart oxend.");
return false;
}
MGINFO("Storage server endpoint is set to: "
<< (epee::net_utils::ipv4_network_address{ m_sn_public_ip, m_storage_port }).str());
}
return true;
@ -1014,9 +999,9 @@ namespace cryptonote
}
}
oxenmq::AuthLevel core::lmq_check_access(const crypto::x25519_public_key& pubkey) const {
auto it = m_lmq_auth.find(pubkey);
if (it != m_lmq_auth.end())
oxenmq::AuthLevel core::omq_check_access(const crypto::x25519_public_key& pubkey) const {
auto it = m_omq_auth.find(pubkey);
if (it != m_omq_auth.end())
return it->second;
return oxenmq::AuthLevel::denied;
}
@ -1032,23 +1017,23 @@ namespace cryptonote
// check_sn is whether we check an incoming key against known service nodes (and thus return
// "true" for the service node access if it checks out).
//
oxenmq::AuthLevel core::lmq_allow(std::string_view ip, std::string_view x25519_pubkey_str, oxenmq::AuthLevel default_auth) {
oxenmq::AuthLevel core::omq_allow(std::string_view ip, std::string_view x25519_pubkey_str, oxenmq::AuthLevel default_auth) {
using namespace oxenmq;
AuthLevel auth = default_auth;
if (x25519_pubkey_str.size() == sizeof(crypto::x25519_public_key)) {
crypto::x25519_public_key x25519_pubkey;
std::memcpy(x25519_pubkey.data, x25519_pubkey_str.data(), x25519_pubkey_str.size());
auto user_auth = lmq_check_access(x25519_pubkey);
auto user_auth = omq_check_access(x25519_pubkey);
if (user_auth >= AuthLevel::basic) {
if (user_auth > auth)
auth = user_auth;
MCINFO("lmq", "Incoming " << auth << "-authenticated connection");
MCINFO("omq", "Incoming " << auth << "-authenticated connection");
}
MCINFO("lmq", "Incoming [" << auth << "] curve connection from " << ip << "/" << x25519_pubkey);
MCINFO("omq", "Incoming [" << auth << "] curve connection from " << ip << "/" << x25519_pubkey);
}
else {
MCINFO("lmq", "Incoming [" << auth << "] plain connection from " << ip);
MCINFO("omq", "Incoming [" << auth << "] plain connection from " << ip);
}
return auth;
}
@ -1056,23 +1041,23 @@ namespace cryptonote
void core::init_oxenmq(const boost::program_options::variables_map& vm) {
using namespace oxenmq;
MGINFO("Starting oxenmq");
m_lmq = std::make_unique<OxenMQ>(
m_omq = std::make_unique<OxenMQ>(
tools::copy_guts(m_service_keys.pub_x25519),
tools::copy_guts(m_service_keys.key_x25519),
m_service_node,
[this](std::string_view x25519_pk) { return m_service_node_list.remote_lookup(x25519_pk); },
[](LogLevel level, const char *file, int line, std::string msg) {
// What a lovely interface (<-- sarcasm)
if (ELPP->vRegistry()->allowed(easylogging_level(level), "lmq"))
el::base::Writer(easylogging_level(level), file, line, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct("lmq") << msg;
if (ELPP->vRegistry()->allowed(easylogging_level(level), "omq"))
el::base::Writer(easylogging_level(level), file, line, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct("omq") << msg;
},
oxenmq::LogLevel::trace
);
// ping.ping: a simple debugging target for pinging the lmq listener
m_lmq->add_category("ping", Access{AuthLevel::none})
// ping.ping: a simple debugging target for pinging the omq listener
m_omq->add_category("ping", Access{AuthLevel::none})
.add_request_command("ping", [](Message& m) {
MCINFO("lmq", "Received ping from " << m.conn);
MCINFO("omq", "Received ping from " << m.conn);
m.send_reply("pong");
})
;
@ -1085,9 +1070,9 @@ namespace cryptonote
listen_ip = "0.0.0.0";
std::string qnet_listen = "tcp://" + listen_ip + ":" + std::to_string(m_quorumnet_port);
MGINFO("- listening on " << qnet_listen << " (quorumnet)");
m_lmq->listen_curve(qnet_listen,
[this, public_=command_line::get_arg(vm, arg_lmq_quorumnet_public)](std::string_view ip, std::string_view pk, bool) {
return lmq_allow(ip, pk, public_ ? AuthLevel::basic : AuthLevel::none);
m_omq->listen_curve(qnet_listen,
[this, public_=command_line::get_arg(vm, arg_omq_quorumnet_public)](std::string_view ip, std::string_view pk, bool) {
return omq_allow(ip, pk, public_ ? AuthLevel::basic : AuthLevel::none);
});
m_quorumnet_state = quorumnet_new(*this);
@ -1097,20 +1082,20 @@ namespace cryptonote
}
void core::start_oxenmq() {
update_lmq_sns(); // Ensure we have SNs set for the current block before starting
update_omq_sns(); // Ensure we have SNs set for the current block before starting
if (m_service_node)
{
m_pulse_thread_id = m_lmq->add_tagged_thread("pulse");
m_lmq->add_timer([this]() { pulse::main(m_quorumnet_state, *this); },
m_pulse_thread_id = m_omq->add_tagged_thread("pulse");
m_omq->add_timer([this]() { pulse::main(m_quorumnet_state, *this); },
std::chrono::milliseconds(500),
false,
m_pulse_thread_id);
m_lmq->add_timer([this]() {this->check_service_node_time();},
m_omq->add_timer([this]() {this->check_service_node_time();},
5s,
false);
}
m_lmq->start();
m_omq->start();
}
//-----------------------------------------------------------------------------------------------
@ -1126,7 +1111,7 @@ namespace cryptonote
#endif
if (m_quorumnet_state)
quorumnet_delete(m_quorumnet_state);
m_lmq.reset();
m_omq.reset();
m_service_node_list.store();
m_miner.stop();
m_mempool.deinit();
@ -1682,7 +1667,7 @@ namespace cryptonote
});
if (proofversion >= MIN_TIMESTAMP_VERSION && x_pkey) {
m_lmq->request(
m_omq->request(
tools::view_guts(x_pkey),
"quorum.timestamp",
[this, pubkey](bool success, std::vector<std::string> data) {
@ -1944,10 +1929,10 @@ namespace cryptonote
auto hf_version = get_hard_fork_version(height);
//TODO: remove after HF18
if (hf_version < HF_VERSION_PROOF_BTENC) {
NOTIFY_UPTIME_PROOF::request req = m_service_node_list.generate_uptime_proof(m_sn_public_ip, m_storage_port, m_storage_lmq_port, m_quorumnet_port);
NOTIFY_UPTIME_PROOF::request req = m_service_node_list.generate_uptime_proof(m_sn_public_ip, storage_https_port(), storage_omq_port(), m_quorumnet_port);
relayed = get_protocol()->relay_uptime_proof(req, fake_context);
} else {
auto proof = m_service_node_list.generate_uptime_proof(m_sn_public_ip, m_storage_port, m_storage_lmq_port, ss_version, m_quorumnet_port, lokinet_version);
auto proof = m_service_node_list.generate_uptime_proof(m_sn_public_ip, storage_https_port(), storage_omq_port(), ss_version, m_quorumnet_port, lokinet_version);
NOTIFY_BTENCODED_UPTIME_PROOF::request req = proof.generate_request();
relayed = get_protocol()->relay_btencoded_uptime_proof(req, fake_context);
}
@ -1965,7 +1950,7 @@ namespace cryptonote
{
oxenmq::pubkey_set added;
added.insert(tools::copy_guts(pkey));
m_lmq->update_active_sns(added, {} /*removed*/);
m_omq->update_active_sns(added, {} /*removed*/);
}
return result;
}
@ -1982,7 +1967,7 @@ namespace cryptonote
{
oxenmq::pubkey_set added;
added.insert(tools::copy_guts(pkey));
m_lmq->update_active_sns(added, {} /*removed*/);
m_omq->update_active_sns(added, {} /*removed*/);
}
return result;
}
@ -2245,12 +2230,12 @@ namespace cryptonote
return true;
}
void core::update_lmq_sns()
void core::update_omq_sns()
{
// TODO: let callers (e.g. lokinet, ss) subscribe to callbacks when this fires
oxenmq::pubkey_set active_sns;
m_service_node_list.copy_active_x25519_pubkeys(std::inserter(active_sns, active_sns.end()));
m_lmq->set_active_sns(std::move(active_sns));
m_omq->set_active_sns(std::move(active_sns));
}
//-----------------------------------------------------------------------------------------------
crypto::hash core::get_tail_id() const
@ -2338,11 +2323,11 @@ namespace cryptonote
m_service_node_list.for_each_service_node_info_and_proof(sn_pks.begin(), sn_pks.end(), [&](auto& pk, auto& sni, auto& proof) {
if (pk != m_service_keys.pub && proof.proof->public_ip == m_sn_public_ip &&
(proof.proof->qnet_port == m_quorumnet_port || proof.proof->storage_port == m_storage_port || proof.proof->storage_port == m_storage_lmq_port))
(proof.proof->qnet_port == m_quorumnet_port || proof.proof->storage_https_port == storage_https_port() || proof.proof->storage_omq_port == storage_omq_port()))
MGINFO_RED(
"Another service node (" << pk << ") is broadcasting the same public IP and ports as this service node (" <<
epee::string_tools::get_ip_string_from_int32(m_sn_public_ip) << ":" << proof.proof->qnet_port << "[qnet], :" <<
proof.proof->storage_port << "[SS-HTTP], :" << proof.proof->storage_lmq_port << "[SS-LMQ]). "
proof.proof->storage_https_port << "[SS-HTTP], :" << proof.proof->storage_omq_port << "[SS-LMQ]). "
"This will lead to deregistration of one or both service nodes if not corrected. "
"(Do both service nodes have the correct IP for the service-node-public-ip setting?)");
});

View File

@ -350,7 +350,7 @@ namespace cryptonote
/// Called (from service_node_quorum_cop) to tell quorumnet that it need to refresh its list of
/// active SNs.
void update_lmq_sns();
void update_omq_sns();
/**
* @brief get the cryptonote protocol instance
@ -698,8 +698,8 @@ namespace cryptonote
tx_memory_pool &get_pool() { return m_mempool; }
/// Returns a reference to the OxenMQ object. Must not be called before init(), and should not
/// be used for any lmq communication until after start_oxenmq() has been called.
oxenmq::OxenMQ& get_lmq() { return *m_lmq; }
/// be used for any omq communication until after start_oxenmq() has been called.
oxenmq::OxenMQ& get_omq() { return *m_omq; }
/**
* @copydoc miner::on_synchronized
@ -1024,10 +1024,11 @@ namespace cryptonote
/// Time point at which the storage server and lokinet last pinged us
std::atomic<time_t> m_last_storage_server_ping, m_last_lokinet_ping;
std::atomic<uint16_t> m_storage_lmq_port;
std::atomic<uint16_t> m_storage_https_port, m_storage_omq_port;
uint32_t sn_public_ip() const { return m_sn_public_ip; }
uint16_t storage_port() const { return m_storage_port; }
uint16_t storage_https_port() const { return m_storage_https_port; }
uint16_t storage_omq_port() const { return m_storage_omq_port; }
uint16_t quorumnet_port() const { return m_quorumnet_port; }
/**
@ -1144,7 +1145,7 @@ namespace cryptonote
* Checks the given x25519 pubkey against the configured access lists and, if allowed, returns
* the access level; otherwise returns `denied`.
*/
oxenmq::AuthLevel lmq_check_access(const crypto::x25519_public_key& pubkey) const;
oxenmq::AuthLevel omq_check_access(const crypto::x25519_public_key& pubkey) const;
/**
* @brief Initializes OxenMQ object, called during init().
@ -1166,7 +1167,7 @@ namespace cryptonote
/**
* Returns whether to allow the connection and, if so, at what authentication level.
*/
oxenmq::AuthLevel lmq_allow(std::string_view ip, std::string_view x25519_pubkey, oxenmq::AuthLevel default_auth);
oxenmq::AuthLevel omq_allow(std::string_view ip, std::string_view x25519_pubkey, oxenmq::AuthLevel default_auth);
/**
* @brief Internal use only!
@ -1174,7 +1175,7 @@ namespace cryptonote
* This returns a mutable reference to the internal auth level map that OxenMQ uses, for
* internal use only.
*/
std::unordered_map<crypto::x25519_public_key, oxenmq::AuthLevel>& _lmq_auth_level_map() { return m_lmq_auth; }
std::unordered_map<crypto::x25519_public_key, oxenmq::AuthLevel>& _omq_auth_level_map() { return m_omq_auth; }
oxenmq::TaggedThreadID const &pulse_thread_id() const { return *m_pulse_thread_id; }
/// Service Node's storage server and lokinet version
@ -1244,13 +1245,12 @@ namespace cryptonote
bool m_service_node; // True if running in service node mode
service_keys m_service_keys; // Always set, even for non-SN mode -- these can be used for public oxenmq rpc
/// Service Node's public IP and storage server port (http and oxenmq)
/// Service Node's public IP and qnet ports
uint32_t m_sn_public_ip;
uint16_t m_storage_port;
uint16_t m_quorumnet_port;
/// OxenMQ main object. Gets created during init().
std::unique_ptr<oxenmq::OxenMQ> m_lmq;
std::unique_ptr<oxenmq::OxenMQ> m_omq;
// Internal opaque data object managed by cryptonote_protocol/quorumnet.cpp. void pointer to
// avoid linking issues (protocol does not link against core).
@ -1258,7 +1258,7 @@ namespace cryptonote
/// Stores x25519 -> access level for LMQ authentication.
/// Not to be modified after the LMQ listener starts.
std::unordered_map<crypto::x25519_public_key, oxenmq::AuthLevel> m_lmq_auth;
std::unordered_map<crypto::x25519_public_key, oxenmq::AuthLevel> m_omq_auth;
size_t block_sync_size;

View File

@ -2753,14 +2753,14 @@ namespace service_nodes
size_t buf_size;
crypto::hash result;
auto buf = tools::memcpy_le(proof.pubkey.data, proof.timestamp, proof.public_ip, proof.storage_port, proof.pubkey_ed25519.data, proof.qnet_port, proof.storage_lmq_port);
auto buf = tools::memcpy_le(proof.pubkey.data, proof.timestamp, proof.public_ip, proof.storage_https_port, proof.pubkey_ed25519.data, proof.qnet_port, proof.storage_omq_port);
buf_size = buf.size();
crypto::cn_fast_hash(buf.data(), buf_size, result);
return result;
}
cryptonote::NOTIFY_UPTIME_PROOF::request service_node_list::generate_uptime_proof(
uint32_t public_ip, uint16_t storage_port, uint16_t storage_lmq_port, uint16_t quorumnet_port) const
uint32_t public_ip, uint16_t storage_https_port, uint16_t storage_omq_port, uint16_t quorumnet_port) const
{
assert(m_service_node_keys);
const auto& keys = *m_service_node_keys;
@ -2769,8 +2769,8 @@ namespace service_nodes
result.timestamp = time(nullptr);
result.pubkey = keys.pub;
result.public_ip = public_ip;
result.storage_port = storage_port;
result.storage_lmq_port = storage_lmq_port;
result.storage_https_port = storage_https_port;
result.storage_omq_port = storage_omq_port;
result.qnet_port = quorumnet_port;
result.pubkey_ed25519 = keys.pub_ed25519;
@ -2780,10 +2780,10 @@ namespace service_nodes
return result;
}
uptime_proof::Proof service_node_list::generate_uptime_proof(uint32_t public_ip, uint16_t storage_port, uint16_t storage_lmq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version) const
uptime_proof::Proof service_node_list::generate_uptime_proof(uint32_t public_ip, uint16_t storage_https_port, uint16_t storage_omq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version) const
{
const auto& keys = *m_service_node_keys;
return uptime_proof::Proof(public_ip, storage_port, storage_lmq_port, ss_version, quorumnet_port, lokinet_version, keys);
return uptime_proof::Proof(public_ip, storage_https_port, storage_omq_port, ss_version, quorumnet_port, lokinet_version, keys);
}
#ifdef __cpp_lib_erase_if // # (C++20)
@ -2853,8 +2853,8 @@ namespace service_nodes
//TODO remove after HF18
bool proof_info::update(uint64_t ts,
uint32_t ip,
uint16_t s_port,
uint16_t s_lmq_port,
uint16_t s_https_port,
uint16_t s_omq_port,
uint16_t q_port,
std::array<uint16_t, 3> ver,
const crypto::ed25519_public_key& pk_ed,
@ -2864,8 +2864,8 @@ namespace service_nodes
if (!proof) proof = std::unique_ptr<uptime_proof::Proof>(new uptime_proof::Proof());
update_db |= update_val(timestamp, ts);
update_db |= update_val(proof->public_ip, ip);
update_db |= update_val(proof->storage_port, s_port);
update_db |= update_val(proof->storage_lmq_port, s_lmq_port);
update_db |= update_val(proof->storage_https_port, s_https_port);
update_db |= update_val(proof->storage_omq_port, s_omq_port);
update_db |= update_val(proof->qnet_port, q_port);
update_db |= update_val(proof->version, ver);
update_db |= update_val(proof->pubkey_ed25519, pk_ed);
@ -2973,7 +2973,7 @@ namespace service_nodes
}
auto old_x25519 = iproof.pubkey_x25519;
if (iproof.update(std::chrono::system_clock::to_time_t(now), proof.public_ip, proof.storage_port, proof.storage_lmq_port, proof.qnet_port, proof.snode_version, proof.pubkey_ed25519, derived_x25519_pubkey))
if (iproof.update(std::chrono::system_clock::to_time_t(now), proof.public_ip, proof.storage_https_port, proof.storage_omq_port, proof.qnet_port, proof.snode_version, proof.pubkey_ed25519, derived_x25519_pubkey))
iproof.store(proof.pubkey, m_blockchain);
if (now - x25519_map_last_pruned >= X25519_MAP_PRUNING_INTERVAL)

View File

@ -172,7 +172,7 @@ namespace service_nodes
// caller's responsibility).
bool update(uint64_t ts, std::unique_ptr<uptime_proof::Proof> new_proof, const crypto::x25519_public_key &pk_x2);
// TODO: remove after HF18
bool update(uint64_t ts, uint32_t ip, uint16_t s_port, uint16_t s_lmq_port, uint16_t q_port, std::array<uint16_t, 3> ver, const crypto::ed25519_public_key &pk_ed, const crypto::x25519_public_key &pk_x2);
bool update(uint64_t ts, uint32_t ip, uint16_t s_https_port, uint16_t s_omq_port, uint16_t q_port, std::array<uint16_t, 3> ver, const crypto::ed25519_public_key &pk_ed, const crypto::x25519_public_key &pk_x2);
// Stores this record in the database.
void store(const crypto::public_key &pubkey, cryptonote::Blockchain &blockchain);
@ -529,11 +529,11 @@ namespace service_nodes
/// Record public ip and storage port and add them to the service node list
//TODO: remove after HF18
cryptonote::NOTIFY_UPTIME_PROOF::request generate_uptime_proof(uint32_t public_ip,
uint16_t storage_port,
uint16_t storage_lmq_port,
uint16_t storage_https_port,
uint16_t storage_omq_port,
uint16_t quorumnet_port) const;
uptime_proof::Proof generate_uptime_proof(uint32_t public_ip, uint16_t storage_port, uint16_t storage_lmq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version) const;
uptime_proof::Proof generate_uptime_proof(uint32_t public_ip, uint16_t storage_port, uint16_t storage_omq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version) const;
//TODO: remove after HF18
bool handle_uptime_proof(cryptonote::NOTIFY_UPTIME_PROOF::request const &proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey);

View File

@ -532,7 +532,7 @@ namespace service_nodes
// These feels out of place here because the hook system sucks: TODO replace it with
// std::function hooks instead.
m_core.update_lmq_sns();
m_core.update_omq_sns();
return true;
}

View File

@ -211,7 +211,7 @@ namespace service_nodes {
// blocks out of sync and sending something that it thinks is legit.
constexpr uint64_t VOTE_OR_TX_VERIFY_HEIGHT_BUFFER = 5;
constexpr std::array<uint16_t, 3> MIN_STORAGE_SERVER_VERSION{{2, 0, 7}};
constexpr std::array<uint16_t, 3> MIN_STORAGE_SERVER_VERSION{{2, 0, 9}};
constexpr std::array<uint16_t, 3> MIN_LOKINET_VERSION{{0, 8, 0}};
// The minimum accepted version number, broadcasted by Service Nodes via uptime proofs for each hardfork

View File

@ -14,7 +14,23 @@ namespace uptime_proof
{
//Constructor for the uptime proof, will take the service node keys as a param and sign
Proof::Proof(uint32_t sn_public_ip, uint16_t sn_storage_port, uint16_t sn_storage_lmq_port, const std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, const std::array<uint16_t, 3> lokinet_version, const service_nodes::service_node_keys& keys) : version{OXEN_VERSION}, pubkey{keys.pub}, timestamp{static_cast<uint64_t>(time(nullptr))}, public_ip{sn_public_ip}, storage_port{sn_storage_port}, pubkey_ed25519{keys.pub_ed25519},qnet_port{quorumnet_port}, storage_lmq_port{sn_storage_lmq_port}, storage_server_version{ss_version}
Proof::Proof(
uint32_t sn_public_ip,
uint16_t sn_storage_https_port,
uint16_t sn_storage_omq_port,
const std::array<uint16_t, 3> ss_version,
uint16_t quorumnet_port,
const std::array<uint16_t, 3> lokinet_version,
const service_nodes::service_node_keys& keys) :
version{OXEN_VERSION},
pubkey{keys.pub},
timestamp{static_cast<uint64_t>(time(nullptr))},
public_ip{sn_public_ip},
pubkey_ed25519{keys.pub_ed25519},
qnet_port{quorumnet_port},
storage_https_port{sn_storage_https_port},
storage_omq_port{sn_storage_omq_port},
storage_server_version{ss_version}
{
this->lokinet_version = lokinet_version;
crypto::hash hash = this->hash_uptime_proof();
@ -41,7 +57,7 @@ Proof::Proof(const std::string& serialized_proof)
//public_ip
bool succeeded = epee::string_tools::get_ip_int32_from_string(public_ip, var::get<std::string>(bt_proof.at("ip")));
//storage_port
storage_port = static_cast<uint16_t>(get_int<unsigned>(bt_proof.at("s")));
storage_https_port = static_cast<uint16_t>(get_int<unsigned>(bt_proof.at("shp")));
//pubkey_ed25519
pubkey_ed25519 = tools::make_from_guts<crypto::ed25519_public_key>(var::get<std::string>(bt_proof.at("pke")));
//pubkey
@ -51,8 +67,8 @@ Proof::Proof(const std::string& serialized_proof)
std::memcpy(pubkey.data, pubkey_ed25519.data, 32);
//qnet_port
qnet_port = get_int<unsigned>(bt_proof.at("q"));
//storage_lmq_port
storage_lmq_port = get_int<unsigned>(bt_proof.at("slp"));
//storage_omq_port
storage_omq_port = get_int<unsigned>(bt_proof.at("sop"));
//storage_version
const bt_list& bt_storage_version = var::get<bt_list>(bt_proof.at("sv"));
k = 0;
@ -92,13 +108,13 @@ oxenmq::bt_dict Proof::bt_encode_uptime_proof() const
//public_ip
{"ip", epee::string_tools::get_ip_string_from_int32(public_ip)},
//storage_port
{"s", storage_port},
{"shp", storage_https_port},
//pubkey_ed25519
{"pke", tools::view_guts(pubkey_ed25519)},
//qnet_port
{"q", qnet_port},
//storage_lmq_port
{"slp", storage_lmq_port},
//storage_omq_port
{"sop", storage_omq_port},
//storage_version
{"sv", oxenmq::bt_list{{storage_server_version[0], storage_server_version[1], storage_server_version[2]}}},
//lokinet_version
@ -132,18 +148,12 @@ bool operator==(const uptime_proof::Proof& lhs, const uptime_proof::Proof& rhs)
(lhs.pubkey_ed25519 != rhs.pubkey_ed25519) ||
(lhs.sig_ed25519 != rhs.sig_ed25519) ||
(lhs.public_ip != rhs.public_ip) ||
(lhs.storage_port != rhs.storage_port) ||
(lhs.storage_lmq_port != rhs.storage_lmq_port) ||
(lhs.storage_https_port != rhs.storage_https_port) ||
(lhs.storage_omq_port != rhs.storage_omq_port) ||
(lhs.qnet_port != rhs.qnet_port) ||
(lhs.version[0] != rhs.version[0]) ||
(lhs.version[1] != rhs.version[1]) ||
(lhs.version[2] != rhs.version[2]) ||
(lhs.storage_server_version[0] != rhs.storage_server_version[0]) ||
(lhs.storage_server_version[1] != rhs.storage_server_version[1]) ||
(lhs.storage_server_version[2] != rhs.storage_server_version[2]) ||
(lhs.lokinet_version[0] != rhs.lokinet_version[0]) ||
(lhs.lokinet_version[1] != rhs.lokinet_version[1]) ||
(lhs.lokinet_version[2] != rhs.lokinet_version[2]))
(lhs.version != rhs.version) ||
(lhs.storage_server_version != rhs.storage_server_version) ||
(lhs.lokinet_version != rhs.lokinet_version))
result = false;
return result;

View File

@ -21,12 +21,12 @@ public:
crypto::ed25519_public_key pubkey_ed25519;
crypto::ed25519_signature sig_ed25519;
uint32_t public_ip;
uint16_t storage_port;
uint16_t storage_lmq_port;
uint16_t storage_https_port;
uint16_t storage_omq_port;
uint16_t qnet_port;
Proof() = default;
Proof(uint32_t sn_public_ip, uint16_t sn_storage_port, uint16_t sn_storage_lmq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version, const service_nodes::service_node_keys& keys);
Proof(uint32_t sn_public_ip, uint16_t sn_storage_https_port, uint16_t sn_storage_omq_port, std::array<uint16_t, 3> ss_version, uint16_t quorumnet_port, std::array<uint16_t, 3> lokinet_version, const service_nodes::service_node_keys& keys);
Proof(const std::string& serialized_proof);
oxenmq::bt_dict bt_encode_uptime_proof() const;

View File

@ -110,8 +110,8 @@ KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_UPTIME_PROOF::request)
KV_SERIALIZE_N(snode_version[2], "snode_version_patch")
KV_SERIALIZE(timestamp)
KV_SERIALIZE(public_ip)
KV_SERIALIZE(storage_port)
KV_SERIALIZE(storage_lmq_port)
KV_SERIALIZE_N(storage_https_port, "storage_port")
KV_SERIALIZE_N(storage_omq_port, "storage_lmq_port")
KV_SERIALIZE(qnet_port)
KV_SERIALIZE_VAL_POD_AS_BLOB(pubkey)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig)

View File

@ -259,8 +259,8 @@ namespace cryptonote
crypto::ed25519_public_key pubkey_ed25519;
crypto::ed25519_signature sig_ed25519;
uint32_t public_ip;
uint16_t storage_port;
uint16_t storage_lmq_port;
uint16_t storage_https_port;
uint16_t storage_omq_port;
uint16_t qnet_port;
KV_MAP_SERIALIZABLE

View File

@ -71,7 +71,7 @@ using pending_signature_set = std::unordered_set<pending_signature, pending_sign
struct QnetState {
cryptonote::core &core;
OxenMQ &lmq{core.get_lmq()};
OxenMQ &omq{core.get_omq()};
// Track submitted blink txes here; unlike the blinks stored in the mempool we store these ones
// more liberally to track submitted blinks, even if unsigned/unacceptable, while the mempool
@ -205,7 +205,7 @@ void peer_relay_to_prepared_destinations(cryptonote::core &core, std::vector<pre
{
for (auto const &[x25519_string, connect_string]: destinations) {
MINFO("Relaying data to " << to_hex(x25519_string) << " @ " << connect_string);
core.get_lmq().send(x25519_string, command, std::move(data), send_option::hint{connect_string});
core.get_omq().send(x25519_string, command, std::move(data), send_option::hint{connect_string});
}
}
@ -261,7 +261,7 @@ public:
std::unordered_set<crypto::public_key> exclude = {},
bool include_workers = false
)
: lmq{qnet.lmq} {
: omq{qnet.omq} {
const auto& keys = qnet.core.get_service_keys();
assert(qnet.core.service_node());
@ -319,7 +319,7 @@ public:
}
private:
OxenMQ &lmq;
OxenMQ &omq;
/// Looks up a pubkey in known remotes and adds it to `peers`. If strong, it is added with an
/// address, otherwise it is added with an empty address. If the element already exists, it
@ -435,9 +435,9 @@ private:
for (auto &peer : peers) {
MTRACE("Relaying " << cmd << " to peer " << to_hex(peer.first) << (peer.second.empty() ? " (if connected)"s : " @ " + peer.second));
if (peer.second.empty())
lmq.send(peer.first, cmd, relay_data[I]..., send_option::optional{});
omq.send(peer.first, cmd, relay_data[I]..., send_option::optional{});
else
lmq.send(peer.first, cmd, relay_data[I]..., send_option::hint{peer.second});
omq.send(peer.first, cmd, relay_data[I]..., send_option::hint{peer.second});
}
}
@ -797,10 +797,10 @@ void process_blink_signatures(QnetState &qnet, const std::shared_ptr<blink_tx> &
if (reply_tag && reply_conn) {
if (became_approved) {
MINFO("Blink tx became approved; sending result back to originating node");
qnet.lmq.send(reply_conn, "bl.good", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
qnet.omq.send(reply_conn, "bl.good", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
} else if (became_rejected) {
MINFO("Blink tx became rejected; sending result back to originating node");
qnet.lmq.send(reply_conn, "bl.bad", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
qnet.omq.send(reply_conn, "bl.bad", bt_serialize(bt_dict{{"!", reply_tag}}), send_option::optional{});
}
}
}
@ -1602,7 +1602,7 @@ void handle_pulse_participation_bit_or_bitset(Message &m, QnetState& qnet, bool
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + tag + "'");
}
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
qnet.omq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
}
void handle_pulse_block_template(Message &m, QnetState &qnet)
@ -1619,7 +1619,7 @@ void handle_pulse_block_template(Message &m, QnetState &qnet)
else
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + tag + "'");
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
qnet.omq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
}
void handle_pulse_random_value_hash(Message &m, QnetState &qnet)
@ -1642,7 +1642,7 @@ void handle_pulse_random_value_hash(Message &m, QnetState &qnet)
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + tag + "'");
}
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
qnet.omq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
}
void handle_pulse_random_value(Message &m, QnetState &qnet)
@ -1663,7 +1663,7 @@ void handle_pulse_random_value(Message &m, QnetState &qnet)
throw std::invalid_argument(std::string(INVALID_ARG_PREFIX) + tag + "'");
}
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
qnet.omq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
}
void handle_pulse_signed_block(Message &m, QnetState &qnet)
@ -1682,7 +1682,7 @@ void handle_pulse_signed_block(Message &m, QnetState &qnet)
throw std::invalid_argument("Invalid pulse signed block: missing required field '"s + tag + "'");
}
qnet.lmq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
qnet.omq.job([&qnet, data = std::move(msg)]() { pulse::handle_message(&qnet, data); }, qnet.core.pulse_thread_id());
}
} // end empty namespace
@ -1701,14 +1701,14 @@ void init_core_callbacks() {
namespace {
void setup_endpoints(cryptonote::core& core, void* obj) {
auto& lmq = core.get_lmq();
auto& omq = core.get_omq();
if (core.service_node()) {
if (!obj)
throw std::logic_error{"qnet initialization failure: quorumnet_new must be called for service node operation"};
auto& qnet = QnetState::from(obj);
// quorum.*: commands between quorum members, requires that both side of the connection is a SN
lmq.add_category("quorum", Access{AuthLevel::none, true /*remote sn*/, true /*local sn*/}, 2 /*reserved threads*/)
omq.add_category("quorum", Access{AuthLevel::none, true /*remote sn*/, true /*local sn*/}, 2 /*reserved threads*/)
// Receives an obligation vote
.add_command("vote_ob", [&qnet](Message& m) { handle_obligation_vote(m, qnet); })
// Receives blink tx signatures or rejections between quorum members (either original or
@ -1719,13 +1719,13 @@ void setup_endpoints(cryptonote::core& core, void* obj) {
;
// blink.*: commands sent to blink quorum members from anyone (e.g. blink submission)
lmq.add_category("blink", Access{AuthLevel::none, false /*remote sn*/, true /*local sn*/}, 1 /*reserved thread*/)
omq.add_category("blink", Access{AuthLevel::none, false /*remote sn*/, true /*local sn*/}, 1 /*reserved thread*/)
// Receives a new blink tx submission from an external node, or forward from other quorum
// members who received it from an external node.
.add_command("submit", [&qnet](Message& m) { handle_blink(m, qnet); })
;
lmq.add_category(PULSE_CMD_CATEGORY, Access{AuthLevel::none, true /*remote sn*/, true /*local sn*/}, 1 /*reserved thread*/)
omq.add_category(PULSE_CMD_CATEGORY, Access{AuthLevel::none, true /*remote sn*/, true /*local sn*/}, 1 /*reserved thread*/)
.add_command(PULSE_CMD_VALIDATOR_BIT, [&qnet](Message& m) { handle_pulse_participation_bit_or_bitset(m, qnet, false /*bitset*/); })
.add_command(PULSE_CMD_VALIDATOR_BITSET, [&qnet](Message& m) { handle_pulse_participation_bit_or_bitset(m, qnet, true /*bitset*/); })
.add_command(PULSE_CMD_BLOCK_TEMPLATE, [&qnet](Message& m) { handle_pulse_block_template(m, qnet); })
@ -1736,7 +1736,7 @@ void setup_endpoints(cryptonote::core& core, void* obj) {
}
// bl.*: responses to blinks sent from quorum members back to the node who submitted the blink
lmq.add_category("bl", Access{AuthLevel::none, true /*remote sn*/, false /*local sn*/})
omq.add_category("bl", Access{AuthLevel::none, true /*remote sn*/, false /*local sn*/})
// Message sent back to the blink initiator that the transaction was NOT relayed, either
// because the height was invalid or the quorum checksum failed. This is only sent by the
// entry point service nodes into the quorum to let it know the tx verification has not
@ -1760,13 +1760,13 @@ void setup_endpoints(cryptonote::core& core, void* obj) {
// 8.x.1 (i.e. the first post-hard-fork release): remove the aliases since no 7.1.x nodes will
// be left.
lmq.add_command_alias("vote_ob", "quorum.vote_ob");
lmq.add_command_alias("blink_sign", "quorum.blink_sign");
lmq.add_command_alias("timestamp", "quorum.timestamp");
lmq.add_command_alias("blink", "blink.submit");
lmq.add_command_alias("bl_nostart", "bl.nostart");
lmq.add_command_alias("bl_bad", "bl.bad");
lmq.add_command_alias("bl_good", "bl.good");
omq.add_command_alias("vote_ob", "quorum.vote_ob");
omq.add_command_alias("blink_sign", "quorum.blink_sign");
omq.add_command_alias("timestamp", "quorum.timestamp");
omq.add_command_alias("blink", "blink.submit");
omq.add_command_alias("bl_nostart", "bl.nostart");
omq.add_command_alias("bl_bad", "bl.bad");
omq.add_command_alias("bl_good", "bl.good");
}
}

View File

@ -271,7 +271,7 @@ void daemon::init_options(boost::program_options::options_description& option_sp
node_server::init_options(option_spec);
cryptonote::rpc::core_rpc_server::init_options(option_spec, hidden);
cryptonote::rpc::http_server::init_options(option_spec, hidden);
cryptonote::rpc::init_lmq_options(option_spec);
cryptonote::rpc::init_omq_options(option_spec);
quorumnet::init_core_callbacks();
}
@ -308,7 +308,7 @@ bool daemon::run(bool interactive)
throw std::runtime_error("Failed to start core");
MGINFO("Starting OxenMQ");
lmq_rpc = std::make_unique<cryptonote::rpc::lmq_rpc>(*core, *rpc, vm);
omq_rpc = std::make_unique<cryptonote::rpc::omq_rpc>(*core, *rpc, vm);
core->start_oxenmq();
if (http_rpc_admin) {

View File

@ -78,7 +78,7 @@ private:
std::unique_ptr<node_server> p2p;
std::unique_ptr<cryptonote::rpc::core_rpc_server> rpc;
std::optional<cryptonote::rpc::http_server> http_rpc_admin, http_rpc_public;
std::unique_ptr<cryptonote::rpc::lmq_rpc> lmq_rpc;
std::unique_ptr<cryptonote::rpc::omq_rpc> omq_rpc;
};
} // namespace daemonize

View File

@ -1667,8 +1667,8 @@ static void append_printable_service_node_list_entry(cryptonote::network_type ne
if (entry.public_ip == "0.0.0.0")
stream << "(Awaiting confirmation from network)";
else
stream << entry.public_ip << " :" << entry.storage_port << " (storage), :" << entry.storage_lmq_port
<< " (storage lmq), :" << entry.quorumnet_port << " (quorumnet)";
stream << entry.public_ip << " :" << entry.storage_port << " (storage https), :" << entry.storage_lmq_port
<< " (storage omq), :" << entry.quorumnet_port << " (quorumnet)";
stream << "\n";
if (detailed_view)

View File

@ -2330,8 +2330,8 @@ namespace cryptonote { namespace rpc {
{
res.service_node_state.service_node_pubkey = std::move(get_service_node_key_res.service_node_pubkey);
res.service_node_state.public_ip = epee::string_tools::get_ip_string_from_int32(m_core.sn_public_ip());
res.service_node_state.storage_port = m_core.storage_port();
res.service_node_state.storage_lmq_port = m_core.m_storage_lmq_port;
res.service_node_state.storage_port = m_core.storage_https_port();
res.service_node_state.storage_lmq_port = m_core.storage_omq_port();
res.service_node_state.quorumnet_port = m_core.quorumnet_port();
res.service_node_state.pubkey_ed25519 = std::move(get_service_node_key_res.service_node_ed25519_pubkey);
res.service_node_state.pubkey_x25519 = std::move(get_service_node_key_res.service_node_x25519_pubkey);
@ -3051,8 +3051,8 @@ namespace cryptonote { namespace rpc {
entry.lokinet_version = proof.proof->lokinet_version;
entry.storage_server_version = proof.proof->storage_server_version;
entry.public_ip = epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip);
entry.storage_port = proof.proof->storage_port;
entry.storage_lmq_port = proof.proof->storage_lmq_port;
entry.storage_port = proof.proof->storage_https_port;
entry.storage_lmq_port = proof.proof->storage_omq_port;
entry.storage_server_reachable = proof.storage_server_reachable;
entry.pubkey_ed25519 = proof.proof->pubkey_ed25519 ? tools::type_to_hex(proof.proof->pubkey_ed25519) : "";
entry.pubkey_x25519 = proof.pubkey_x25519 ? tools::type_to_hex(proof.pubkey_x25519) : "";
@ -3187,95 +3187,6 @@ namespace cryptonote { namespace rpc {
return res;
}
//------------------------------------------------------------------------------------------------------------------------------
/// Start with seed and perform a series of computation arriving at the answer
static uint64_t perform_blockchain_test_routine(const cryptonote::core& core,
uint64_t max_height,
uint64_t seed)
{
/// Should be sufficiently large to make it impractical
/// to query remote nodes
constexpr size_t NUM_ITERATIONS = 1000;
std::mt19937_64 mt(seed);
crypto::hash hash;
uint64_t height = seed;
for (auto i = 0u; i < NUM_ITERATIONS; ++i)
{
height = height % (max_height + 1);
hash = core.get_block_id_by_height(height);
using blob_t = cryptonote::blobdata;
using block_pair_t = std::pair<blob_t, block>;
/// pick a random byte from the block blob
std::vector<block_pair_t> blocks;
std::vector<blob_t> txs;
if (!core.get_blockchain_storage().get_blocks(height, 1, blocks, txs)) {
MERROR("Could not query block at requested height: " << height);
return 0;
}
const blob_t &blob = blocks.at(0).first;
const uint64_t byte_idx = tools::uniform_distribution_portable(mt, blob.size());
uint8_t byte = blob[byte_idx];
/// pick a random byte from a random transaction blob if found
if (!txs.empty()) {
const uint64_t tx_idx = tools::uniform_distribution_portable(mt, txs.size());
const blob_t &tx_blob = txs[tx_idx];
/// not sure if this can be empty, so check to be safe
if (!tx_blob.empty()) {
const uint64_t byte_idx = tools::uniform_distribution_portable(mt, tx_blob.size());
const uint8_t tx_byte = tx_blob[byte_idx];
byte ^= tx_byte;
}
}
{
/// reduce hash down to 8 bytes
uint64_t n[4];
std::memcpy(n, hash.data, sizeof(n));
for (auto &ni : n) {
boost::endian::little_to_native_inplace(ni);
}
/// Note that byte (obviously) only affects the lower byte
/// of height, but that should be sufficient in this case
height = n[0] ^ n[1] ^ n[2] ^ n[3] ^ byte;
}
}
return height;
}
//------------------------------------------------------------------------------------------------------------------------------
PERFORM_BLOCKCHAIN_TEST::response core_rpc_server::invoke(PERFORM_BLOCKCHAIN_TEST::request&& req, rpc_context context)
{
PERFORM_BLOCKCHAIN_TEST::response res{};
PERF_TIMER(on_perform_blockchain_test);
uint64_t max_height = req.max_height;
uint64_t seed = req.seed;
if (m_core.get_current_blockchain_height() <= max_height)
throw rpc_error{ERROR_TOO_BIG_HEIGHT, "Requested block height too big."};
uint64_t res_height = perform_blockchain_test_routine(m_core, max_height, seed);
res.status = STATUS_OK;
res.res_height = res_height;
return res;
}
namespace {
struct version_printer { const std::array<uint16_t, 3> &v; };
@ -3318,12 +3229,13 @@ namespace cryptonote { namespace rpc {
//------------------------------------------------------------------------------------------------------------------------------
STORAGE_SERVER_PING::response core_rpc_server::invoke(STORAGE_SERVER_PING::request&& req, rpc_context context)
{
m_core.ss_version = {req.version_major, req.version_minor, req.version_patch};
m_core.ss_version = req.version;
return handle_ping<STORAGE_SERVER_PING>(
{req.version_major, req.version_minor, req.version_patch}, service_nodes::MIN_STORAGE_SERVER_VERSION,
req.version, service_nodes::MIN_STORAGE_SERVER_VERSION,
"Storage Server", m_core.m_last_storage_server_ping, m_core.get_net_config().UPTIME_PROOF_FREQUENCY,
[this, &req](bool significant) {
m_core.m_storage_lmq_port = req.storage_lmq_port;
m_core.m_storage_https_port = req.https_port;
m_core.m_storage_omq_port = req.omq_port;
if (significant)
m_core.reset_proof_interval();
});

View File

@ -89,7 +89,7 @@ namespace cryptonote::rpc {
/// Junk that epee makes us deal with to pass in a generically parsed json value
using jsonrpc_params = std::pair<epee::serialization::portable_storage, epee::serialization::storage_entry>;
enum struct rpc_source : uint8_t { internal, http, lmq };
enum struct rpc_source : uint8_t { internal, http, omq };
/// Contains the context of the invocation, which must be filled out by the glue code (e.g. HTTP
/// RPC server) with requester-specific context details.
@ -260,7 +260,6 @@ namespace cryptonote::rpc {
GET_SERVICE_NODE_STATUS::response invoke(GET_SERVICE_NODE_STATUS::request&& req, rpc_context context);
GET_SERVICE_NODES::response invoke(GET_SERVICE_NODES::request&& req, rpc_context context);
GET_STAKING_REQUIREMENT::response invoke(GET_STAKING_REQUIREMENT::request&& req, rpc_context context);
PERFORM_BLOCKCHAIN_TEST::response invoke(PERFORM_BLOCKCHAIN_TEST::request&& req, rpc_context context);
STORAGE_SERVER_PING::response invoke(STORAGE_SERVER_PING::request&& req, rpc_context context);
LOKINET_PING::response invoke(LOKINET_PING::request&& req, rpc_context context);
GET_CHECKPOINTS::response invoke(GET_CHECKPOINTS::request&& req, rpc_context context);

View File

@ -1065,18 +1065,6 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_SERVICE_PRIVKEYS::response)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(PERFORM_BLOCKCHAIN_TEST::request)
KV_SERIALIZE(max_height)
KV_SERIALIZE(seed)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(PERFORM_BLOCKCHAIN_TEST::response)
KV_SERIALIZE(status)
KV_SERIALIZE(res_height)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(service_node_contribution)
KV_SERIALIZE(key_image)
KV_SERIALIZE(key_image_pub_key)
@ -1211,10 +1199,9 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(STORAGE_SERVER_PING::request)
KV_SERIALIZE(version_major);
KV_SERIALIZE(version_minor);
KV_SERIALIZE(version_patch);
KV_SERIALIZE(storage_lmq_port);
KV_SERIALIZE(version);
KV_SERIALIZE(https_port);
KV_SERIALIZE(omq_port);
KV_SERIALIZE_MAP_CODE_END()

View File

@ -1988,29 +1988,6 @@ namespace rpc {
};
};
OXEN_RPC_DOC_INTROSPECT
// TODO: Undocumented, -- unused
struct PERFORM_BLOCKCHAIN_TEST : RPC_COMMAND
{
static constexpr auto names() { return NAMES("perform_blockchain_test"); }
struct request
{
uint64_t max_height;
uint64_t seed;
KV_MAP_SERIALIZABLE
};
struct response
{
std::string status;
uint64_t res_height;
KV_MAP_SERIALIZABLE
};
};
OXEN_RPC_DOC_INTROSPECT
struct service_node_contribution
{
@ -2193,10 +2170,9 @@ namespace rpc {
struct request
{
uint16_t version_major; // Storage Server Major version
uint16_t version_minor; // Storage Server Minor version
uint16_t version_patch; // Storage Server Patch version
uint16_t storage_lmq_port; // Storage Server lmq port to include in uptime proofs
std::array<uint16_t, 3> version; // Storage server version
uint16_t https_port; // Storage server https port to include in uptime proofs
uint16_t omq_port; // Storage Server oxenmq port to include in uptime proofs
KV_MAP_SERIALIZABLE
};
@ -2647,7 +2623,6 @@ namespace rpc {
GET_SERVICE_NODE_REGISTRATION_CMD,
GET_SERVICE_KEYS,
GET_SERVICE_PRIVKEYS,
PERFORM_BLOCKCHAIN_TEST,
GET_SERVICE_NODES,
GET_SERVICE_NODE_STATUS,
STORAGE_SERVER_PING,

View File

@ -492,11 +492,11 @@ namespace cryptonote::rpc {
if (!done)
return;
auto& lmq = data->core_rpc.get_core().get_lmq();
auto& omq = data->core_rpc.get_core().get_omq();
std::string cat{data->call->is_public ? "rpc" : "admin"};
std::string cmd{"http:" + data->uri}; // Used for LMQ job logging; prefixed with http: so we can distinguish it
std::string remote{data->request.context.remote};
lmq.inject_task(std::move(cat), std::move(cmd), std::move(remote), [data=std::move(data)] { invoke_rpc(std::move(data)); });
omq.inject_task(std::move(cat), std::move(cmd), std::move(remote), [data=std::move(data)] { invoke_rpc(std::move(data)); });
});
}
@ -565,11 +565,11 @@ namespace cryptonote::rpc {
if (!ps.get_value("params", st_entry, nullptr))
data->request.body = ""sv;
auto& lmq = data->core_rpc.get_core().get_lmq();
auto& omq = data->core_rpc.get_core().get_omq();
std::string cat{data->call->is_public ? "rpc" : "admin"};
std::string cmd{"jsonrpc:" + method}; // Used for LMQ job logging; prefixed with jsonrpc: so we can distinguish it
std::string remote{data->request.context.remote};
lmq.inject_task(std::move(cat), std::move(cmd), std::move(remote), [data=std::move(data)] { invoke_rpc(std::move(data)); });
omq.inject_task(std::move(cat), std::move(cmd), std::move(remote), [data=std::move(data)] { invoke_rpc(std::move(data)); });
});
}
@ -588,9 +588,9 @@ namespace cryptonote::rpc {
m_sent_startup = true;
m_listen_socks = m_startup_success.get();
auto& lmq = m_server.get_core().get_lmq();
if (timer_started.insert(&lmq).second)
lmq.add_timer(long_poll_process_timeouts, 1s);
auto& omq = m_server.get_core().get_omq();
if (timer_started.insert(&omq).second)
omq.add_timer(long_poll_process_timeouts, 1s);
}
void http_server::shutdown(bool join)

View File

@ -11,29 +11,32 @@ using oxenmq::AuthLevel;
namespace {
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_public{
// TODO: all of this --lmq-blah options really should be renamed to --omq-blah, but then we *also*
// need some sort of backwards compatibility shim, and that is a nuissance.
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_public{
"lmq-public",
"Adds a public, unencrypted OxenMQ RPC listener (with restricted capabilities) at the given "
"address; can be specified multiple times. Examples: tcp://0.0.0.0:5555 (listen on port 5555), "
"tcp://198.51.100.42:5555 (port 5555 on specific IPv4 address), tcp://[::]:5555, "
"tcp://[2001:db8::abc]:5555 (IPv6), or ipc:///path/to/socket to listen on a unix domain socket"};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_curve_public{
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_curve_public{
"lmq-curve-public",
"Adds a curve-encrypted OxenMQ RPC listener at the given address that accepts (restricted) rpc "
"commands from any client. Clients must already know this server's public x25519 key to "
"establish an encrypted connection."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_curve{
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_curve{
"lmq-curve",
"Adds a curve-encrypted OxenMQ RPC listener at the given address that only accepts client connections from whitelisted client x25519 pubkeys. "
"Clients must already know this server's public x25519 key to establish an encrypted connection. When running in service node mode "
"the quorumnet port is already listening as if specified with --lmq-curve."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_admin{
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_admin{
"lmq-admin",
"Adds an x25519 pubkey of a client permitted to connect to the --lmq-curve, --lmq-curve-public, or quorumnet address(es) with unrestricted (admin) capabilities."};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_user{
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_user{
"lmq-user",
"Specifies an x25519 pubkey of a client permitted to connect to the --lmq-curve or quorumnet address(es) with restricted capabilities"};
const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_local_control{
const command_line::arg_descriptor<std::vector<std::string>> arg_omq_local_control{
"lmq-local-control",
"Adds an unencrypted OxenMQ RPC listener with full, unrestricted capabilities and no authentication at the given address. "
#ifndef _WIN32
@ -42,18 +45,18 @@ const command_line::arg_descriptor<std::vector<std::string>> arg_lmq_local_contr
"WARNING: Do not use this on a publicly accessible address!"};
#ifndef _WIN32
const command_line::arg_descriptor<std::string> arg_lmq_umask{
const command_line::arg_descriptor<std::string> arg_omq_umask{
"lmq-umask",
"Sets the umask to apply to any listening ipc:///path/to/sock LMQ sockets, in octal.",
"0007"};
#endif
void check_lmq_listen_addr(std::string_view addr) {
void check_omq_listen_addr(std::string_view addr) {
// Crude check for basic validity; you can specify all sorts of invalid things, but at least
// we can check the prefix for something that looks zmq-y.
if (addr.size() < 7 || (addr.substr(0, 6) != "tcp://" && addr.substr(0, 6) != "ipc://"))
throw std::runtime_error("Error: lmq listen address '" + std::string(addr) + "' is invalid: expected tcp://IP:PORT, tcp://[IPv6]:PORT or ipc:///path/to/socket");
throw std::runtime_error("Error: omq listen address '" + std::string(addr) + "' is invalid: expected tcp://IP:PORT, tcp://[IPv6]:PORT or ipc:///path/to/socket");
}
@ -80,49 +83,49 @@ constexpr std::string_view
} // end anonymous namespace
void init_lmq_options(boost::program_options::options_description& desc)
void init_omq_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_lmq_public);
command_line::add_arg(desc, arg_lmq_curve_public);
command_line::add_arg(desc, arg_lmq_curve);
command_line::add_arg(desc, arg_lmq_admin);
command_line::add_arg(desc, arg_lmq_user);
command_line::add_arg(desc, arg_lmq_local_control);
command_line::add_arg(desc, arg_omq_public);
command_line::add_arg(desc, arg_omq_curve_public);
command_line::add_arg(desc, arg_omq_curve);
command_line::add_arg(desc, arg_omq_admin);
command_line::add_arg(desc, arg_omq_user);
command_line::add_arg(desc, arg_omq_local_control);
#ifndef _WIN32
command_line::add_arg(desc, arg_lmq_umask);
command_line::add_arg(desc, arg_omq_umask);
#endif
}
lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm)
omq_rpc::omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm)
: core_{core}, rpc_{rpc}
{
auto& lmq = core.get_lmq();
auto& auth = core._lmq_auth_level_map();
auto& omq = core.get_omq();
auto& auth = core._omq_auth_level_map();
// Set up any requested listening sockets. (Note: if we are a service node, we'll already have
// the quorumnet listener set up in cryptonote_core).
for (const auto &addr : command_line::get_arg(vm, arg_lmq_public)) {
check_lmq_listen_addr(addr);
for (const auto &addr : command_line::get_arg(vm, arg_omq_public)) {
check_omq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (public unencrypted)");
lmq.listen_plain(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::basic); });
omq.listen_plain(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.omq_allow(ip, pk, AuthLevel::basic); });
}
for (const auto &addr : command_line::get_arg(vm, arg_lmq_curve_public)) {
check_lmq_listen_addr(addr);
for (const auto &addr : command_line::get_arg(vm, arg_omq_curve_public)) {
check_omq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (public curve)");
lmq.listen_curve(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::basic); });
omq.listen_curve(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.omq_allow(ip, pk, AuthLevel::basic); });
}
for (const auto &addr : command_line::get_arg(vm, arg_lmq_curve)) {
check_lmq_listen_addr(addr);
for (const auto &addr : command_line::get_arg(vm, arg_omq_curve)) {
check_omq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (curve restricted)");
lmq.listen_curve(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::denied); });
omq.listen_curve(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.omq_allow(ip, pk, AuthLevel::denied); });
}
auto locals = command_line::get_arg(vm, arg_lmq_local_control);
auto locals = command_line::get_arg(vm, arg_omq_local_control);
if (locals.empty()) {
// FIXME: this requires unix sockets and so probably won't work on older Windows 10 or pre-Win10
// windows. In theory we could do some runtime detection to see if the Windows version is new
@ -138,14 +141,14 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
locals.clear();
}
for (const auto &addr : locals) {
check_lmq_listen_addr(addr);
check_omq_listen_addr(addr);
MGINFO("LMQ listening on " << addr << " (unauthenticated local admin)");
lmq.listen_plain(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.lmq_allow(ip, pk, AuthLevel::admin); });
omq.listen_plain(addr,
[&core](std::string_view ip, std::string_view pk, bool /*sn*/) { return core.omq_allow(ip, pk, AuthLevel::admin); });
}
#ifndef _WIN32
auto umask_str = command_line::get_arg(vm, arg_lmq_umask);
auto umask_str = command_line::get_arg(vm, arg_omq_umask);
try {
int umask = -1;
size_t len = 0;
@ -154,7 +157,7 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
throw std::invalid_argument("not an octal value");
if (umask < 0 || umask > 0777)
throw std::invalid_argument("invalid umask value");
lmq.STARTUP_UMASK = umask;
omq.STARTUP_UMASK = umask;
} catch (const std::exception& e) {
throw std::invalid_argument("Invalid --lmq-umask value '" + umask_str + "': value must be an octal value between 0 and 0777");
}
@ -164,27 +167,27 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
// Insert our own pubkey so that, e.g., console commands from localhost automatically get full access
{
crypto::x25519_public_key my_pubkey;
const std::string& pk = lmq.get_pubkey();
const std::string& pk = omq.get_pubkey();
std::copy(pk.begin(), pk.end(), my_pubkey.data);
auth.emplace(std::move(my_pubkey), AuthLevel::admin);
}
// User-specified admin/user pubkeys
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_lmq_admin)))
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_omq_admin)))
auth.emplace(std::move(pk), AuthLevel::admin);
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_lmq_user)))
for (auto& pk : as_x_pubkeys(command_line::get_arg(vm, arg_omq_user)))
auth.emplace(std::move(pk), AuthLevel::basic);
// basic (non-admin) rpc commands go into the "rpc." category (e.g. 'rpc.get_info')
lmq.add_category("rpc", AuthLevel::basic, 0 /*no reserved threads*/, 1000 /*max queued requests*/);
omq.add_category("rpc", AuthLevel::basic, 0 /*no reserved threads*/, 1000 /*max queued requests*/);
// Admin rpc commands go into "admin.". We also always keep one (potential) thread reserved for
// admin RPC commands; that way even if there are loads of basic commands being processed we'll
// still have room to invoke an admin command without waiting for the basic ones to finish.
constexpr unsigned int admin_reserved_threads = 1;
lmq.add_category("admin", AuthLevel::admin, admin_reserved_threads);
omq.add_category("admin", AuthLevel::admin, admin_reserved_threads);
for (auto& cmd : rpc_commands) {
lmq.add_request_command(cmd.second->is_public ? "rpc" : "admin", cmd.first,
omq.add_request_command(cmd.second->is_public ? "rpc" : "admin", cmd.first,
[name=std::string_view{cmd.first}, &call=*cmd.second, this](oxenmq::Message& m) {
if (m.data.size() > 1)
m.send_reply(LMQ_BAD_REQUEST, "Bad request: RPC commands must have at most one data part "
@ -192,7 +195,7 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
rpc_request request{};
request.context.admin = m.access.auth >= AuthLevel::admin;
request.context.source = rpc_source::lmq;
request.context.source = rpc_source::omq;
request.context.remote = m.remote;
request.body = m.data.empty() ? ""sv : m.data[0];
@ -231,7 +234,7 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
// The "subscribe" category is for public subscriptions; i.e. anyone on a public RPC node, or
// anyone on a private RPC node with public access level.
lmq.add_category("sub", AuthLevel::basic);
omq.add_category("sub", AuthLevel::basic);
// TX mempool subscriptions: [sub.mempool, blink] or [sub.mempool, all] to subscribe to new
// approved mempool blink txes, or to all new mempool txes. You get back a reply of "OK" or
@ -250,7 +253,7 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
// such as txes that came from an existing block during a rollback). Note that both txhash and
// txblob are binary: in particular, txhash is *not* hex-encoded.
//
lmq.add_request_command("sub", "mempool", [this](oxenmq::Message& m) {
omq.add_request_command("sub", "mempool", [this](oxenmq::Message& m) {
if (m.data.size() != 1) {
m.send_reply("Invalid subscription request: no subscription type given");
@ -296,7 +299,7 @@ lmq_rpc::lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::prog
// The block notification for new blocks consists of a message [notify.block, height, blockhash]
// containing the latest height/hash. (Note that blockhash is the hash in bytes, *not* the hex
// encoded block hash).
lmq.add_request_command("sub", "block", [this](oxenmq::Message& m) {
omq.add_request_command("sub", "block", [this](oxenmq::Message& m) {
std::unique_lock lock{subs_mutex_};
auto expiry = std::chrono::steady_clock::now() + 30min;
auto result = block_subs_.emplace(m.conn, block_sub{expiry});
@ -352,23 +355,23 @@ static void send_notifies(Mutex& mutex, Subs& subs, const char* desc, Call call)
}
}
bool lmq_rpc::block_added(const block& block, const std::vector<transaction>& txs, const checkpoint_t *)
bool omq_rpc::block_added(const block& block, const std::vector<transaction>& txs, const checkpoint_t *)
{
auto& lmq = core_.get_lmq();
auto& omq = core_.get_omq();
std::string height = std::to_string(get_block_height(block));
send_notifies(subs_mutex_, block_subs_, "block", [&](auto& conn, auto& sub) {
lmq.send(conn, "notify.block", height, std::string_view{block.hash.data, sizeof(block.hash.data)});
omq.send(conn, "notify.block", height, std::string_view{block.hash.data, sizeof(block.hash.data)});
});
return true;
}
void lmq_rpc::send_mempool_notifications(const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts)
void omq_rpc::send_mempool_notifications(const crypto::hash& id, const transaction& tx, const std::string& blob, const tx_pool_options& opts)
{
auto& lmq = core_.get_lmq();
auto& omq = core_.get_omq();
send_notifies(subs_mutex_, mempool_subs_, "mempool", [&](auto& conn, auto& sub) {
if (sub.type == mempool_sub_type::all || opts.approved_blink)
lmq.send(conn, "notify.mempool", std::string_view{id.data, sizeof(id.data)}, blob);
omq.send(conn, "notify.mempool", std::string_view{id.data, sizeof(id.data)}, blob);
});
}

View File

@ -37,14 +37,14 @@ namespace oxenmq { class OxenMQ; }
namespace cryptonote { namespace rpc {
void init_lmq_options(boost::program_options::options_description& desc);
void init_omq_options(boost::program_options::options_description& desc);
/**
* LMQ RPC server class. This doesn't actually hold the OxenMQ instance--that's in
* cryptonote_core--but it works with it to add RPC endpoints, make it listen on RPC ports, and
* handles RPC requests.
*/
class lmq_rpc final : public cryptonote::BlockAddedHook {
class omq_rpc final : public cryptonote::BlockAddedHook {
enum class mempool_sub_type { all, blink };
struct mempool_sub {
@ -63,7 +63,7 @@ class lmq_rpc final : public cryptonote::BlockAddedHook {
std::unordered_map<oxenmq::ConnectionID, block_sub> block_subs_;
public:
lmq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm);
omq_rpc(cryptonote::core& core, core_rpc_server& rpc, const boost::program_options::variables_map& vm);
bool block_added(const block& block, const std::vector<transaction>& txs, const checkpoint_t *) override;