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:
parent
15eebb1ca6
commit
35cca9a576
15 changed files with 291 additions and 182 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 &);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#define IGNORE_GTEST_INCL
|
||||
#include <nano/core_test/testutil.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue