Merge pull request #1370 from darcys22/1366-uptime-proof-version-details

Uptime proof version details + bt-encoded proofs
This commit is contained in:
Jason Rhinelander 2021-03-01 17:36:35 -04:00 committed by GitHub
commit 4c1d03576e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 708 additions and 96 deletions

View File

@ -33,6 +33,8 @@
#include <boost/endian/conversion.hpp>
#include <memory>
#include <cstring>
#include <type_traits>
#include <variant>
#include "epee/string_tools.h"
#include "common/file.h"
@ -46,6 +48,7 @@
#include "checkpoints/checkpoints.h"
#include "cryptonote_core/service_node_rules.h"
#include "cryptonote_core/service_node_list.h"
#include "cryptonote_core/uptime_proof.h"
#include "cryptonote_basic/hardfork.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
@ -269,8 +272,15 @@ void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const
throw0(cryptonote::DB_OPEN_FAILURE((lmdb_error(error_string + " : ", res) + std::string(" - you may want to start with --db-salvage")).c_str()));
}
template <typename T, typename...>
struct first_type { using type = T; };
template <typename... T>
using first_type_t = typename first_type<T...>::type;
// Lets you iterator over all the pairs of K/V pairs in a database
template <typename K, typename V>
// If multiple V are provided then value will be a variant<V1*, V2*, ...> with the populated pointer
// matched by size of the record.
template <typename K, typename... V>
class iterable_db {
private:
MDB_cursor* cursor;
@ -281,13 +291,13 @@ public:
class iterator {
public:
using value_type = std::pair<K*, V*>;
using value_type = std::pair<K*, std::conditional_t<sizeof...(V) == 1, first_type_t<V...>*, std::variant<V*...>>>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::input_iterator_tag;
constexpr iterator() : element{nullptr, nullptr} {}
constexpr iterator() : element{} {}
iterator(MDB_cursor* c, MDB_cursor_op op_start, MDB_cursor_op op_incr) : cursor{c}, op_incr{op_incr} {
next(op_start);
}
@ -302,14 +312,29 @@ public:
bool operator!=(const iterator& i) const { return !(*this == i); }
private:
template <typename T, typename... More>
void load_variant() {
if (v.mv_size == sizeof(T))
element.second = static_cast<T*>(v.mv_data);
else if constexpr (sizeof...(More))
load_variant<More...>();
else {
MWARNING("Invalid stored type size in iterable_db: stored size (" << v.mv_size <<
") matched none of " << tools::type_name<value_type>());
var::get<0>(element.second) = nullptr;
}
}
void next(MDB_cursor_op op) {
int result = mdb_cursor_get(cursor, &k, &v, op);
if (result == MDB_NOTFOUND) {
element.first = nullptr;
element.second = nullptr;
element = {};
} else if (result == MDB_SUCCESS) {
element.first = static_cast<K*>(k.mv_data);
element.second = static_cast<V*>(v.mv_data);
if constexpr (sizeof...(V) == 1)
element.second = static_cast<typename value_type::second_type*>(v.mv_data);
else
load_variant<V...>();
} else {
throw0(cryptonote::DB_ERROR(lmdb_error("enumeration failed: ", result)));
}
@ -318,7 +343,7 @@ public:
MDB_cursor* cursor = nullptr;
const MDB_cursor_op op_incr = MDB_NEXT;
MDB_val k, v;
std::pair<K*, V*> element;
value_type element;
};
iterator begin() { return {cursor, op_start, op_incr}; }
@ -6240,31 +6265,47 @@ void BlockchainLMDB::clear_service_node_data()
}
}
struct service_node_proof_serialized
template <typename C>
C native_to_little_container(const C& c) {
C result{c};
for (auto& x : result) native_to_little_inplace(x);
return result;
}
template <typename C>
C little_to_native_container(const C& c) {
C result{c};
for (auto& x : result) little_to_native_inplace(x);
return result;
}
struct service_node_proof_serialized_old
{
service_node_proof_serialized() = default;
service_node_proof_serialized(const service_nodes::proof_info &info)
: timestamp{native_to_little(info.timestamp)},
ip{native_to_little(info.public_ip)},
storage_port{native_to_little(info.storage_port)},
storage_lmq_port{native_to_little(info.storage_lmq_port)},
quorumnet_port{native_to_little(info.quorumnet_port)},
version{native_to_little(info.version[0]), native_to_little(info.version[1]), native_to_little(info.version[2])},
pubkey_ed25519{info.pubkey_ed25519}
service_node_proof_serialized_old() = default;
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)},
quorumnet_port{native_to_little(info.proof->qnet_port)},
version{native_to_little_container(info.proof->version)},
pubkey_ed25519{info.proof->pubkey_ed25519}
{}
void update(service_nodes::proof_info &info) const
{
info.timestamp = little_to_native(timestamp);
if (info.timestamp > info.effective_timestamp)
info.effective_timestamp = info.timestamp;
info.public_ip = little_to_native(ip);
info.storage_port = little_to_native(storage_port);
info.storage_lmq_port = little_to_native(storage_lmq_port);
info.quorumnet_port = little_to_native(quorumnet_port);
for (size_t i = 0; i < info.version.size(); i++)
info.version[i] = little_to_native(version[i]);
info.proof->timestamp = little_to_native(timestamp);
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->qnet_port = little_to_native(quorumnet_port);
info.proof->version = little_to_native_container(version);
info.proof->storage_server_version = {0, 0, 0};
info.proof->lokinet_version = {0, 0, 0};
info.update_pubkey(pubkey_ed25519);
}
operator service_nodes::proof_info() const
{
service_nodes::proof_info info{};
@ -6276,11 +6317,39 @@ struct service_node_proof_serialized
uint32_t ip;
uint16_t storage_port;
uint16_t quorumnet_port;
uint16_t version[3];
std::array<uint16_t, 3> version;
uint16_t storage_lmq_port;
crypto::ed25519_public_key pubkey_ed25519;
};
static_assert(sizeof(service_node_proof_serialized) == 56, "service node serialization struct has unexpected size and/or padding");
static_assert(sizeof(service_node_proof_serialized_old) == 56, "service node serialization struct has unexpected size and/or padding");
struct service_node_proof_serialized : service_node_proof_serialized_old {
service_node_proof_serialized() = default;
service_node_proof_serialized(const service_nodes::proof_info &info)
: service_node_proof_serialized_old{info},
storage_server_version{native_to_little_container(info.proof->storage_server_version)},
lokinet_version{native_to_little_container(info.proof->lokinet_version)}
{}
std::array<uint16_t, 3> storage_server_version;
std::array<uint16_t, 3> lokinet_version;
char _padding[4];
void update(service_nodes::proof_info& info) const {
if (!info.proof) info.proof = std::unique_ptr<uptime_proof::Proof>(new uptime_proof::Proof());
service_node_proof_serialized_old::update(info);
info.proof->storage_server_version = little_to_native_container(storage_server_version);
info.proof->lokinet_version = little_to_native_container(lokinet_version);
}
operator service_nodes::proof_info() const
{
service_nodes::proof_info info{};
update(info);
return info;
}
};
static_assert(sizeof(service_node_proof_serialized) == 72, "service node serialization struct has unexpected size and/or padding");
bool BlockchainLMDB::get_service_node_proof(const crypto::public_key &pubkey, service_nodes::proof_info &proof) const
{
@ -6298,7 +6367,11 @@ bool BlockchainLMDB::get_service_node_proof(const crypto::public_key &pubkey, se
else if (result != MDB_SUCCESS)
throw0(DB_ERROR(lmdb_error("DB error attempting to get service node data", result)));
static_cast<const service_node_proof_serialized *>(v.mv_data)->update(proof);
if (v.mv_size == sizeof(service_node_proof_serialized_old))
static_cast<const service_node_proof_serialized_old*>(v.mv_data)->update(proof);
else
static_cast<const service_node_proof_serialized*>(v.mv_data)->update(proof);
return true;
}
@ -6328,8 +6401,13 @@ std::unordered_map<crypto::public_key, service_nodes::proof_info> BlockchainLMDB
RCURSOR(service_node_proofs);
std::unordered_map<crypto::public_key, service_nodes::proof_info> result;
for (const auto &pair : iterable_db<crypto::public_key, service_node_proof_serialized>(m_cursors->service_node_proofs))
result.emplace(*pair.first, *pair.second);
for (const auto &pair : iterable_db<crypto::public_key, service_node_proof_serialized, service_node_proof_serialized_old>(
m_cursors->service_node_proofs)) {
if (std::holds_alternative<service_node_proof_serialized*>(pair.second))
result.emplace(*pair.first, *var::get<service_node_proof_serialized*>(pair.second));
else
result.emplace(*pair.first, service_node_proof_serialized{*var::get<service_node_proof_serialized_old*>(pair.second)});
}
return result;
}

