Skip to content

Commit cff1dbd

Browse files
pwojcikdevclemahieu
authored andcommitted
Connect handshake v2 messages to the node.
1 parent 427fded commit cff1dbd

File tree

8 files changed

+122
-41
lines changed

8 files changed

+122
-41
lines changed

nano/core_test/telemetry.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ TEST (telemetry, ongoing_broadcasts)
509509
ASSERT_TIMELY (5s, node2.stats.count (nano::stat::type::telemetry, nano::stat::detail::process) >= 3)
510510
}
511511

512-
TEST (telemetry, mismatched_genesis)
512+
// TODO: With handshake V2, nodes with mismatched genesis will refuse to connect while setting up the system
513+
TEST (telemetry, DISABLED_mismatched_genesis)
513514
{
514515
// Only second node will broadcast telemetry
515516
nano::test::system system;

nano/lib/stats_enums.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ enum class type : uint8_t
4343
unchecked,
4444
election_scheduler,
4545
optimistic_scheduler,
46+
handshake,
4647

4748
_last // Must be the last enum
4849
};
@@ -53,6 +54,7 @@ enum class detail : uint8_t
5354
all = 0,
5455

5556
// common
57+
ok,
5658
loop,
5759
total,
5860
process,
@@ -273,6 +275,11 @@ enum class detail : uint8_t
273275
insert_priority_success,
274276
erase_oldest,
275277

278+
// handshake
279+
invalid_node_id,
280+
missing_cookie,
281+
invalid_genesis,
282+
276283
_last // Must be the last enum
277284
};
278285

nano/node/network.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,68 @@ void nano::network::exclude (std::shared_ptr<nano::transport::channel> const & c
759759
erase (*channel);
760760
}
761761

762+
bool nano::network::verify_handshake_response (const nano::node_id_handshake::response_payload & response, const nano::endpoint & remote_endpoint)
763+
{
764+
// Prevent connection with ourselves
765+
if (response.node_id == node.node_id.pub)
766+
{
767+
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_node_id);
768+
return false; // Fail
769+
}
770+
771+
// Prevent mismatched genesis
772+
if (response.v2 && response.v2->genesis != node.network_params.ledger.genesis->hash ())
773+
{
774+
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_genesis);
775+
return false; // Fail
776+
}
777+
778+
auto cookie = syn_cookies.cookie (remote_endpoint);
779+
if (!cookie)
780+
{
781+
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::missing_cookie);
782+
return false; // Fail
783+
}
784+
785+
if (!response.validate (*cookie))
786+
{
787+
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::invalid_signature);
788+
return false; // Fail
789+
}
790+
791+
node.stats.inc (nano::stat::type::handshake, nano::stat::detail::ok);
792+
return true; // OK
793+
}
794+
795+
std::optional<nano::node_id_handshake::query_payload> nano::network::prepare_handshake_query (const nano::endpoint & remote_endpoint)
796+
{
797+
if (auto cookie = syn_cookies.assign (remote_endpoint); cookie)
798+
{
799+
nano::node_id_handshake::query_payload query{ *cookie };
800+
return query;
801+
}
802+
return std::nullopt;
803+
}
804+
805+
nano::node_id_handshake::response_payload nano::network::prepare_handshake_response (const nano::node_id_handshake::query_payload & query, bool v2) const
806+
{
807+
nano::node_id_handshake::response_payload response{};
808+
response.node_id = node.node_id.pub;
809+
if (v2)
810+
{
811+
nano::node_id_handshake::response_payload::v2_payload response_v2{};
812+
response_v2.salt = nano::random_pool::generate<uint256_union> ();
813+
response_v2.genesis = node.network_params.ledger.genesis->hash ();
814+
response.v2 = response_v2;
815+
}
816+
response.sign (query.cookie, node.node_id);
817+
return response;
818+
}
819+
820+
/*
821+
* tcp_message_manager
822+
*/
823+
762824
nano::tcp_message_manager::tcp_message_manager (unsigned incoming_connections_max_a) :
763825
max_entries (incoming_connections_max_a * nano::tcp_message_manager::max_entries_per_connection + 1)
764826
{

nano/node/network.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ class network final
128128
/** Disconnects and adds peer to exclusion list */
129129
void exclude (std::shared_ptr<nano::transport::channel> const & channel);
130130

131+
/** Verifies that handshake response matches our query. @returns true if OK */
132+
bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint);
133+
std::optional<nano::node_id_handshake::query_payload> prepare_handshake_query (nano::endpoint const & remote_endpoint);
134+
nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const;
135+
131136
static std::string to_string (nano::networks);
132137

133138
private:

