Sign telemetry messages (#2618)

* Sign telemetry messages

* Stein review comment about reusing ed25519 wrappers

* Remove unused header

* Add IGNORE_GTEST_INCL define to node.cpp
This commit is contained in:
Wesley Shillingford 2020-03-16 15:31:41 +00:00 committed by GitHub
commit 35cca9a576
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 291 additions and 182 deletions

View file

@ -11,7 +11,6 @@ using namespace std::chrono_literals;
namespace
{
void wait_peer_connections (nano::system & system_a);
void compare_default_test_result_data (nano::telemetry_data const & telemetry_data_a, nano::node const & node_server_a);
}
TEST (node_telemetry, consolidate_data)
@ -152,7 +151,7 @@ TEST (node_telemetry, serialize_deserialize_json_optional)
data.timestamp = std::chrono::system_clock::time_point (100ms);
nano::jsonconfig config;
data.serialize_json (config);
data.serialize_json (config, false);
uint8_t val;
ASSERT_FALSE (config.get ("minor_version", val).get_error ());
@ -168,7 +167,7 @@ TEST (node_telemetry, serialize_deserialize_json_optional)
ASSERT_EQ (timestamp, 100);
nano::telemetry_data data1;
data1.deserialize_json (config);
data1.deserialize_json (config, false);
ASSERT_EQ (*data1.minor_version, 1);
ASSERT_EQ (*data1.patch_version, 4);
ASSERT_EQ (*data1.pre_release_version, 6);
@ -177,7 +176,7 @@ TEST (node_telemetry, serialize_deserialize_json_optional)
nano::telemetry_data no_optional_data;
nano::jsonconfig config1;
no_optional_data.serialize_json (config1);
no_optional_data.serialize_json (config1, false);
ASSERT_FALSE (config1.get_optional<uint8_t> ("minor_version").is_initialized ());
ASSERT_FALSE (config1.get_optional<uint8_t> ("patch_version").is_initialized ());
ASSERT_FALSE (config1.get_optional<uint8_t> ("pre_release_version").is_initialized ());
@ -185,7 +184,7 @@ TEST (node_telemetry, serialize_deserialize_json_optional)
ASSERT_FALSE (config1.get_optional<uint64_t> ("timestamp").is_initialized ());
nano::telemetry_data no_optional_data1;
no_optional_data1.deserialize_json (config1);
no_optional_data1.deserialize_json (config1, false);
ASSERT_FALSE (no_optional_data1.minor_version.is_initialized ());
ASSERT_FALSE (no_optional_data1.patch_version.is_initialized ());
ASSERT_FALSE (no_optional_data1.pre_release_version.is_initialized ());
@ -258,6 +257,26 @@ TEST (node_telemetry, consolidate_data_remove_outliers)
ASSERT_EQ (data, consolidated_telemetry_data);
}
TEST (node_telemetry, signatures)
{
nano::keypair node_id;
nano::telemetry_data data;
data.node_id = node_id.pub;
data.major_version = 20;
data.minor_version = 1;
data.patch_version = 5;
data.pre_release_version = 2;
data.maker = 1;
data.timestamp = std::chrono::system_clock::time_point (100ms);
data.sign (node_id);
ASSERT_FALSE (data.validate_signature (nano::telemetry_data::size));
auto signature = data.signature;
// Check that the signature is different if changing a piece of data
data.maker = 2;
data.sign (node_id);
ASSERT_NE (data.signature, signature);
}
TEST (node_telemetry, no_peers)
{
nano::system system (1);
@ -296,7 +315,7 @@ TEST (node_telemetry, basic)
}
// Check the metrics are correct
compare_default_test_result_data (telemetry_data, *node_server);
nano::compare_default_telemetry_response_data (telemetry_data, node_server->network_params, node_server->config.bandwidth_limit, node_server->node_id);
// Call again straight away. It should use the cache
{
@ -454,7 +473,7 @@ TEST (node_telemetry, over_udp)
auto channel = node_client->network.find_channel (node_server->network.endpoint ());
node_client->telemetry->get_metrics_single_peer_async (channel, [&done, &node_server](nano::telemetry_data_response const & response_a) {
ASSERT_FALSE (response_a.error);
compare_default_test_result_data (response_a.telemetry_data, *node_server);
nano::compare_default_telemetry_response_data (response_a.telemetry_data, node_server->network_params, node_server->config.bandwidth_limit, node_server->node_id);
done = true;
});
@ -527,7 +546,7 @@ TEST (node_telemetry, blocking_request)
// Now try single request metric
auto telemetry_data_response = node_client->telemetry->get_metrics_single_peer (node_client->network.find_channel (node_server->network.endpoint ()));
ASSERT_FALSE (telemetry_data_response.error);
compare_default_test_result_data (telemetry_data_response.telemetry_data, *node_server);
nano::compare_default_telemetry_response_data (telemetry_data_response.telemetry_data, node_server->network_params, node_server->config.bandwidth_limit, node_server->node_id);
done = true;
promise.get_future ().wait ();
@ -754,9 +773,9 @@ TEST (node_telemetry, disable_metrics)
// It should still be able to receive metrics though
done = false;
auto channel1 = node_server->network.find_channel (node_client->network.endpoint ());
node_server->telemetry->get_metrics_single_peer_async (channel1, [&done, node_server](nano::telemetry_data_response const & response_a) {
node_server->telemetry->get_metrics_single_peer_async (channel1, [&done, node_client](nano::telemetry_data_response const & response_a) {
ASSERT_FALSE (response_a.error);
compare_default_test_result_data (response_a.telemetry_data, *node_server);
nano::compare_default_telemetry_response_data (response_a.telemetry_data, node_client->network_params, node_client->config.bandwidth_limit, node_client->node_id);
done = true;
});
@ -796,23 +815,4 @@ void wait_peer_connections (nano::system & system_a)
wait_peer_count (true);
wait_peer_count (false);
}
void compare_default_test_result_data (nano::telemetry_data const & telemetry_data_a, nano::node const & node_server_a)
{
ASSERT_EQ (telemetry_data_a.block_count, 1);
ASSERT_EQ (telemetry_data_a.cemented_count, 1);
ASSERT_EQ (telemetry_data_a.bandwidth_cap, node_server_a.config.bandwidth_limit);
ASSERT_EQ (telemetry_data_a.peer_count, 1);
ASSERT_EQ (telemetry_data_a.protocol_version, node_server_a.network_params.protocol.telemetry_protocol_version_min);
ASSERT_EQ (telemetry_data_a.unchecked_count, 0);
ASSERT_EQ (telemetry_data_a.account_count, 1);
ASSERT_LT (telemetry_data_a.uptime, 100);
ASSERT_EQ (telemetry_data_a.genesis_block, node_server_a.network_params.ledger.genesis_hash);
ASSERT_EQ (telemetry_data_a.major_version, nano::get_major_node_version ());
ASSERT_EQ (*telemetry_data_a.minor_version, nano::get_minor_node_version ());
ASSERT_EQ (*telemetry_data_a.patch_version, nano::get_patch_node_version ());
ASSERT_EQ (*telemetry_data_a.pre_release_version, nano::get_pre_release_node_version ());
ASSERT_EQ (*telemetry_data_a.maker, 0);
ASSERT_GT (*telemetry_data_a.timestamp, std::chrono::system_clock::now () - 100s);
}
}

View file

@ -3,6 +3,14 @@
#include <nano/lib/locks.hpp>
#include <nano/lib/timer.hpp>
// This test header is unfortunately included in many different files.
// Requires guarding in some cases.
#ifndef IGNORE_GTEST_INCL
#include <nano/node/common.hpp>
#include <gtest/gtest.h>
#endif
#include <boost/iostreams/concepts.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/console.hpp>
@ -229,4 +237,37 @@ inline uint16_t get_available_port ()
return available_port;
}
#ifndef IGNORE_GTEST_INCL
inline void compare_default_telemetry_response_data_excluding_signature (nano::telemetry_data const & telemetry_data_a, nano::network_params const & network_params_a, uint64_t bandwidth_limit_a)
{
ASSERT_EQ (telemetry_data_a.block_count, 1);
ASSERT_EQ (telemetry_data_a.cemented_count, 1);
ASSERT_EQ (telemetry_data_a.bandwidth_cap, bandwidth_limit_a);
ASSERT_EQ (telemetry_data_a.peer_count, 1);
ASSERT_EQ (telemetry_data_a.protocol_version, network_params_a.protocol.telemetry_protocol_version_min);
ASSERT_EQ (telemetry_data_a.unchecked_count, 0);
ASSERT_EQ (telemetry_data_a.account_count, 1);
ASSERT_LT (telemetry_data_a.uptime, 100);
ASSERT_EQ (telemetry_data_a.genesis_block, network_params_a.ledger.genesis_hash);
ASSERT_EQ (telemetry_data_a.major_version, nano::get_major_node_version ());
ASSERT_EQ (*telemetry_data_a.minor_version, nano::get_minor_node_version ());
ASSERT_EQ (*telemetry_data_a.patch_version, nano::get_patch_node_version ());
ASSERT_EQ (*telemetry_data_a.pre_release_version, nano::get_pre_release_node_version ());
ASSERT_EQ (*telemetry_data_a.maker, 0);
ASSERT_GT (*telemetry_data_a.timestamp, std::chrono::system_clock::now () - std::chrono::seconds (100));
}
inline void compare_default_telemetry_response_data (nano::telemetry_data const & telemetry_data_a, nano::network_params const & network_params_a, uint64_t bandwidth_limit_a, nano::keypair const & node_id_a)
{
ASSERT_FALSE (telemetry_data_a.validate_signature (nano::telemetry_data::size));
nano::telemetry_data telemetry_data_l = telemetry_data_a;
telemetry_data_l.signature.clear ();
telemetry_data_l.sign (node_id_a);
// Signature should be different because uptime/timestamp will have changed.
ASSERT_NE (telemetry_data_a.signature, telemetry_data_l.signature);
compare_default_telemetry_response_data_excluding_signature (telemetry_data_a, network_params_a, bandwidth_limit_a);
ASSERT_EQ (telemetry_data_a.node_id, node_id_a.pub);
}
#endif
}