View File

@ -45,6 +45,7 @@
#include "blockchain_objects.h"
#include "blockchain_db/blockchain_db.h"
#include "version.h"
#include "cryptonote_core/uptime_proof.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "bcutil"

View File

@ -45,6 +45,7 @@
#include "blockchain_db/blockchain_db.h"
#include "wallet/ringdb.h"
#include "version.h"
#include "cryptonote_core/uptime_proof.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "bcutil"

View File

@ -31,6 +31,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_objects.h"
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_core/uptime_proof.h"
#include "version.h"
#undef OXEN_DEFAULT_LOG_CATEGORY

View File

@ -33,6 +33,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_objects.h"
#include "version.h"
#include "cryptonote_core/uptime_proof.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "bcutil"

View File

@ -41,6 +41,7 @@
#include "blocks/blocks.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "serialization/binary_utils.h"
#include "cryptonote_core/uptime_proof.h"
#include "cryptonote_core/cryptonote_core.h"
#include "common/hex.h"

View File

@ -36,6 +36,7 @@
#include "blockchain_db/blockchain_db.h"
#include "version.h"
#include "epee/misc_os_dependent.h"
#include "cryptonote_core/uptime_proof.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "bcutil"

View File

@ -32,6 +32,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_objects.h"
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_core/uptime_proof.h"
#include "version.h"
#undef OXEN_DEFAULT_LOG_CATEGORY

View File

@ -1,6 +1,7 @@
#pragma once
#include <string_view>
#include <vector>
#include <cstring>
#include <iterator>
#include <charconv>
#include <sstream>
@ -109,6 +110,19 @@ std::string copy_guts(const T& val) {
return std::string{view_guts(val)};
}
/// Function to reverse the above view_guts
template <typename T>
T make_from_guts(std::string_view s) {
static_assert((std::is_standard_layout_v<T> && std::has_unique_object_representations_v<T>)
|| epee::is_byte_spannable<T>,
"cannot safely reconstitute a non-trivial class from data");
if (s.size() != sizeof(T))
throw std::runtime_error("Cannot reconstitute type: wrong type content size");
T x;
std::memcpy(&x, s.data(), sizeof(T));
return x;
}
std::string lowercase_ascii_string(std::string_view src);
/// Converts a duration into a human friendlier string.

View File

@ -194,6 +194,7 @@ constexpr uint64_t BLOCKS_EXPECTED_IN_YEARS(int years) { return BLOCKS_EXPECTED_
#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY cryptonote::network_version_16_pulse
#define HF_VERSION_PULSE cryptonote::network_version_16_pulse
#define HF_VERSION_CLSAG cryptonote::network_version_16_pulse
#define HF_VERSION_PROOF_BTENC cryptonote::network_version_17
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8

View File

@ -40,7 +40,8 @@ add_library(cryptonote_core
tx_pool.cpp
tx_sanity_check.cpp
cryptonote_tx_utils.cpp
pulse.cpp)
pulse.cpp
uptime_proof.cpp)
target_link_libraries(cryptonote_core
PUBLIC

View File

@ -49,6 +49,7 @@ extern "C" {
#include <sqlite3.h>
#include "cryptonote_core.h"
#include "uptime_proof.h"
#include "common/file.h"
#include "common/sha256sum.h"
#include "common/threadpool.h"
@ -1670,7 +1671,7 @@ namespace cryptonote
std::array<uint16_t,3> proofversion;
m_service_node_list.access_proof(pubkey, [&](auto &proof) {
x_pkey = proof.pubkey_x25519;
proofversion = proof.version;
proofversion = proof.proof->version;
});
if (proofversion >= MIN_TIMESTAMP_VERSION && x_pkey) {
@ -1930,10 +1931,19 @@ namespace cryptonote
if (!m_service_node)
return true;
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);
cryptonote_connection_context fake_context{};
bool relayed = get_protocol()->relay_uptime_proof(req, fake_context);
bool relayed;
auto height = get_current_blockchain_height();
auto hf_version = get_hard_fork_version(height);
//TODO: remove after HF17
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);
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);
NOTIFY_BTENCODED_UPTIME_PROOF::request req = proof.generate_request();
relayed = get_protocol()->relay_btencoded_uptime_proof(req, fake_context);
}
if (relayed)
MGINFO("Submitted uptime-proof for Service Node (yours): " << m_service_keys.pub);
@ -1953,6 +1963,23 @@ namespace cryptonote
return result;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_btencoded_uptime_proof(const NOTIFY_BTENCODED_UPTIME_PROOF::request &req, bool &my_uptime_proof_confirmation)
{
crypto::x25519_public_key pkey = {};
auto proof = std::make_unique<uptime_proof::Proof>(req.proof);
proof->sig = tools::make_from_guts<crypto::signature>(req.sig);
proof->sig_ed25519 = tools::make_from_guts<crypto::ed25519_signature>(req.ed_sig);
auto pubkey = proof->pubkey;
bool result = m_service_node_list.handle_btencoded_uptime_proof(std::move(proof), my_uptime_proof_confirmation, pkey);
if (result && m_service_node_list.is_service_node(pubkey, true /*require_active*/) && pkey)
{
oxenmq::pubkey_set added;
added.insert(tools::copy_guts(pkey));
m_lmq->update_active_sns(added, {} /*removed*/);
}
return result;
}
//-----------------------------------------------------------------------------------------------
crypto::hash core::on_transaction_relayed(const cryptonote::blobdata& tx_blob)
{
std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
@ -2301,12 +2328,12 @@ namespace cryptonote
sn_pks.push_back(sni.pubkey);
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.public_ip == m_sn_public_ip &&
(proof.quorumnet_port == m_quorumnet_port || proof.storage_port == m_storage_port || proof.storage_port == m_storage_lmq_port))
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))
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.quorumnet_port << "[qnet], :" <<
proof.storage_port << "[SS-HTTP], :" << proof.storage_lmq_port << "[SS-LMQ]). "
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]). "
"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