nano/node/transport/socket.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class socket : public std::enable_shared_from_this<nano::transport::socket>
4040
{
4141
friend class server_socket;
4242
friend class tcp_server;
43+
friend class tcp_channels;
4344

4445
public:
4546
enum class type_t

nano/node/transport/tcp.cpp

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <nano/lib/stats.hpp>
22
#include <nano/node/node.hpp>
3+
#include <nano/node/transport/message_deserializer.hpp>
34
#include <nano/node/transport/tcp.hpp>
45

56
#include <boost/format.hpp>
@@ -544,14 +545,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a
544545
if (!ec && channel)
545546
{
546547
// TCP node ID handshake
547-
548-
std::optional<nano::node_id_handshake::query_payload> query;
549-
if (auto cookie = node_l->network.syn_cookies.assign (endpoint_a); cookie)
550-
{
551-
nano::node_id_handshake::query_payload pld{ *cookie };
552-
query = pld;
553-
}
554-
548+
auto query = node_l->network.prepare_handshake_query (endpoint_a);
555549
nano::node_id_handshake message{ node_l->network_params.network, query };
556550

557551
if (node_l->config.logging.network_node_id_handshake_logging ())
@@ -619,7 +613,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
619613
}
620614
};
621615

622-
socket_l->async_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {
616+
auto message_deserializer = std::make_shared<nano::transport::message_deserializer> (node.network_params.network, node.network.publish_filter, node.block_uniquer, node.vote_uniquer,
617+
[socket_l] (std::shared_ptr<std::vector<uint8_t>> const & data_a, size_t size_a, std::function<void (boost::system::error_code const &, std::size_t)> callback_a) {
618+
debug_assert (socket_l != nullptr);
619+
socket_l->read_impl (data_a, size_a, callback_a);
620+
});
621+
message_deserializer->read ([node_w, socket_l, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code ec, std::unique_ptr<nano::message> message) {
623622
auto node_l = node_w.lock ();
624623
if (!node_l)
625624
{
@@ -636,10 +635,9 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
636635
}
637636
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::in);
638637
auto error (false);
639-
nano::bufferstream stream (receive_buffer_a->data (), size_a);
640-
nano::message_header header (error, stream);
638+
641639
// the header type should in principle be checked after checking the network bytes and the version numbers, I will not change it here since the benefits do not outweight the difficulties
642-
if (error || header.type != nano::message_type::node_id_handshake)
640+
if (error || message->type () != nano::message_type::node_id_handshake)
643641
{
644642
if (node_l->config.logging.network_node_id_handshake_logging ())
645643
{
@@ -648,10 +646,12 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
648646
cleanup_node_id_handshake_socket (endpoint_a);
649647
return;
650648
}
651-
if (header.network != node_l->network_params.network.current_network || header.version_using < node_l->network_params.network.protocol_version_min)
649+
auto & handshake = static_cast<nano::node_id_handshake &> (*message);
650+
651+
if (message->header.network != node_l->network_params.network.current_network || message->header.version_using < node_l->network_params.network.protocol_version_min)
652652
{
653653
// error handling, either the networks bytes or the version is wrong
654-
if (header.network == node_l->network_params.network.current_network)
654+
if (message->header.network == node_l->network_params.network.current_network)
655655
{
656656
node_l->stats.inc (nano::stat::type::message, nano::stat::detail::invalid_network);
657657
}
@@ -668,8 +668,8 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
668668
}
669669
return;
670670
}
671-
nano::node_id_handshake message (error, stream, header);
672-
if (error || !message.response || !message.query)
671+
672+
if (error || !handshake.response || !handshake.query)
673673
{
674674
if (node_l->config.logging.network_node_id_handshake_logging ())
675675
{
@@ -678,15 +678,16 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
678678
cleanup_node_id_handshake_socket (endpoint_a);
679679
return;
680680
}
681-
channel_a->set_network_version (header.version_using);
681+
channel_a->set_network_version (handshake.header.version_using);
682+
683+
debug_assert (handshake.query);
684+
debug_assert (handshake.response);
682685

683-
debug_assert (message.query);
684-
debug_assert (message.response);
686+
auto const node_id = handshake.response->node_id;
685687

686-
auto node_id = message.response->node_id;
687-
bool process = (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->signature) && node_id != node_l->node_id.pub);
688-
if (!process)
688+
if (!node_l->network.verify_handshake_response (*handshake.response, endpoint_a))
689689
{
690+
cleanup_node_id_handshake_socket (endpoint_a);
690691
return;
691692
}
692693

@@ -695,18 +696,20 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr<n
695696
auto existing_channel (node_l->network.tcp_channels.find_node_id (node_id));
696697
if (existing_channel && !existing_channel->temporary)
697698
{
699+
cleanup_node_id_handshake_socket (endpoint_a);
698700
return;
699701
}
700702

701703
channel_a->set_node_id (node_id);
702704
channel_a->set_last_packet_received (std::chrono::steady_clock::now ());
703705

704-
nano::node_id_handshake::response_payload response{ node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, message.query->cookie) };
706+
debug_assert (handshake.query);
707+
auto response = node_l->network.prepare_handshake_response (*handshake.query, handshake.is_v2 ());
705708
nano::node_id_handshake handshake_response (node_l->network_params.network, std::nullopt, response);
706709

707710
if (node_l->config.logging.network_node_id_handshake_logging ())
708711
{
709-
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % message.query->cookie.to_string ()));
712+
node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % handshake.query->cookie.to_string ()));
710713
}
711714