View file

@ -396,13 +396,6 @@ nano::private_key const & nano::raw_key::as_private_key () const
return reinterpret_cast<nano::private_key const &> (data);
}
nano::signature nano::sign_message (nano::raw_key const & private_key, nano::public_key const & public_key, nano::uint256_union const & message)
{
nano::signature result;
ed25519_sign (message.bytes.data (), sizeof (message.bytes), private_key.data.bytes.data (), public_key.bytes.data (), result.bytes.data ());
return result;
}
nano::private_key nano::deterministic_key (nano::raw_key const & seed_a, uint32_t index_a)
{
nano::private_key prv_key;
@ -422,10 +415,26 @@ nano::public_key nano::pub_key (nano::private_key const & privatekey_a)
return result;
}
nano::signature nano::sign_message (nano::raw_key const & private_key, nano::public_key const & public_key, uint8_t const * data, size_t size)
{
nano::signature result;
ed25519_sign (data, size, private_key.data.bytes.data (), public_key.bytes.data (), result.bytes.data ());
return result;
}
nano::signature nano::sign_message (nano::raw_key const & private_key, nano::public_key const & public_key, nano::uint256_union const & message)
{
return nano::sign_message (private_key, public_key, message.bytes.data (), sizeof (message.bytes));
}
bool nano::validate_message (nano::public_key const & public_key, uint8_t const * data, size_t size, nano::signature const & signature)
{
return 0 != ed25519_sign_open (data, size, public_key.bytes.data (), signature.bytes.data ());
}
bool nano::validate_message (nano::public_key const & public_key, nano::uint256_union const & message, nano::signature const & signature)
{
auto result (0 != ed25519_sign_open (message.bytes.data (), sizeof (message.bytes), public_key.bytes.data (), signature.bytes.data ()));
return result;
return validate_message (public_key, message.bytes.data (), sizeof (message.bytes), signature);
}
bool nano::validate_message_batch (const unsigned char ** m, size_t * mlen, const unsigned char ** pk, const unsigned char ** RS, size_t num, int * valid)