@ -159,6 +159,15 @@ namespace cryptonote
*/
bool handle_uptime_proof(const NOTIFY_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation);
/**
* @brief handles an incoming uptime proof that is encoded using B-encoding
*
* Parses an incoming uptime proof
*
* @return true if we haven't seen it before and thus need to relay.
*/
bool handle_btencoded_uptime_proof(const NOTIFY_BTENCODED_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation);
/**
* @brief handles an incoming transaction
*
@ -1163,6 +1172,10 @@ namespace cryptonote
std::unordered_map<crypto::x25519_public_key, oxenmq::AuthLevel>& _lmq_auth_level_map() { return m_lmq_auth; }
oxenmq::TaggedThreadID const &pulse_thread_id() const { return *m_pulse_thread_id; }
/// Service Node's storage server and lokinet version
std::array<uint16_t, 3> ss_version;
std::array<uint16_t, 3> lokinet_version;
private:
/**

View File

@ -43,6 +43,7 @@ extern "C" {
#include "cryptonote_tx_utils.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_core/uptime_proof.h"
#include "epee/int-util.h"
#include "common/scoped_message_writer.h"
#include "common/i18n.h"
@ -56,8 +57,10 @@ extern "C" {
#include "pulse.h"
#include "service_node_list.h"
#include "uptime_proof.h"
#include "service_node_rules.h"
#include "service_node_swarm.h"
#include <lokimq/bt_serialize.h>
#include "version.h"
#undef OXEN_DEFAULT_LOG_CATEGORY
@ -2743,15 +2746,14 @@ namespace service_nodes
return true;
}
static crypto::hash hash_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof, uint8_t hf_version)
//TODO: remove after HF17
crypto::hash service_node_list::hash_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof) const
{
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);
size_t buf_size = buf.size();
if (hf_version < cryptonote::network_version_15_lns) // TODO - can be removed post-HF15
buf_size -= sizeof(proof.storage_lmq_port);
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);
buf_size = buf.size();
crypto::cn_fast_hash(buf.data(), buf_size, result);
return result;
}
@ -2771,12 +2773,18 @@ namespace service_nodes
result.qnet_port = quorumnet_port;
result.pubkey_ed25519 = keys.pub_ed25519;
crypto::hash hash = hash_uptime_proof(result, m_blockchain.get_current_hard_fork_version());
crypto::hash hash = hash_uptime_proof(result);
crypto::generate_signature(hash, keys.pub, keys.key, result.sig);
crypto_sign_detached(result.sig_ed25519.data, NULL, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), keys.key_ed25519.data);
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
{
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);
}
#ifdef __cpp_lib_erase_if // # (C++20)
using std::erase_if;
#else
@ -2800,13 +2808,48 @@ namespace service_nodes
return false;
}
proof_info::proof_info()
: proof(std::make_unique<uptime_proof::Proof>()) {};
void proof_info::store(const crypto::public_key &pubkey, cryptonote::Blockchain &blockchain)
{
if (!proof) proof = std::unique_ptr<uptime_proof::Proof>(new uptime_proof::Proof());
std::unique_lock lock{blockchain};
auto &db = blockchain.get_db();
db.set_service_node_proof(pubkey, *this);
}
bool proof_info::update(uint64_t ts, std::unique_ptr<uptime_proof::Proof> new_proof, const crypto::x25519_public_key &pk_x2)
{
bool update_db = false;
if (!proof || *proof != *new_proof) {
update_db = true;
proof = std::move(new_proof);
}
update_db |= update_val(timestamp, ts);
effective_timestamp = proof->timestamp;
pubkey_x25519 = pk_x2;
// Track an IP change (so that the obligations quorum can penalize for IP changes)
// We only keep the two most recent because all we really care about is whether it had more than one
//
// If we already know about the IP, update its timestamp:
auto now = std::time(nullptr);
if (public_ips[0].first && public_ips[0].first == proof->public_ip)
public_ips[0].second = now;
else if (public_ips[1].first && public_ips[1].first == proof->public_ip)
public_ips[1].second = now;
// Otherwise replace whichever IP has the older timestamp
else if (public_ips[0].second > public_ips[1].second)
public_ips[1] = {proof->public_ip, now};
else
public_ips[0] = {proof->public_ip, now};
return update_db;
};
//TODO remove after HF17
bool proof_info::update(uint64_t ts,
uint32_t ip,
uint16_t s_port,
@ -2817,14 +2860,15 @@ namespace service_nodes
const crypto::x25519_public_key& pk_x2)
{
bool update_db = false;
if (!proof) proof = std::unique_ptr<uptime_proof::Proof>(new uptime_proof::Proof());
update_db |= update_val(timestamp, ts);
update_db |= update_val(public_ip, ip);
update_db |= update_val(storage_port, s_port);
update_db |= update_val(storage_lmq_port, s_lmq_port);
update_db |= update_val(quorumnet_port, q_port);
update_db |= update_val(version, ver);
update_db |= update_val(pubkey_ed25519, pk_ed);
effective_timestamp = timestamp;
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->qnet_port, q_port);
update_db |= update_val(proof->version, ver);
update_db |= update_val(proof->pubkey_ed25519, pk_ed);
effective_timestamp = proof->timestamp;
pubkey_x25519 = pk_x2;
// Track an IP change (so that the obligations quorum can penalize for IP changes)
@ -2832,33 +2876,34 @@ namespace service_nodes
//
// If we already know about the IP, update its timestamp:
auto now = std::time(nullptr);
if (public_ips[0].first && public_ips[0].first == public_ip)
if (public_ips[0].first && public_ips[0].first == proof->public_ip)
public_ips[0].second = now;
else if (public_ips[1].first && public_ips[1].first == public_ip)
else if (public_ips[1].first && public_ips[1].first == proof->public_ip)
public_ips[1].second = now;
// Otherwise replace whichever IP has the older timestamp
else if (public_ips[0].second > public_ips[1].second)
public_ips[1] = {public_ip, now};
public_ips[1] = {proof->public_ip, now};
else
public_ips[0] = {public_ip, now};
public_ips[0] = {proof->public_ip, now};
return update_db;
};
void proof_info::update_pubkey(const crypto::ed25519_public_key &pk) {
if (pk == pubkey_ed25519)
if (pk == proof->pubkey_ed25519)
return;
if (pk && 0 == crypto_sign_ed25519_pk_to_curve25519(pubkey_x25519.data, pk.data)) {
pubkey_ed25519 = pk;
proof->pubkey_ed25519 = pk;
} else {
MWARNING("Failed to derive x25519 pubkey from ed25519 pubkey " << pubkey_ed25519);
MWARNING("Failed to derive x25519 pubkey from ed25519 pubkey " << proof->pubkey_ed25519);
pubkey_x25519 = crypto::x25519_public_key::null();
pubkey_ed25519 = crypto::ed25519_public_key::null();
proof->pubkey_ed25519 = crypto::ed25519_public_key::null();
}
}
#define REJECT_PROOF(log) do { LOG_PRINT_L2("Rejecting uptime proof from " << proof.pubkey << ": " log); return false; } while (0)
//TODO remove after HF17
bool service_node_list::handle_uptime_proof(cryptonote::NOTIFY_UPTIME_PROOF::request const &proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey)
{
uint8_t const hf_version = m_blockchain.get_current_hard_fork_version();
@ -2878,7 +2923,8 @@ namespace service_nodes
//
// Validate proof signature
//
crypto::hash hash = hash_uptime_proof(proof, hf_version);
crypto::hash hash = hash_uptime_proof(proof);
if (!crypto::check_signature(hash, proof.pubkey, proof.sig))
REJECT_PROOF("signature validation failed");
@ -2904,6 +2950,7 @@ namespace service_nodes
auto &iproof = proofs[proof.pubkey];
if (iproof.timestamp >= now - (UPTIME_PROOF_FREQUENCY_IN_SECONDS / 2))
REJECT_PROOF("already received one uptime proof for this node recently");
@ -2945,6 +2992,97 @@ namespace service_nodes
return true;
}
#undef REJECT_PROOF
#define REJECT_PROOF(log) do { LOG_PRINT_L2("Rejecting uptime proof from " << proof->pubkey << ": " log); return false; } while (0)
bool service_node_list::handle_btencoded_uptime_proof(std::unique_ptr<uptime_proof::Proof> proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey)
{
uint8_t const hf_version = m_blockchain.get_current_hard_fork_version();
uint64_t const now = time(nullptr);
// Validate proof version, timestamp range,
if ((proof->timestamp < now - UPTIME_PROOF_BUFFER_IN_SECONDS) || (proof->timestamp > now + UPTIME_PROOF_BUFFER_IN_SECONDS))
REJECT_PROOF("timestamp is too far from now");
for (auto const &min : MIN_UPTIME_PROOF_VERSIONS)
if (hf_version >= min.hardfork && proof->version < min.version)
REJECT_PROOF("v" << min.version[0] << "." << min.version[1] << "." << min.version[2] << "+ oxen version is required for v" << std::to_string(hf_version) << "+ network proofs");
if (!debug_allow_local_ips && !epee::net_utils::is_ip_public(proof->public_ip))
REJECT_PROOF("public_ip is not actually public");
//
// Validate proof signature
//
crypto::hash hash = proof->hash_uptime_proof();
if (!crypto::check_signature(hash, proof->pubkey, proof->sig))
REJECT_PROOF("signature validation failed");
crypto::x25519_public_key derived_x25519_pubkey = crypto::x25519_public_key::null();
if (!proof->pubkey_ed25519)
REJECT_PROOF("required ed25519 auxiliary pubkey " << proof->pubkey_ed25519 << " not included in proof");
if (0 != crypto_sign_verify_detached(proof->sig_ed25519.data, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), proof->pubkey_ed25519.data))
REJECT_PROOF("ed25519 signature validation failed");
if (0 != crypto_sign_ed25519_pk_to_curve25519(derived_x25519_pubkey.data, proof->pubkey_ed25519.data)
|| !derived_x25519_pubkey)
REJECT_PROOF("invalid ed25519 pubkey included in proof (x25519 derivation failed)");
if (proof->qnet_port == 0)
REJECT_PROOF("invalid quorumnet port in uptime proof");
auto locks = tools::unique_locks(m_blockchain, m_sn_mutex, m_x25519_map_mutex);
auto it = m_state.service_nodes_infos.find(proof->pubkey);
if (it == m_state.service_nodes_infos.end())
REJECT_PROOF("no such service node is currently registered");
auto &iproof = proofs[proof->pubkey];
if (iproof.timestamp >= now - (UPTIME_PROOF_FREQUENCY_IN_SECONDS / 2))
REJECT_PROOF("already received one uptime proof for this node recently");
if (m_service_node_keys && proof->pubkey == m_service_node_keys->pub)
{
my_uptime_proof_confirmation = true;
MGINFO("Received uptime-proof confirmation back from network for Service Node (yours): " << proof->pubkey);
}
else
{
my_uptime_proof_confirmation = false;
LOG_PRINT_L2("Accepted uptime proof from " << proof->pubkey);
if (m_service_node_keys && proof->pubkey_ed25519 == m_service_node_keys->pub_ed25519)
MGINFO_RED("Uptime proof from SN " << proof->pubkey << " is not us, but is using our ed/x25519 keys; "
"this is likely to lead to deregistration of one or both service nodes.");
}
auto old_x25519 = iproof.pubkey_x25519;
if (iproof.update(now, std::move(proof), derived_x25519_pubkey))
{
iproof.store(iproof.proof->pubkey, m_blockchain);
}
if ((uint64_t) x25519_map_last_pruned + X25519_MAP_PRUNING_INTERVAL <= now)
{
time_t cutoff = now - X25519_MAP_PRUNING_LAG;
erase_if(x25519_to_pub, [&cutoff](const decltype(x25519_to_pub)::value_type &x) { return x.second.second < cutoff; });
x25519_map_last_pruned = now;
}
if (old_x25519 && old_x25519 != derived_x25519_pubkey)
x25519_to_pub.erase(old_x25519);
if (derived_x25519_pubkey)
x25519_to_pub[derived_x25519_pubkey] = {iproof.proof->pubkey, now};
if (derived_x25519_pubkey && (old_x25519 != derived_x25519_pubkey))
x25519_pkey = derived_x25519_pubkey;
return true;
}
void service_node_list::cleanup_proofs()
{
MDEBUG("Cleaning up expired SN proofs");
@ -3018,8 +3156,8 @@ namespace service_nodes
uint16_t port = 0;
for_each_service_node_info_and_proof(&pubkey, &pubkey + 1, [&](auto&, auto&, auto& proof) {
found = true;
ip = proof.public_ip;
port = proof.quorumnet_port;
ip = proof.proof->public_ip;
port = proof.proof->qnet_port;
});
if (!found) {

View File

@ -38,6 +38,8 @@
#include "cryptonote_core/service_node_quorum_cop.h"
#include "common/util.h"
#include <lokimq/bt_serialize.h>
namespace cryptonote
{
class Blockchain;
@ -45,6 +47,11 @@ class BlockchainDB;
struct checkpoint_t;
}; // namespace cryptonote
namespace uptime_proof
{
class Proof;
}
namespace service_nodes
{
constexpr uint64_t INVALID_HEIGHT = static_cast<uint64_t>(-1);
@ -136,6 +143,8 @@ namespace service_nodes
struct proof_info
{
proof_info();
participation_history<participation_entry> pulse_participation{};
participation_history<participation_entry> checkpoint_participation{};
participation_history<timestamp_participation_entry> timestamp_participation{};
@ -149,12 +158,7 @@ namespace service_nodes
uint64_t storage_server_reachable_timestamp = 0;
// Unlike all of the above (except for timestamp), these values *do* get serialized
uint32_t public_ip = 0;
uint16_t storage_port = 0;
uint16_t storage_lmq_port = 0;
uint16_t quorumnet_port = 0;
std::array<uint16_t, 3> version{{0,0,0}};
crypto::ed25519_public_key pubkey_ed25519 = crypto::ed25519_public_key::null();
std::unique_ptr<uptime_proof::Proof> proof;
// Derived from pubkey_ed25519, not serialized
crypto::x25519_public_key pubkey_x25519 = crypto::x25519_public_key::null();
@ -168,6 +172,8 @@ namespace service_nodes
// Returns true if serializable data is changed (in which case `store()` should be called).
// Note that this does not update the m_x25519_to_pub map if the x25519 key changes (that's the
// 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 HF 17
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);
// Stores this record in the database.
@ -511,13 +517,23 @@ namespace service_nodes
void set_quorum_history_storage(uint64_t hist_size); // 0 = none (default), 1 = unlimited, N = # of blocks
bool store();
//TODO: remove after HF17
crypto::hash hash_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof) const;
/// Record public ip and storage port and add them to the service node list
//TODO: remove after HF17
cryptonote::NOTIFY_UPTIME_PROOF::request generate_uptime_proof(uint32_t public_ip,
uint16_t storage_port,
uint16_t storage_lmq_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;
//TODO: remove after HF17
bool handle_uptime_proof(cryptonote::NOTIFY_UPTIME_PROOF::request const &proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey);
bool handle_btencoded_uptime_proof(std::unique_ptr<uptime_proof::Proof> proof, bool &my_uptime_proof_confirmation, crypto::x25519_public_key &x25519_pkey);
void record_checkpoint_participation(crypto::public_key const &pubkey, uint64_t height, bool participated);
// Called every hour to remove proofs for expired SNs from memory and the database.

View File

@ -29,6 +29,7 @@
#include "service_node_quorum_cop.h"
#include "service_node_voting.h"
#include "service_node_list.h"
#include "uptime_proof.h"
#include "cryptonote_config.h"
#include "cryptonote_core.h"
#include "version.h"
@ -98,13 +99,13 @@ namespace service_nodes
m_core.get_service_node_list().access_proof(pubkey, [&](const proof_info &proof) {
ss_reachable = proof.storage_server_reachable;
timestamp = std::max(proof.timestamp, proof.effective_timestamp);
timestamp = std::max(proof.proof->timestamp, proof.effective_timestamp);
ips = proof.public_ips;
checkpoint_participation = proof.checkpoint_participation;
pulse_participation = proof.pulse_participation;
// TODO: remove after HF17
if (proof.version >= MIN_TIMESTAMP_VERSION && hf_version >= cryptonote::network_version_17) {
if (proof.proof->version >= MIN_TIMESTAMP_VERSION && hf_version >= cryptonote::network_version_17) {
timestamp_participation = proof.timestamp_participation;
timesync_status = proof.timesync_status;
check_timestamp_obligation = true;

View File

@ -213,8 +213,8 @@ 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<int, 3> MIN_STORAGE_SERVER_VERSION{{2, 0, 7}};
constexpr std::array<int, 3> MIN_LOKINET_VERSION{{0, 8, 0}};
constexpr std::array<uint16_t, 3> MIN_STORAGE_SERVER_VERSION{{2, 0, 7}};
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
struct proof_version
@ -224,6 +224,7 @@ namespace service_nodes {
};
constexpr proof_version MIN_UPTIME_PROOF_VERSIONS[] = {
{cryptonote::network_version_17, {9,0,0}},
{cryptonote::network_version_16_pulse, {8,1,0}},
{cryptonote::network_version_15_lns, {7,1,2}},
{cryptonote::network_version_14_blink, {6,1,0}},

View File

@ -0,0 +1,154 @@
#include "uptime_proof.h"
#include "common/string_util.h"
#include "version.h"
extern "C"
{
#include <sodium/crypto_sign.h>
}
#undef OXEN_DEFAULT_LOG_CATEGORY
#define OXEN_DEFAULT_LOG_CATEGORY "uptime_proof"
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}
{
this->lokinet_version = lokinet_version;
crypto::hash hash = this->hash_uptime_proof();
crypto::generate_signature(hash, keys.pub, keys.key, sig);
crypto_sign_detached(sig_ed25519.data, NULL, reinterpret_cast<unsigned char *>(hash.data), sizeof(hash.data), keys.key_ed25519.data);
}
//Deserialize from a btencoded string into our Proof instance
Proof::Proof(const std::string& serialized_proof)
{
try {
const lokimq::bt_dict bt_proof = lokimq::bt_deserialize<lokimq::bt_dict>(serialized_proof);
//snode_version <X,X,X>
const lokimq::bt_list& bt_version = var::get<lokimq::bt_list>(bt_proof.at("v"));
int k = 0;
for (lokimq::bt_value const &i: bt_version){
version[k++] = static_cast<uint16_t>(lokimq::get_int<unsigned>(i));
}
//timestamp
timestamp = lokimq::get_int<unsigned>(bt_proof.at("t"));
//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>(lokimq::get_int<unsigned>(bt_proof.at("s")));
//pubkey_ed25519
pubkey_ed25519 = tools::make_from_guts<crypto::ed25519_public_key>(var::get<std::string>(bt_proof.at("pke")));
//pubkey
if (auto it = bt_proof.find("pk"); it != bt_proof.end())
pubkey = tools::make_from_guts<crypto::public_key>(var::get<std::string>(bt_proof.at("pk")));
else
std::memcpy(pubkey.data, pubkey_ed25519.data, 32);
//qnet_port
qnet_port = lokimq::get_int<unsigned>(bt_proof.at("q"));
//storage_lmq_port
storage_lmq_port = lokimq::get_int<unsigned>(bt_proof.at("slp"));
//storage_version
const lokimq::bt_list& bt_storage_version = var::get<lokimq::bt_list>(bt_proof.at("sv"));
k = 0;
for (lokimq::bt_value const &i: bt_storage_version){
storage_server_version[k++] = static_cast<uint16_t>(lokimq::get_int<unsigned>(i));
}
//lokinet_version
const lokimq::bt_list& bt_lokinet_version = var::get<lokimq::bt_list>(bt_proof.at("lv"));
k = 0;
for (lokimq::bt_value const &i: bt_lokinet_version){
lokinet_version[k++] = static_cast<uint16_t>(lokimq::get_int<unsigned>(i));
}
} catch (const std::exception& e) {
MWARNING("deserialization failed: " << e.what());
throw;
}
}
crypto::hash Proof::hash_uptime_proof() const
{
crypto::hash result;
std::string serialized_proof = lokimq::bt_serialize(bt_encode_uptime_proof());
size_t buf_size = serialized_proof.size();
crypto::cn_fast_hash(serialized_proof.data(), buf_size, result);
return result;
}
lokimq::bt_dict Proof::bt_encode_uptime_proof() const
{
lokimq::bt_dict encoded_proof{
//version
{"v", lokimq::bt_list{{version[0], version[1], version[2]}}},
//timestamp
{"t", timestamp},
//public_ip
{"ip", epee::string_tools::get_ip_string_from_int32(public_ip)},
//storage_port
{"s", storage_port},
//pubkey_ed25519
{"pke", tools::view_guts(pubkey_ed25519)},
//qnet_port
{"q", qnet_port},
//storage_lmq_port
{"slp", storage_lmq_port},
//storage_version
{"sv", lokimq::bt_list{{storage_server_version[0], storage_server_version[1], storage_server_version[2]}}},
//lokinet_version
{"lv", lokimq::bt_list{{lokinet_version[0], lokinet_version[1], lokinet_version[2]}}},
};
if (pubkey != pubkey_ed25519) encoded_proof["pk"] = tools::view_guts(pubkey);
return encoded_proof;
}
cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request Proof::generate_request() const
{
cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request request;
request.proof = lokimq::bt_serialize(this->bt_encode_uptime_proof());
request.sig = tools::view_guts(this->sig);
request.ed_sig = tools::view_guts(this->sig_ed25519);
return request;
}
}
bool operator==(const uptime_proof::Proof& lhs, const uptime_proof::Proof& rhs)
{
bool result = true;
if( (lhs.timestamp != rhs.timestamp) ||
(lhs.pubkey != rhs.pubkey) ||
(lhs.sig != rhs.sig) ||
(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.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]))
result = false;
return result;
}
bool operator!=(const uptime_proof::Proof& lhs, const uptime_proof::Proof& rhs)
{
return !(lhs == rhs);
}

View File

@ -0,0 +1,41 @@
#pragma once
#include "service_node_list.h"
#include "../cryptonote_protocol/cryptonote_protocol_defs.h"
#include <lokimq/lokimq.h>
namespace uptime_proof
{
class Proof
{
public:
std::array<uint16_t, 3> version;
std::array<uint16_t, 3> storage_server_version;
std::array<uint16_t, 3> lokinet_version;
uint64_t timestamp;
crypto::public_key pubkey;
crypto::signature sig;
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 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(const std::string& serialized_proof);
lokimq::bt_dict bt_encode_uptime_proof() const;
crypto::hash hash_uptime_proof() const;
cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request generate_request() const;
};
}
bool operator==(const uptime_proof::Proof& lhs, const uptime_proof::Proof& rhs);
bool operator!=(const uptime_proof::Proof& lhs, const uptime_proof::Proof& rhs);

View File

@ -119,6 +119,13 @@ KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_UPTIME_PROOF::request)
KV_SERIALIZE_VAL_POD_AS_BLOB(sig_ed25519)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_BTENCODED_UPTIME_PROOF::request)
KV_SERIALIZE(proof)
KV_SERIALIZE(sig)
KV_SERIALIZE(ed_sig)
KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(NOTIFY_REQUEST_BLOCK_BLINKS::request)
KV_SERIALIZE(heights)
KV_SERIALIZE_MAP_CODE_END()

View File

@ -267,6 +267,25 @@ namespace cryptonote
};
};
/************************************************************************/
/* */
/************************************************************************/
struct NOTIFY_BTENCODED_UPTIME_PROOF
{
const static int ID = BC_COMMANDS_POOL_BASE + 12;
struct request
{
// BT-Encoded string of the Uptime Proof
std::string proof;
std::string sig;
std::string ed_sig;
KV_MAP_SERIALIZABLE
};
};
/************************************************************************/
/* */
/************************************************************************/