712715
channel_a->send (handshake_response, [node_w, channel_a, endpoint_a, cleanup_node_id_handshake_socket] (boost::system::error_code const & ec, std::size_t size_a) {

nano/node/transport/tcp_server.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
#include <memory>
1212

13+
/*
14+
* tcp_listener
15+
*/
16+
1317
nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a) :
1418
node (node_a),
1519
port (port_a)
@@ -123,6 +127,10 @@ std::unique_ptr<nano::container_info_component> nano::transport::collect_contain
123127
return composite;
124128
}
125129

130+
/*
131+
* tcp_server
132+
*/
133+
126134
nano::transport::tcp_server::tcp_server (std::shared_ptr<nano::transport::socket> socket_a, std::shared_ptr<nano::node> node_a, bool allow_bootstrap_a) :
127135
socket{ std::move (socket_a) },
128136
node{ std::move (node_a) },
@@ -315,6 +323,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
315323
{
316324
server->node->logger.try_log (boost::str (boost::format ("Disabled realtime TCP for handshake %1%") % server->remote_endpoint));
317325
}
326+
// Stop invalid handshake
318327
server->stop ();
319328
return;
320329
}
@@ -325,6 +334,7 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
325334
{
326335
server->node->logger.try_log (boost::str (boost::format ("Detected multiple node_id_handshake query from %1%") % server->remote_endpoint));
327336
}
337+
// Stop invalid handshake
328338
server->stop ();
329339
return;
330340
}
@@ -338,37 +348,29 @@ void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (
338348

339349
if (message.query)
340350
{
341-
server->send_handshake_response (*message.query);
351+
server->send_handshake_response (*message.query, message.is_v2 ());
342352
}
343353
if (message.response)
344354
{
345-
nano::account const & node_id = message.response->node_id;
346-
if (!server->node->network.syn_cookies.validate (nano::transport::map_tcp_to_endpoint (server->remote_endpoint), node_id, message.response->signature) && node_id != server->node->node_id.pub)
355+
if (server->node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (server->remote_endpoint)))
347356
{
348-
server->to_realtime_connection (node_id);
357+
server->to_realtime_connection (message.response->node_id);
349358
}
350359
else
351360
{
352361
// Stop invalid handshake
353362
server->stop ();
363+
return;
354364
}
355365
}
356366

357367
process = true;
358368
}
359369

360-
void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query)
370+
void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2)
361371
{
362-
nano::node_id_handshake::response_payload response{ node->node_id.pub, nano::sign_message (node->node_id.prv, node->node_id.pub, query.cookie) };
363-
debug_assert (!nano::validate_message (response.node_id, query.cookie, response.signature));
364-
365-
std::optional<nano::node_id_handshake::query_payload> own_query;
366-
if (auto own_cookie = node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (remote_endpoint)); own_cookie)
367-
{
368-
nano::node_id_handshake::query_payload pld{ *own_cookie };
369-
own_query = pld;
370-
}
371-
372+
auto response = node->network.prepare_handshake_response (query, v2);
373+
auto own_query = node->network.prepare_handshake_query (nano::transport::map_tcp_to_endpoint (remote_endpoint));
372374
nano::node_id_handshake handshake_response{ node->network_params.network, own_query, response };
373375

374376
// TODO: Use channel

nano/node/transport/tcp_server.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class tcp_server final : public std::enable_shared_from_this<tcp_server>
6363
std::chrono::steady_clock::time_point last_telemetry_req{};
6464

6565
private:
66-
void send_handshake_response (nano::node_id_handshake::query_payload const & query);
66+
void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2);
6767

6868
void receive_message ();
6969
void received_message (std::unique_ptr<nano::message> message);

0 commit comments

Comments
 (0)