View file

@ -243,8 +243,10 @@ public:
};
nano::signature sign_message (nano::raw_key const &, nano::public_key const &, nano::uint256_union const &);
nano::signature sign_message (nano::raw_key const &, nano::public_key const &, uint8_t const *, size_t);
bool validate_message (nano::public_key const &, nano::uint256_union const &, nano::signature const &);
bool validate_message_batch (const unsigned char **, size_t *, const unsigned char **, const unsigned char **, size_t, int *);
bool validate_message (nano::public_key const &, uint8_t const *, size_t, nano::signature const &);
bool validate_message_batch (unsigned const char **, size_t *, unsigned const char **, unsigned const char **, size_t, int *);
nano::private_key deterministic_key (nano::raw_key const &, uint32_t);
nano::public_key pub_key (nano::private_key const &);

View file

@ -3,6 +3,7 @@
#include <nano/boost/beast/core/flat_buffer.hpp>
#include <nano/boost/beast/http.hpp>
#include <nano/boost/process/child.hpp>
#define IGNORE_GTEST_INCL
#include <nano/core_test/testutil.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/tomlconfig.hpp>

View file

@ -1117,21 +1117,7 @@ void nano::telemetry_ack::serialize (nano::stream & stream_a) const
header.serialize (stream_a);
if (!is_empty_payload ())
{
write (stream_a, data.block_count);
write (stream_a, data.cemented_count);
write (stream_a, data.unchecked_count);
write (stream_a, data.account_count);
write (stream_a, data.bandwidth_cap);
write (stream_a, data.peer_count);
write (stream_a, data.protocol_version);
write (stream_a, data.major_version);
write (stream_a, data.uptime);
write (stream_a, data.genesis_block.bytes);
write (stream_a, *data.minor_version);
write (stream_a, *data.patch_version);
write (stream_a, *data.pre_release_version);
write (stream_a, *data.maker);
write (stream_a, std::chrono::duration_cast<std::chrono::milliseconds> (data.timestamp->time_since_epoch ()).count ());
data.serialize (stream_a);
}
}
@ -1143,36 +1129,7 @@ bool nano::telemetry_ack::deserialize (nano::stream & stream_a)
{
if (!is_empty_payload ())
{
read (stream_a, data.block_count);
read (stream_a, data.cemented_count);
read (stream_a, data.unchecked_count);
read (stream_a, data.account_count);
read (stream_a, data.bandwidth_cap);
read (stream_a, data.peer_count);
read (stream_a, data.protocol_version);
read (stream_a, data.major_version);
read (stream_a, data.uptime);
read (stream_a, data.genesis_block.bytes);
if (header.extensions.to_ulong () > telemetry_data::size_v0)
{
uint8_t out;
read (stream_a, out);
data.minor_version = out;
read (stream_a, out);
data.patch_version = out;
read (stream_a, out);
data.pre_release_version = out;
read (stream_a, out);
data.maker = out;
}
if (header.extensions.to_ulong () > telemetry_data::size_v1)
{
uint64_t timestamp;
read (stream_a, timestamp);
data.timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp));
}
data.deserialize (stream_a, header.extensions.to_ulong ());
}
}
catch (std::runtime_error const &)
@ -1203,8 +1160,75 @@ bool nano::telemetry_ack::is_empty_payload () const
return size () == 0;
}
nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json) const
void nano::telemetry_data::deserialize (nano::stream & stream_a, uint16_t payload_length_a)
{
read (stream_a, signature);
read (stream_a, node_id);
read (stream_a, block_count);
read (stream_a, cemented_count);
read (stream_a, unchecked_count);
read (stream_a, account_count);
read (stream_a, bandwidth_cap);
read (stream_a, peer_count);
read (stream_a, protocol_version);
read (stream_a, major_version);
read (stream_a, uptime);
read (stream_a, genesis_block.bytes);
if (payload_length_a > size_v0)
{
uint8_t out;
read (stream_a, out);
minor_version = out;
read (stream_a, out);
patch_version = out;
read (stream_a, out);
pre_release_version = out;
read (stream_a, out);
maker = out;
}
if (payload_length_a > size_v1)
{
uint64_t timestamp_l;
read (stream_a, timestamp_l);
timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l));
}
}
void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a, uint16_t /* size_a */) const
{
write (stream_a, node_id);
write (stream_a, block_count);
write (stream_a, cemented_count);
write (stream_a, unchecked_count);
write (stream_a, account_count);
write (stream_a, bandwidth_cap);
write (stream_a, peer_count);
write (stream_a, protocol_version);
write (stream_a, major_version);
write (stream_a, uptime);
write (stream_a, genesis_block.bytes);
write (stream_a, *minor_version);
write (stream_a, *patch_version);
write (stream_a, *pre_release_version);
write (stream_a, *maker);
write (stream_a, std::chrono::duration_cast<std::chrono::milliseconds> (timestamp->time_since_epoch ()).count ());
}
void nano::telemetry_data::serialize (nano::stream & stream_a) const
{
write (stream_a, signature);
serialize_without_signature (stream_a, size);
}
nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) const
{
if (!ignore_identification_metrics_a)
{
json.put ("signature", signature.to_string ());
json.put ("node_id", node_id.to_string ());
}
json.put ("block_count", block_count);
json.put ("cemented_count", cemented_count);
json.put ("unchecked_count", unchecked_count);
@ -1238,8 +1262,31 @@ nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json) const
return json.get_error ();
}
nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json)
nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a)
{
if (!ignore_identification_metrics_a)
{
std::string signature_l;
json.get ("signature", signature_l);
if (!json.get_error ())
{
if (signature.decode_hex (signature_l))
{
json.get_error ().set ("Could not deserialize signature");
}
}
std::string node_id_l;
json.get ("node_id", node_id_l);
if (!json.get_error ())
{
if (node_id.decode_hex (node_id_l))
{
json.get_error ().set ("Could not deserialize node id");
}
}
}
json.get ("block_count", block_count);
json.get ("cemented_count", cemented_count);
json.get ("unchecked_count", unchecked_count);
@ -1273,7 +1320,7 @@ nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json)
bool nano::telemetry_data::operator== (nano::telemetry_data const & data_a) const
{
return (block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp);
return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp);
}
bool nano::telemetry_data::operator!= (nano::telemetry_data const & data_a) const
@ -1281,6 +1328,29 @@ bool nano::telemetry_data::operator!= (nano::telemetry_data const & data_a) cons
return !(*this == data_a);
}
void nano::telemetry_data::sign (nano::keypair const & node_id_a)
{
debug_assert (node_id == node_id_a.pub);
std::vector<uint8_t> bytes;
{
nano::vectorstream stream (bytes);
serialize_without_signature (stream, size);
}
signature = nano::sign_message (node_id_a.prv, node_id_a.pub, bytes.data (), bytes.size ());
}
bool nano::telemetry_data::validate_signature (uint16_t size_a) const
{
std::vector<uint8_t> bytes;
{
nano::vectorstream stream (bytes);
serialize_without_signature (stream, size_a);
}
return nano::validate_message (node_id, bytes.data (), bytes.size (), signature);
}
nano::node_id_handshake::node_id_handshake (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) :
message (header_a),
query (boost::none),