View File

@ -80,6 +80,7 @@ namespace cryptonote
HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, handle_notify_new_fluffy_block)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, handle_request_fluffy_missing_tx)
HANDLE_NOTIFY_T2(NOTIFY_UPTIME_PROOF, handle_uptime_proof)
HANDLE_NOTIFY_T2(NOTIFY_BTENCODED_UPTIME_PROOF, handle_btencoded_uptime_proof)
HANDLE_NOTIFY_T2(NOTIFY_NEW_SERVICE_NODE_VOTE, handle_notify_new_service_node_vote)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_BLOCK_BLINKS, handle_request_block_blinks)
HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_BLOCK_BLINKS, handle_response_block_blinks)
@ -118,6 +119,7 @@ namespace cryptonote
int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context);
int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context);
int handle_uptime_proof(int command, NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& context);
int handle_btencoded_uptime_proof(int command, NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& context);
int handle_notify_new_service_node_vote(int command, NOTIFY_NEW_SERVICE_NODE_VOTE::request& arg, cryptonote_connection_context& context);
int handle_request_block_blinks(int command, NOTIFY_REQUEST_BLOCK_BLINKS::request& arg, cryptonote_connection_context& context);
int handle_response_block_blinks(int command, NOTIFY_RESPONSE_BLOCK_BLINKS::request& arg, cryptonote_connection_context& context);
@ -152,6 +154,7 @@ namespace cryptonote
virtual bool relay_block(NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_btencoded_uptime_proof(NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context);
virtual bool relay_service_node_votes(NOTIFY_NEW_SERVICE_NODE_VOTE::request& arg, cryptonote_connection_context& exclude_context);
//----------------------------------------------------------------------------------
//bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context);

View File

@ -876,6 +876,39 @@ namespace cryptonote
}
return 1;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_btencoded_uptime_proof(int command, NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& context)
{
MLOG_P2P_MESSAGE("Received NOTIFY_BTENCODED_UPTIME_PROOF");
// NOTE: Don't relay your own uptime proof, otherwise we have the following situation
// Node1 sends uptime ->
// Node2 receives uptime and relays it back to Node1 for acknowledgement ->
// Node1 receives it, handle_uptime_proof returns true to acknowledge, Node1 tries to resend to the same peers again
// Instead, if we receive our own uptime proof, then acknowledge but don't
// send on. If the we are missing an uptime proof it will have been
// submitted automatically by the daemon itself instead of
// using my own proof relayed by other nodes.
(void)context;
bool my_uptime_proof_confirmation = false;
if (m_core.handle_btencoded_uptime_proof(arg, my_uptime_proof_confirmation))
{
if (!my_uptime_proof_confirmation)
{
// NOTE: The default exclude context contains the peer who sent us this
// uptime proof, we want to ensure we relay it back so they know that the
// peer they relayed to received their uptime and confirm it, so send in an
// empty context so we don't omit the source peer from the relay back.
cryptonote_connection_context empty_context = {};
relay_btencoded_uptime_proof(arg, empty_context);
}
}
return 1;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@ -2526,6 +2559,13 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_btencoded_uptime_proof(NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)
{
bool result = relay_to_synchronized_peers<NOTIFY_BTENCODED_UPTIME_PROOF>(arg, exclude_context);
return result;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::relay_service_node_votes(NOTIFY_NEW_SERVICE_NODE_VOTE::request& arg, cryptonote_connection_context& exclude_context)
{
bool result = relay_to_synchronized_peers<NOTIFY_NEW_SERVICE_NODE_VOTE>(arg, exclude_context);

View File

@ -43,6 +43,7 @@ namespace cryptonote
virtual bool relay_block(NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0;
virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0;
virtual bool relay_uptime_proof(NOTIFY_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)=0;
virtual bool relay_btencoded_uptime_proof(NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)=0;
//virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0;
virtual bool relay_service_node_votes(NOTIFY_NEW_SERVICE_NODE_VOTE::request& arg, cryptonote_connection_context& exclude_context)=0;
};
@ -68,5 +69,9 @@ namespace cryptonote
{
return false;
}
virtual bool relay_btencoded_uptime_proof(NOTIFY_BTENCODED_UPTIME_PROOF::request& arg, cryptonote_connection_context& exclude_context)
{
return false;
}
};
}

View File

@ -33,6 +33,7 @@
#include "cryptonote_core/tx_blink.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/pulse.h"
#include "cryptonote_core/uptime_proof.h"
#include "quorumnet_conn_matrix.h"
#include "cryptonote_config.h"
#include "common/random.h"
@ -160,7 +161,7 @@ peer_prepare_relay_to_quorum_subset(cryptonote::core &core, It quorum_begin, It
MDEBUG("Have " << candidates.size() << " SN candidates");
std::vector<std::tuple<std::string, std::string, decltype(proof_info{}.version)>> remotes; // {x25519 pubkey, connect string, version}
std::vector<std::tuple<std::string, std::string, decltype(proof_info{}.proof->version)>> remotes; // {x25519 pubkey, connect string, version}
remotes.reserve(candidates.size());
core.get_service_node_list().for_each_service_node_info_and_proof(candidates.begin(), candidates.end(),
[&remotes](const auto &pubkey, const auto &info, const auto &proof) {
@ -168,14 +169,14 @@ peer_prepare_relay_to_quorum_subset(cryptonote::core &core, It quorum_begin, It
MTRACE("Not include inactive node " << pubkey);
return;
}
if (!proof.pubkey_x25519 || !proof.quorumnet_port || !proof.public_ip) {
if (!proof.pubkey_x25519 || !proof.proof->qnet_port || !proof.proof->public_ip) {
MTRACE("Not including node " << pubkey << ": missing x25519(" << to_hex(get_data_as_string(proof.pubkey_x25519)) << "), "
"public_ip(" << epee::string_tools::get_ip_string_from_int32(proof.public_ip) << "), or qnet port(" << proof.quorumnet_port << ")");
"public_ip(" << epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip) << "), or qnet port(" << proof.proof->qnet_port << ")");
return;
}
remotes.emplace_back(get_data_as_string(proof.pubkey_x25519),
"tcp://" + epee::string_tools::get_ip_string_from_int32(proof.public_ip) + ":" + std::to_string(proof.quorumnet_port),
proof.version);
"tcp://" + epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip) + ":" + std::to_string(proof.proof->qnet_port),
proof.proof->version);
});
// Select 4 random SNs to send the data to, but prefer SNs with newer versions because they may have network fixes.
@ -294,9 +295,9 @@ public:
// Lookup the x25519 and ZMQ connection string for all peers
qnet.core.get_service_node_list().for_each_service_node_info_and_proof(need_remotes.begin(), need_remotes.end(),
[this](const auto &pubkey, const auto &info, const auto &proof) {
if (info.is_active() && proof.pubkey_x25519 && proof.quorumnet_port && proof.public_ip)
if (info.is_active() && proof.pubkey_x25519 && proof.proof->qnet_port && proof.proof->public_ip)
remotes.emplace(pubkey, std::make_pair(proof.pubkey_x25519,
"tcp://" + epee::string_tools::get_ip_string_from_int32(proof.public_ip) + ":" + std::to_string(proof.quorumnet_port)));
"tcp://" + epee::string_tools::get_ip_string_from_int32(proof.proof->public_ip) + ":" + std::to_string(proof.proof->qnet_port)));
});
compute_validator_peers(qbegin, qend, opportunistic);

View File

@ -45,6 +45,7 @@
#include "rpc/http_server.h"
#include "rpc/lmq_server.h"
#include "cryptonote_protocol/quorumnet.h"
#include "cryptonote_core/uptime_proof.h"
#include "common/password.h"
#include "common/signal_handler.h"

View File

@ -1678,6 +1678,12 @@ static void append_printable_service_node_list_entry(cryptonote::network_type ne
stream << "Last checked: " << get_human_time_ago(entry.storage_server_reachable_timestamp, now);
stream << ")\n";
//
// NOTE: Component Versions
//
stream << indent2 << "Storage Server Version: " << tools::join(".", entry.storage_server_version) << "\n";
stream << indent2 << "Lokinet Router Version: " << tools::join(".", entry.lokinet_version) << "\n";
//
// NOTE: Node Credits
//

View File

@ -56,6 +56,7 @@
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_core/tx_sanity_check.h"
#include "cryptonote_core/uptime_proof.h"
#include "epee/misc_language.h"
#include "net/parse.h"
#include "crypto/hash.h"
@ -3038,17 +3039,19 @@ namespace cryptonote { namespace rpc {
entry.decommission_count = info.decommission_count;
m_core.get_service_node_list().access_proof(sn_info.pubkey, [&entry](const auto &proof) {
entry.service_node_version = proof.version;
entry.public_ip = epee::string_tools::get_ip_string_from_int32(proof.public_ip);
entry.storage_port = proof.storage_port;
entry.storage_lmq_port = proof.storage_lmq_port;
entry.service_node_version = proof.proof->version;
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_server_reachable = proof.storage_server_reachable;
entry.pubkey_ed25519 = proof.pubkey_ed25519 ? tools::type_to_hex(proof.pubkey_ed25519) : "";
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) : "";
entry.quorumnet_port = proof.quorumnet_port;
entry.quorumnet_port = proof.proof->qnet_port;
// NOTE: Service Node Testing
entry.last_uptime_proof = proof.timestamp;
entry.last_uptime_proof = proof.proof->timestamp;
entry.storage_server_reachable = proof.storage_server_reachable;
entry.storage_server_reachable_timestamp = proof.storage_server_reachable_timestamp;
@ -3267,7 +3270,7 @@ namespace cryptonote { namespace rpc {
}
namespace {
struct version_printer { const std::array<int, 3> &v; };
struct version_printer { const std::array<uint16_t, 3> &v; };
std::ostream &operator<<(std::ostream &o, const version_printer &vp) { return o << vp.v[0] << '.' << vp.v[1] << '.' << vp.v[2]; }
// Handles a ping. Returns true if the ping was significant (i.e. first ping after startup, or
@ -3275,7 +3278,7 @@ namespace cryptonote { namespace rpc {
// argument: true if this ping should trigger an immediate proof send (i.e. first ping after
// startup or after a ping expiry), false for an ordinary ping.
template <typename RPC, typename Success>
auto handle_ping(std::array<int, 3> cur_version, std::array<int, 3> required, const char* name, std::atomic<std::time_t>& update, time_t lifetime, Success success)
auto handle_ping(std::array<uint16_t, 3> cur_version, std::array<uint16_t, 3> required, const char* name, std::atomic<std::time_t>& update, time_t lifetime, Success success)
{
typename RPC::response res{};
if (cur_version < required) {
@ -3301,6 +3304,7 @@ 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};
return handle_ping<STORAGE_SERVER_PING>(
{req.version_major, req.version_minor, req.version_patch}, service_nodes::MIN_STORAGE_SERVER_VERSION,
"Storage Server", m_core.m_last_storage_server_ping, STORAGE_SERVER_PING_LIFETIME,
@ -3313,6 +3317,7 @@ namespace cryptonote { namespace rpc {
//------------------------------------------------------------------------------------------------------------------------------
LOKINET_PING::response core_rpc_server::invoke(LOKINET_PING::request&& req, rpc_context context)
{
m_core.lokinet_version = req.version;
return handle_ping<LOKINET_PING>(
req.version, service_nodes::MIN_LOKINET_VERSION,
"Lokinet", m_core.m_last_lokinet_ping, LOKINET_PING_LIFETIME,

View File

@ -2052,6 +2052,8 @@ namespace rpc {
bool earned_downtime_blocks;
bool service_node_version;
bool lokinet_version;
bool storage_server_version;
bool contributors;
bool total_contributed;
bool total_reserved;
@ -2108,6 +2110,8 @@ namespace rpc {
uint32_t decommission_count; // The number of times the Service Node has been decommissioned since registration
int64_t earned_downtime_blocks; // The number of blocks earned towards decommissioning, or the number of blocks remaining until deregistration if currently decommissioned
std::array<uint16_t, 3> service_node_version; // The major, minor, patch version of the Service Node respectively.
std::array<uint16_t, 3> lokinet_version; // The major, minor, patch version of the Service Node's lokinet router.
std::array<uint16_t, 3> storage_server_version; // The major, minor, patch version of the Service Node's storage server.
std::vector<service_node_contributor> contributors; // Array of contributors, contributing to this Service Node.
uint64_t total_contributed; // The total amount of Loki in atomic units contributed to this Service Node.
uint64_t total_reserved; // The total amount of Loki in atomic units reserved in this Service Node.
@ -2183,9 +2187,9 @@ namespace rpc {
struct request
{
int version_major; // Storage Server Major version
int version_minor; // Storage Server Minor version
int version_patch; // Storage Server Patch version
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
KV_MAP_SERIALIZABLE
};
@ -2200,7 +2204,7 @@ namespace rpc {
struct request
{
std::array<int, 3> version; // Lokinet version
std::array<uint16_t, 3> version; // Lokinet version
KV_MAP_SERIALIZABLE
};

View File

@ -33,6 +33,7 @@
#include "cryptonote_core/blockchain.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/uptime_proof.h"
#include "blockchain_utilities/blockchain_objects.h"
#include "blockchain_db/testdb.h"

View File

@ -247,6 +247,12 @@ bool tests::proxy_core::handle_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROO
return false; // never relay these for tests.
}
bool tests::proxy_core::handle_btencoded_uptime_proof(const cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation)
{
// TODO: add tests for core uptime proof checking.
return false; // never relay these for tests.
}
bool tests::proxy_core::get_short_chain_history(std::list<crypto::hash>& ids) {
build_short_history(ids, m_lastblk);
return true;

View File

@ -85,6 +85,7 @@ namespace tests
int add_blinks(const std::vector<std::shared_ptr<cryptonote::blink_tx>> &blinks) { return 0; }
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t *checkpoint, bool update_miner_blocktemplate = true);
bool handle_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation);
bool handle_btencoded_uptime_proof(const cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation);
void pause_mine(){}
void resume_mine(){}
bool on_idle(){return true;}

View File

@ -31,6 +31,7 @@
#include "chaingen.h"
#include "block_validation.h"
#include "common/util.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -33,6 +33,7 @@
#include "chaingen.h"
#include "bulletproofs.h"
#include "device/device.hpp"
#include "cryptonote_core/uptime_proof.h"
#include "common/util.h"
using namespace crypto;

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "chain_split_1.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "chain_switch_1.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -50,6 +50,7 @@
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_core/uptime_proof.h"
#include "oxen_economy.h"
#include "ringct/rctSigs.h"

View File

@ -35,6 +35,7 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/uptime_proof.h"
#include "chaingen.h"
#include "chaingen_tests_list.h"

View File

@ -32,6 +32,7 @@
#include "chaingen_tests_list.h"
#include "common/util.h"
#include "common/command_line.h"
#include "cryptonote_core/uptime_proof.h"
#include "transaction_tests.h"
namespace po = boost::program_options;

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "double_spend.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "integer_overflow.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -30,6 +30,7 @@
#include "ringct/rctSigs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_core/uptime_proof.h"
#include "multisig/multisig.h"
#include "common/apply_permutation.h"
#include "common/util.h"

View File

@ -35,6 +35,7 @@
#include "cryptonote_config.h"
#include "cryptonote_core/oxen_name_system.h"
#include "cryptonote_core/service_node_list.h"
#include "cryptonote_core/uptime_proof.h"
#include "oxen_economy.h"
#include "common/random.h"

View File

@ -33,6 +33,7 @@
#include "rct.h"
#include "device/device.hpp"
#include "common/util.h"
#include "cryptonote_core/uptime_proof.h"
using namespace crypto;
using namespace cryptonote;

View File

@ -30,6 +30,7 @@
#include "chaingen.h"
#include "ring_signature_1.h"
#include "cryptonote_core/uptime_proof.h"
using namespace cryptonote;

View File

@ -31,6 +31,7 @@
#include "chaingen.h"
#include "tx_validation.h"
#include "device/device.hpp"
#include "cryptonote_core/uptime_proof.h"
using namespace crypto;
using namespace cryptonote;

View File

@ -31,6 +31,7 @@
#include "chaingen.h"
#include "v2_tests.h"
#include "common/util.h"
#include "cryptonote_core/uptime_proof.h"
using namespace crypto;
using namespace cryptonote;

View File

@ -3,6 +3,7 @@
//
#include "wallet_tools.h"
#include "cryptonote_core/uptime_proof.h"
#include <random>
using namespace crypto;

View File

@ -33,6 +33,7 @@
#include <memory>
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/uptime_proof.h"
#include "checkpoints/checkpoints.cpp"
#include "blockchain_db/testdb.h"

View File

@ -34,6 +34,7 @@
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/hardfork.h"
#include "cryptonote_core/uptime_proof.h"
#include "blockchain_db/testdb.h"
#include "checkpoints/checkpoints.h"

View File

@ -32,6 +32,7 @@
#include "cryptonote_core/blockchain.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/uptime_proof.h"
#include "blockchain_utilities/blockchain_objects.h"
#include "blockchain_db/testdb.h"

View File

@ -64,6 +64,7 @@ public:
int add_blinks(const std::vector<std::shared_ptr<cryptonote::blink_tx>> &blinks) { return 0; }
bool handle_incoming_block(const cryptonote::blobdata& block_blob, const cryptonote::block *block, cryptonote::block_verification_context& bvc, cryptonote::checkpoint_t const *checkpoint, bool update_miner_blocktemplate = true) { return true; }
bool handle_uptime_proof(const cryptonote::NOTIFY_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation) { return false; }
bool handle_btencoded_uptime_proof(const cryptonote::NOTIFY_BTENCODED_UPTIME_PROOF::request &proof, bool &my_uptime_proof_confirmation) { return false; }
void pause_mine(){}
void resume_mine(){}
bool on_idle(){return true;}

View File

@ -33,6 +33,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/blockchain.h"
#include "cryptonote_core/uptime_proof.h"
#include "blockchain_db/testdb.h"
#include "blockchain_utilities/blockchain_objects.h"