View file

@ -339,6 +339,8 @@ public:
class telemetry_data
{
public:
nano::signature signature{ 0 };
nano::account node_id{ 0 };
uint64_t block_count{ 0 };
uint64_t cemented_count{ 0 };
uint64_t unchecked_count{ 0 };
@ -355,14 +357,21 @@ public:
boost::optional<uint8_t> maker; // 0 for NF node
boost::optional<std::chrono::system_clock::time_point> timestamp;
nano::error serialize_json (nano::jsonconfig & json) const;
nano::error deserialize_json (nano::jsonconfig & json);
void serialize (nano::stream &) const;
void deserialize (nano::stream &, uint16_t);
nano::error serialize_json (nano::jsonconfig &, bool) const;
nano::error deserialize_json (nano::jsonconfig &, bool);
void sign (nano::keypair const &);
bool validate_signature (uint16_t) const;
bool operator== (nano::telemetry_data const &) const;
bool operator!= (nano::telemetry_data const &) const;
static auto constexpr size_v0 = sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version);
static auto constexpr size_v0 = sizeof (signature) + sizeof (node_id) + sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version);
static auto constexpr size_v1 = size_v0 + sizeof (decltype (minor_version)::value_type) + sizeof (decltype (patch_version)::value_type) + sizeof (decltype (pre_release_version)::value_type) + sizeof (decltype (maker)::value_type);
static auto constexpr size = size_v1 + sizeof (uint64_t);
private:
void serialize_without_signature (nano::stream &, uint16_t) const;
};
class telemetry_req final : public message
{

View file

@ -3940,10 +3940,11 @@ void nano::json_handler::telemetry ()
if (address.is_loopback () && port == rpc_l->node.network.endpoint ().port ())
{
// Requesting telemetry metrics locally
auto telemetry_data = nano::local_telemetry_data (rpc_l->node.ledger.cache, rpc_l->node.network, rpc_l->node.config.bandwidth_limit, rpc_l->node.network_params, rpc_l->node.startup_time);
auto telemetry_data = nano::local_telemetry_data (rpc_l->node.ledger.cache, rpc_l->node.network, rpc_l->node.config.bandwidth_limit, rpc_l->node.network_params, rpc_l->node.startup_time, rpc_l->node.node_id);
nano::jsonconfig config_l;
auto err = telemetry_data.serialize_json (config_l);
auto const should_ignore_identification_metrics = false;
auto err = telemetry_data.serialize_json (config_l, should_ignore_identification_metrics);
auto const & ptree = config_l.get_tree ();
if (!err)
@ -3987,7 +3988,8 @@ void nano::json_handler::telemetry ()
if (!telemetry_response_a.error)
{
nano::jsonconfig config_l;
auto err = telemetry_response_a.telemetry_data.serialize_json (config_l);
auto const should_ignore_identification_metrics = false;
auto err = telemetry_response_a.telemetry_data.serialize_json (config_l, should_ignore_identification_metrics);
auto const & ptree = config_l.get_tree ();
if (!err)
@ -4032,7 +4034,8 @@ void nano::json_handler::telemetry ()
for (auto & telemetry_metrics : telemetry_responses)
{
nano::jsonconfig config_l;
auto err = telemetry_metrics.second.serialize_json (config_l);
auto const should_ignore_identification_metrics = false;
auto err = telemetry_metrics.second.serialize_json (config_l, should_ignore_identification_metrics);
config_l.put ("address", telemetry_metrics.first.address ());
config_l.put ("port", telemetry_metrics.first.port ());
if (!err)
@ -4057,7 +4060,9 @@ void nano::json_handler::telemetry ()
});
auto average_telemetry_metrics = nano::consolidate_telemetry_data (telemetry_datas);
auto err = average_telemetry_metrics.serialize_json (config_l);
// Don't add node_id/signature in consolidated metrics
auto const should_ignore_identification_metrics = true;
auto err = average_telemetry_metrics.serialize_json (config_l, should_ignore_identification_metrics);
auto const & ptree = config_l.get_tree ();
if (!err)

View file

@ -483,7 +483,7 @@ public:
nano::telemetry_ack telemetry_ack;
if (!node.flags.disable_providing_telemetry_metrics)
{
auto telemetry_data = nano::local_telemetry_data (node.ledger.cache, node.network, node.config.bandwidth_limit, node.network_params, node.startup_time);
auto telemetry_data = nano::local_telemetry_data (node.ledger.cache, node.network, node.config.bandwidth_limit, node.network_params, node.startup_time, node.node_id);
telemetry_ack = nano::telemetry_ack (telemetry_data);
}
channel->send (telemetry_ack, nullptr, nano::buffer_drop_policy::no_socket_drop);
@ -497,7 +497,7 @@ public:
node.stats.inc (nano::stat::type::message, nano::stat::detail::telemetry_ack, nano::stat::dir::in);
if (node.telemetry)
{
node.telemetry->set (message_a.data, channel->get_endpoint (), message_a.is_empty_payload ());
node.telemetry->set (message_a, *channel);
}
}
nano::node & node;

View file

@ -1,3 +1,4 @@
#define IGNORE_GTEST_INCL
#include <nano/core_test/testutil.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/tomlconfig.hpp>

View file

@ -37,24 +37,30 @@ void nano::telemetry::stop ()
stopped = true;
}
void nano::telemetry::set (nano::telemetry_data const & telemetry_data_a, nano::endpoint const & endpoint_a, bool is_empty_a)
void nano::telemetry::set (nano::telemetry_ack const & message_a, nano::transport::channel const & channel_a)
{
if (!stopped)
{
nano::lock_guard<std::mutex> guard (mutex);
auto it = recent_or_initial_request_telemetry_data.find (endpoint_a);
auto it = recent_or_initial_request_telemetry_data.find (channel_a.get_endpoint ());
if (it == recent_or_initial_request_telemetry_data.cend ())
{
// Not requesting telemetry data from this peer so ignore it
return;
}
recent_or_initial_request_telemetry_data.modify (it, [&telemetry_data_a](nano::telemetry_info & telemetry_info_a) {
telemetry_info_a.data = telemetry_data_a;
recent_or_initial_request_telemetry_data.modify (it, [&message_a](nano::telemetry_info & telemetry_info_a) {
telemetry_info_a.data = message_a.data;
telemetry_info_a.undergoing_request = false;
});
channel_processed (endpoint_a, is_empty_a);
auto error = false;
if (!message_a.is_empty_payload ())
{
error = !message_a.data.validate_signature (message_a.size ()) && (channel_a.get_node_id () != message_a.data.node_id);
}
channel_processed (channel_a.get_endpoint (), error || message_a.is_empty_payload ());
}
}
@ -574,9 +580,10 @@ nano::telemetry_data nano::consolidate_telemetry_data (std::vector<nano::telemet
return consolidated_data;
}
nano::telemetry_data nano::local_telemetry_data (nano::ledger_cache const & ledger_cache_a, nano::network & network_a, uint64_t bandwidth_limit_a, nano::network_params const & network_params_a, std::chrono::steady_clock::time_point statup_time_a)
nano::telemetry_data nano::local_telemetry_data (nano::ledger_cache const & ledger_cache_a, nano::network & network_a, uint64_t bandwidth_limit_a, nano::network_params const & network_params_a, std::chrono::steady_clock::time_point statup_time_a, nano::keypair const & node_id_a)
{
nano::telemetry_data telemetry_data;
telemetry_data.node_id = node_id_a.pub;
telemetry_data.block_count = ledger_cache_a.block_count;
telemetry_data.cemented_count = ledger_cache_a.cemented_count;
telemetry_data.bandwidth_cap = bandwidth_limit_a;
@ -592,5 +599,7 @@ nano::telemetry_data nano::local_telemetry_data (nano::ledger_cache const & ledg
telemetry_data.pre_release_version = nano::get_pre_release_node_version ();
telemetry_data.maker = 0; // 0 Indicates it originated from the NF
telemetry_data.timestamp = std::chrono::system_clock::now ();
// Make sure this is the final operation!
telemetry_data.sign (node_id_a);
return telemetry_data;
}

View file

@ -63,9 +63,9 @@ public:
void stop ();
/*
* Set the telemetry data associated with this peer
* Received telemetry metrics from this peer
*/
void set (nano::telemetry_data const &, nano::endpoint const &, bool);
void set (nano::telemetry_ack const &, nano::transport::channel const &);
/*
* This returns what ever is in the cache
@ -142,5 +142,5 @@ private:
std::unique_ptr<nano::container_info_component> collect_container_info (telemetry & telemetry, const std::string & name);
nano::telemetry_data consolidate_telemetry_data (std::vector<telemetry_data> const & telemetry_data);
nano::telemetry_data local_telemetry_data (nano::ledger_cache const &, nano::network &, uint64_t, nano::network_params const &, std::chrono::steady_clock::time_point);
}
nano::telemetry_data local_telemetry_data (nano::ledger_cache const &, nano::network &, uint64_t, nano::network_params const &, std::chrono::steady_clock::time_point, nano::keypair const & node_id_a);
}

View file

@ -1,3 +1,4 @@
#define IGNORE_GTEST_INCL
#include <nano/core_test/testutil.hpp>
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/node/common.hpp>

View file

@ -7850,29 +7850,6 @@ TEST (rpc, receive_work_disabled)
}
}
namespace
{
void compare_default_test_result_data (test_response & response, nano::node const & node_server_a)
{
ASSERT_EQ (200, response.status);
ASSERT_EQ (1, response.json.get<uint64_t> ("block_count"));
ASSERT_EQ (1, response.json.get<uint64_t> ("cemented_count"));
ASSERT_EQ (0, response.json.get<uint64_t> ("unchecked_count"));
ASSERT_EQ (1, response.json.get<uint64_t> ("account_count"));
ASSERT_EQ (node_server_a.config.bandwidth_limit, response.json.get<uint64_t> ("bandwidth_cap"));
ASSERT_EQ (1, response.json.get<uint32_t> ("peer_count"));
ASSERT_EQ (node_server_a.network_params.protocol.protocol_version, response.json.get<uint8_t> ("protocol_version"));
ASSERT_GE (100, response.json.get<uint64_t> ("uptime"));
ASSERT_EQ (node_server_a.network_params.ledger.genesis_hash.to_string (), response.json.get<std::string> ("genesis_block"));
ASSERT_EQ (nano::get_major_node_version (), response.json.get<uint8_t> ("major_version"));
ASSERT_EQ (nano::get_minor_node_version (), response.json.get<uint8_t> ("minor_version"));
ASSERT_EQ (nano::get_patch_node_version (), response.json.get<uint8_t> ("patch_version"));
ASSERT_EQ (nano::get_pre_release_node_version (), response.json.get<uint8_t> ("pre_release_version"));
ASSERT_EQ (0, response.json.get<uint8_t> ("maker"));
ASSERT_GE (std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()).count (), response.json.get<uint64_t> ("timestamp"));
}
}
TEST (rpc, node_telemetry_single)
{
nano::system system (1);
@ -7967,7 +7944,12 @@ TEST (rpc, node_telemetry_single)
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
compare_default_test_result_data (response, *node);
nano::jsonconfig config (response.json);
nano::telemetry_data telemetry_data;
auto const should_ignore_identification_metrics = false;
ASSERT_FALSE (telemetry_data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data (telemetry_data, node->network_params, node->config.bandwidth_limit, node->node_id);
}
}
@ -8017,7 +7999,13 @@ TEST (rpc, node_telemetry_all)
{
ASSERT_NO_ERROR (system.poll ());
}
compare_default_test_result_data (response, *node);
nano::jsonconfig config (response.json);
nano::telemetry_data telemetry_data;
auto const should_ignore_identification_metrics = true;
ASSERT_FALSE (telemetry_data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data_excluding_signature (telemetry_data, node->network_params, node->config.bandwidth_limit);
ASSERT_FALSE (response.json.get_optional<std::string> ("node_id").is_initialized ());
ASSERT_FALSE (response.json.get_optional<std::string> ("signature").is_initialized ());
}
request.put ("raw", "true");
@ -8031,55 +8019,17 @@ TEST (rpc, node_telemetry_all)
// This may fail if the response has taken longer than the cache cutoff time.
auto & all_metrics = response.json.get_child ("metrics");
auto & metrics = all_metrics.front ().second;
ASSERT_EQ (1, all_metrics.size ());
class telemetry_response_data
{
public:
uint64_t block_count;
uint64_t cemented_count;
uint64_t unchecked_count;
uint64_t account_count;
uint64_t bandwidth_cap;
uint32_t peer_count;
uint8_t protocol_version;
uint64_t uptime;
std::string genesis_block;
uint8_t major_version;
uint8_t minor_version;
uint8_t patch_version;
uint8_t pre_release_version;
uint8_t maker;
uint64_t timestamp;
std::string address;
uint16_t port;
};
nano::jsonconfig config (metrics);
nano::telemetry_data data;
auto const should_ignore_identification_metrics = false;
ASSERT_FALSE (data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data (data, node->network_params, node->config.bandwidth_limit, node->node_id);
std::vector<telemetry_response_data> raw_metrics_json_l;
for (auto & metrics_pair : all_metrics)
{
auto & metrics = metrics_pair.second;
raw_metrics_json_l.push_back ({ metrics.get<uint64_t> ("block_count"), metrics.get<uint64_t> ("cemented_count"), metrics.get<uint64_t> ("unchecked_count"), metrics.get<uint64_t> ("account_count"), metrics.get<uint64_t> ("bandwidth_cap"), metrics.get<uint32_t> ("peer_count"), metrics.get<uint8_t> ("protocol_version"), metrics.get<uint64_t> ("uptime"), metrics.get<std::string> ("genesis_block"), metrics.get<uint8_t> ("major_version"), metrics.get<uint8_t> ("minor_version"), metrics.get<uint8_t> ("patch_version"), metrics.get<uint8_t> ("pre_release_version"), metrics.get<uint8_t> ("maker"), metrics.get<uint64_t> ("timestamp"), metrics.get<std::string> ("address"), metrics.get<uint16_t> ("port") });
}
ASSERT_EQ (1, raw_metrics_json_l.size ());
auto const & metrics = raw_metrics_json_l.front ();
ASSERT_EQ (1, metrics.block_count);
ASSERT_EQ (1, metrics.cemented_count);
ASSERT_EQ (0, metrics.unchecked_count);
ASSERT_EQ (1, metrics.account_count);
ASSERT_EQ (node->config.bandwidth_limit, metrics.bandwidth_cap);
ASSERT_EQ (1, metrics.peer_count);
ASSERT_EQ (node->network_params.protocol.protocol_version, metrics.protocol_version);
ASSERT_GE (100, metrics.uptime);
ASSERT_EQ (node1.network_params.ledger.genesis_hash.to_string (), metrics.genesis_block);
ASSERT_EQ (nano::get_major_node_version (), metrics.major_version);
ASSERT_EQ (nano::get_minor_node_version (), metrics.minor_version);
ASSERT_EQ (nano::get_patch_node_version (), metrics.patch_version);
ASSERT_EQ (nano::get_pre_release_node_version (), metrics.pre_release_version);
ASSERT_EQ (0, metrics.maker);
ASSERT_GE (std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()).count (), metrics.timestamp);
ASSERT_EQ (node->network.endpoint ().address ().to_string (), metrics.address);
ASSERT_EQ (node->network.endpoint ().port (), metrics.port);
ASSERT_EQ (node->network.endpoint ().address ().to_string (), metrics.get<std::string> ("address"));
ASSERT_EQ (node->network.endpoint ().port (), metrics.get<uint16_t> ("port"));
}
// Also tests all forms of ipv4/ipv6
@ -8103,6 +8053,7 @@ TEST (rpc, node_telemetry_self)
request.put ("action", "node_telemetry");
request.put ("address", "::1");
request.put ("port", node1.network.endpoint ().port ());
auto const should_ignore_identification_metrics = false;
{
test_response response (request, rpc.config.port, system.io_ctx);
system.deadline_set (10s);
@ -8111,7 +8062,10 @@ TEST (rpc, node_telemetry_self)
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
compare_default_test_result_data (response, node1);
nano::telemetry_data data;
nano::jsonconfig config (response.json);
ASSERT_FALSE (data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data (data, node1.network_params, node1.config.bandwidth_limit, node1.node_id);
}
request.put ("address", "[::1]");
@ -8123,7 +8077,10 @@ TEST (rpc, node_telemetry_self)
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
compare_default_test_result_data (response, node1);
nano::telemetry_data data;
nano::jsonconfig config (response.json);
ASSERT_FALSE (data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data (data, node1.network_params, node1.config.bandwidth_limit, node1.node_id);
}
request.put ("address", "127.0.0.1");
@ -8135,7 +8092,10 @@ TEST (rpc, node_telemetry_self)
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
compare_default_test_result_data (response, node1);
nano::telemetry_data data;
nano::jsonconfig config (response.json);
ASSERT_FALSE (data.deserialize_json (config, should_ignore_identification_metrics));
nano::compare_default_telemetry_response_data (data, node1.network_params, node1.config.bandwidth_limit, node1.node_id);
}
// Incorrect port should fail

View file

@ -1,3 +1,4 @@
#define IGNORE_GTEST_INCL
#include <nano/core_test/testutil.hpp>
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/config.hpp>