774 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			774 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <nano/node/bootstrap/bootstrap_bulk_push.hpp>
 | |
| #include <nano/node/bootstrap/bootstrap_frontier.hpp>
 | |
| #include <nano/node/bootstrap/bootstrap_server.hpp>
 | |
| #include <nano/node/node.hpp>
 | |
| #include <nano/node/transport/tcp.hpp>
 | |
| 
 | |
| #include <boost/format.hpp>
 | |
| #include <boost/variant/get.hpp>
 | |
| 
 | |
| nano::bootstrap_listener::bootstrap_listener (uint16_t port_a, nano::node & node_a) :
 | |
| 	node (node_a),
 | |
| 	port (port_a)
 | |
| {
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_listener::start ()
 | |
| {
 | |
| 	nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 	on = true;
 | |
| 	listening_socket = std::make_shared<nano::server_socket> (node, boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::any (), port), node.config.tcp_incoming_connections_max);
 | |
| 	boost::system::error_code ec;
 | |
| 	listening_socket->start (ec);
 | |
| 	if (ec)
 | |
| 	{
 | |
| 		node.logger.try_log (boost::str (boost::format ("Error while binding for incoming TCP/bootstrap on port %1%: %2%") % listening_socket->listening_port () % ec.message ()));
 | |
| 		throw std::runtime_error (ec.message ());
 | |
| 	}
 | |
| 	debug_assert (node.network.endpoint ().port () == listening_socket->listening_port ());
 | |
| 	listening_socket->on_connection ([this] (std::shared_ptr<nano::socket> const & new_connection, boost::system::error_code const & ec_a) {
 | |
| 		bool keep_accepting = true;
 | |
| 		if (ec_a)
 | |
| 		{
 | |
| 			keep_accepting = false;
 | |
| 			this->node.logger.try_log (boost::str (boost::format ("Error while accepting incoming TCP/bootstrap connections: %1%") % ec_a.message ()));
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			accept_action (ec_a, new_connection);
 | |
| 		}
 | |
| 		return keep_accepting;
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_listener::stop ()
 | |
| {
 | |
| 	decltype (connections) connections_l;
 | |
| 	{
 | |
| 		nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 		on = false;
 | |
| 		connections_l.swap (connections);
 | |
| 	}
 | |
| 	if (listening_socket)
 | |
| 	{
 | |
| 		nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 		listening_socket->close ();
 | |
| 		listening_socket = nullptr;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| size_t nano::bootstrap_listener::connection_count ()
 | |
| {
 | |
| 	nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 	return connections.size ();
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_listener::accept_action (boost::system::error_code const & ec, std::shared_ptr<nano::socket> const & socket_a)
 | |
| {
 | |
| 	if (!node.network.excluded_peers.check (socket_a->remote_endpoint ()))
 | |
| 	{
 | |
| 		auto connection (std::make_shared<nano::bootstrap_server> (socket_a, node.shared ()));
 | |
| 		nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 		connections[connection.get ()] = connection;
 | |
| 		connection->receive ();
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_excluded);
 | |
| 		if (node.config.logging.network_rejected_logging ())
 | |
| 		{
 | |
| 			node.logger.try_log ("Rejected connection from excluded peer ", socket_a->remote_endpoint ());
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| boost::asio::ip::tcp::endpoint nano::bootstrap_listener::endpoint ()
 | |
| {
 | |
| 	nano::lock_guard<nano::mutex> lock (mutex);
 | |
| 	if (on && listening_socket)
 | |
| 	{
 | |
| 		return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), listening_socket->listening_port ());
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), 0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| std::unique_ptr<nano::container_info_component> nano::collect_container_info (bootstrap_listener & bootstrap_listener, std::string const & name)
 | |
| {
 | |
| 	auto sizeof_element = sizeof (decltype (bootstrap_listener.connections)::value_type);
 | |
| 	auto composite = std::make_unique<container_info_composite> (name);
 | |
| 	composite->add_component (std::make_unique<container_info_leaf> (container_info{ "connections", bootstrap_listener.connection_count (), sizeof_element }));
 | |
| 	return composite;
 | |
| }
 | |
| 
 | |
| nano::bootstrap_server::bootstrap_server (std::shared_ptr<nano::socket> const & socket_a, std::shared_ptr<nano::node> const & node_a) :
 | |
| 	receive_buffer (std::make_shared<std::vector<uint8_t>> ()),
 | |
| 	socket (socket_a),
 | |
| 	node (node_a)
 | |
| {
 | |
| 	receive_buffer->resize (1024);
 | |
| }
 | |
| 
 | |
| nano::bootstrap_server::~bootstrap_server ()
 | |
| {
 | |
| 	if (node->config.logging.bulk_pull_logging ())
 | |
| 	{
 | |
| 		node->logger.try_log ("Exiting incoming TCP/bootstrap server");
 | |
| 	}
 | |
| 	if (type == nano::bootstrap_server_type::bootstrap)
 | |
| 	{
 | |
| 		--node->bootstrap.bootstrap_count;
 | |
| 	}
 | |
| 	else if (type == nano::bootstrap_server_type::realtime)
 | |
| 	{
 | |
| 		--node->bootstrap.realtime_count;
 | |
| 		// Clear temporary channel
 | |
| 		auto exisiting_response_channel (node->network.tcp_channels.find_channel (remote_endpoint));
 | |
| 		if (exisiting_response_channel != nullptr)
 | |
| 		{
 | |
| 			exisiting_response_channel->temporary = false;
 | |
| 			node->network.tcp_channels.erase (remote_endpoint);
 | |
| 		}
 | |
| 	}
 | |
| 	stop ();
 | |
| 	nano::lock_guard<nano::mutex> lock (node->bootstrap.mutex);
 | |
| 	node->bootstrap.connections.erase (this);
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::stop ()
 | |
| {
 | |
| 	if (!stopped.exchange (true))
 | |
| 	{
 | |
| 		if (socket != nullptr)
 | |
| 		{
 | |
| 			socket->close ();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive ()
 | |
| {
 | |
| 	// Increase timeout to receive TCP header (idle server socket)
 | |
| 	socket->set_timeout (node->network_params.node.idle_timeout);
 | |
| 	auto this_l (shared_from_this ());
 | |
| 	socket->async_read (receive_buffer, 8, [this_l] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 		// Set remote_endpoint
 | |
| 		if (this_l->remote_endpoint.port () == 0)
 | |
| 		{
 | |
| 			this_l->remote_endpoint = this_l->socket->remote_endpoint ();
 | |
| 		}
 | |
| 		// Decrease timeout to default
 | |
| 		this_l->socket->set_timeout (this_l->node->config.tcp_io_timeout);
 | |
| 		// Receive header
 | |
| 		this_l->receive_header_action (ec, size_a);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_header_action (boost::system::error_code const & ec, size_t size_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		debug_assert (size_a == 8);
 | |
| 		nano::bufferstream type_stream (receive_buffer->data (), size_a);
 | |
| 		auto error (false);
 | |
| 		nano::message_header header (error, type_stream);
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			auto this_l (shared_from_this ());
 | |
| 			switch (header.type)
 | |
| 			{
 | |
| 				case nano::message_type::bulk_pull:
 | |
| 				{
 | |
| 					node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull, nano::stat::dir::in);
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_bulk_pull_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::bulk_pull_account:
 | |
| 				{
 | |
| 					node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_account, nano::stat::dir::in);
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_bulk_pull_account_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::frontier_req:
 | |
| 				{
 | |
| 					node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::frontier_req, nano::stat::dir::in);
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_frontier_req_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::bulk_push:
 | |
| 				{
 | |
| 					node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_push, nano::stat::dir::in);
 | |
| 					if (is_bootstrap_connection ())
 | |
| 					{
 | |
| 						add_request (std::make_unique<nano::bulk_push> (header));
 | |
| 					}
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::keepalive:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_keepalive_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::publish:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_publish_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::confirm_ack:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_confirm_ack_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::confirm_req:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_confirm_req_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::node_id_handshake:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_node_id_handshake_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::telemetry_req:
 | |
| 				{
 | |
| 					if (is_realtime_connection ())
 | |
| 					{
 | |
| 						// Only handle telemetry requests if they are outside of the cutoff time
 | |
| 						auto is_very_first_message = last_telemetry_req == std::chrono::steady_clock::time_point{};
 | |
| 						auto cache_exceeded = std::chrono::steady_clock::now () >= last_telemetry_req + nano::telemetry_cache_cutoffs::network_to_time (node->network_params.network);
 | |
| 						if (is_very_first_message || cache_exceeded)
 | |
| 						{
 | |
| 							last_telemetry_req = std::chrono::steady_clock::now ();
 | |
| 							add_request (std::make_unique<nano::telemetry_req> (header));
 | |
| 						}
 | |
| 						else
 | |
| 						{
 | |
| 							node->stats.inc (nano::stat::type::telemetry, nano::stat::detail::request_within_protection_cache_zone);
 | |
| 						}
 | |
| 					}
 | |
| 					receive ();
 | |
| 					break;
 | |
| 				}
 | |
| 				case nano::message_type::telemetry_ack:
 | |
| 				{
 | |
| 					socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 						this_l->receive_telemetry_ack_action (ec, size_a, header);
 | |
| 					});
 | |
| 					break;
 | |
| 				}
 | |
| 				default:
 | |
| 				{
 | |
| 					if (node->config.logging.network_logging ())
 | |
| 					{
 | |
| 						node->logger.try_log (boost::str (boost::format ("Received invalid type from bootstrap connection %1%") % static_cast<uint8_t> (header.type)));
 | |
| 					}
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (node->config.logging.bulk_pull_logging ())
 | |
| 		{
 | |
| 			node->logger.try_log (boost::str (boost::format ("Error while receiving type: %1%") % ec.message ()));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_bulk_pull_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::bulk_pull> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (node->config.logging.bulk_pull_logging ())
 | |
| 			{
 | |
| 				node->logger.try_log (boost::str (boost::format ("Received bulk pull for %1% down to %2%, maximum of %3% from %4%") % request->start.to_string () % request->end.to_string () % (request->count ? request->count : std::numeric_limits<double>::infinity ()) % remote_endpoint));
 | |
| 			}
 | |
| 			if (is_bootstrap_connection () && !node->flags.disable_bootstrap_bulk_pull_server)
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_bulk_pull_account_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		debug_assert (size_a == header_a.payload_length_bytes ());
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::bulk_pull_account> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (node->config.logging.bulk_pull_logging ())
 | |
| 			{
 | |
| 				node->logger.try_log (boost::str (boost::format ("Received bulk pull account for %1% with a minimum amount of %2%") % request->account.to_account () % nano::amount (request->minimum_amount).format_balance (nano::Mxrb_ratio, 10, true)));
 | |
| 			}
 | |
| 			if (is_bootstrap_connection () && !node->flags.disable_bootstrap_bulk_pull_server)
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_frontier_req_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::frontier_req> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (node->config.logging.bulk_pull_logging ())
 | |
| 			{
 | |
| 				node->logger.try_log (boost::str (boost::format ("Received frontier request for %1% with age %2%") % request->start.to_string () % request->age));
 | |
| 			}
 | |
| 			if (is_bootstrap_connection ())
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (node->config.logging.network_logging ())
 | |
| 		{
 | |
| 			node->logger.try_log (boost::str (boost::format ("Error sending receiving frontier request: %1%") % ec.message ()));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_keepalive_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::keepalive> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (is_realtime_connection ())
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (node->config.logging.network_keepalive_logging ())
 | |
| 		{
 | |
| 			node->logger.try_log (boost::str (boost::format ("Error receiving keepalive: %1%") % ec.message ()));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_telemetry_ack_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::telemetry_ack> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (is_realtime_connection ())
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (node->config.logging.network_telemetry_logging ())
 | |
| 		{
 | |
| 			node->logger.try_log (boost::str (boost::format ("Error receiving telemetry ack: %1%") % ec.message ()));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_publish_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		nano::uint128_t digest;
 | |
| 		if (!node->network.publish_filter.apply (receive_buffer->data (), size_a, &digest))
 | |
| 		{
 | |
| 			auto error (false);
 | |
| 			nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 			auto request (std::make_unique<nano::publish> (error, stream, header_a, digest));
 | |
| 			if (!error)
 | |
| 			{
 | |
| 				if (is_realtime_connection ())
 | |
| 				{
 | |
| 					if (!nano::work_validate_entry (*request->block))
 | |
| 					{
 | |
| 						add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work);
 | |
| 					}
 | |
| 				}
 | |
| 				receive ();
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			node->stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish);
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (node->config.logging.network_message_logging ())
 | |
| 		{
 | |
| 			node->logger.try_log (boost::str (boost::format ("Error receiving publish: %1%") % ec.message ()));
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_confirm_req_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::confirm_req> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (is_realtime_connection ())
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else if (node->config.logging.network_message_logging ())
 | |
| 	{
 | |
| 		node->logger.try_log (boost::str (boost::format ("Error receiving confirm_req: %1%") % ec.message ()));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_confirm_ack_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::confirm_ack> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (is_realtime_connection ())
 | |
| 			{
 | |
| 				bool process_vote (true);
 | |
| 				if (header_a.block_type () != nano::block_type::not_a_block)
 | |
| 				{
 | |
| 					for (auto & vote_block : request->vote->blocks)
 | |
| 					{
 | |
| 						if (!vote_block.which ())
 | |
| 						{
 | |
| 							auto const & block (boost::get<std::shared_ptr<nano::block>> (vote_block));
 | |
| 							if (nano::work_validate_entry (*block))
 | |
| 							{
 | |
| 								process_vote = false;
 | |
| 								node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work);
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if (process_vote)
 | |
| 				{
 | |
| 					add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 				}
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else if (node->config.logging.network_message_logging ())
 | |
| 	{
 | |
| 		node->logger.try_log (boost::str (boost::format ("Error receiving confirm_ack: %1%") % ec.message ()));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::receive_node_id_handshake_action (boost::system::error_code const & ec, size_t size_a, nano::message_header const & header_a)
 | |
| {
 | |
| 	if (!ec)
 | |
| 	{
 | |
| 		auto error (false);
 | |
| 		nano::bufferstream stream (receive_buffer->data (), size_a);
 | |
| 		auto request (std::make_unique<nano::node_id_handshake> (error, stream, header_a));
 | |
| 		if (!error)
 | |
| 		{
 | |
| 			if (type == nano::bootstrap_server_type::undefined && !node->flags.disable_tcp_realtime)
 | |
| 			{
 | |
| 				add_request (std::unique_ptr<nano::message> (request.release ()));
 | |
| 			}
 | |
| 			receive ();
 | |
| 		}
 | |
| 	}
 | |
| 	else if (node->config.logging.network_node_id_handshake_logging ())
 | |
| 	{
 | |
| 		node->logger.try_log (boost::str (boost::format ("Error receiving node_id_handshake: %1%") % ec.message ()));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::add_request (std::unique_ptr<nano::message> message_a)
 | |
| {
 | |
| 	debug_assert (message_a != nullptr);
 | |
| 	nano::unique_lock<nano::mutex> lock (mutex);
 | |
| 	auto start (requests.empty ());
 | |
| 	requests.push (std::move (message_a));
 | |
| 	if (start)
 | |
| 	{
 | |
| 		run_next (lock);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::finish_request ()
 | |
| {
 | |
| 	nano::unique_lock<nano::mutex> lock (mutex);
 | |
| 	requests.pop ();
 | |
| 	if (!requests.empty ())
 | |
| 	{
 | |
| 		run_next (lock);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		std::weak_ptr<nano::bootstrap_server> this_w (shared_from_this ());
 | |
| 		node->workers.add_timed_task (std::chrono::steady_clock::now () + (node->config.tcp_io_timeout * 2) + std::chrono::seconds (1), [this_w] () {
 | |
| 			if (auto this_l = this_w.lock ())
 | |
| 			{
 | |
| 				this_l->timeout ();
 | |
| 			}
 | |
| 		});
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::finish_request_async ()
 | |
| {
 | |
| 	std::weak_ptr<nano::bootstrap_server> this_w (shared_from_this ());
 | |
| 	node->background ([this_w] () {
 | |
| 		if (auto this_l = this_w.lock ())
 | |
| 		{
 | |
| 			this_l->finish_request ();
 | |
| 		}
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::timeout ()
 | |
| {
 | |
| 	if (socket != nullptr)
 | |
| 	{
 | |
| 		if (socket->has_timed_out ())
 | |
| 		{
 | |
| 			if (node->config.logging.bulk_pull_logging ())
 | |
| 			{
 | |
| 				node->logger.try_log ("Closing incoming tcp / bootstrap server by timeout");
 | |
| 			}
 | |
| 			{
 | |
| 				nano::lock_guard<nano::mutex> lock (node->bootstrap.mutex);
 | |
| 				node->bootstrap.connections.erase (this);
 | |
| 			}
 | |
| 			socket->close ();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		nano::lock_guard<nano::mutex> lock (node->bootstrap.mutex);
 | |
| 		node->bootstrap.connections.erase (this);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| namespace
 | |
| {
 | |
| class request_response_visitor : public nano::message_visitor
 | |
| {
 | |
| public:
 | |
| 	explicit request_response_visitor (std::shared_ptr<nano::bootstrap_server> const & connection_a) :
 | |
| 		connection (connection_a)
 | |
| 	{
 | |
| 	}
 | |
| 	void keepalive (nano::keepalive const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::keepalive> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void publish (nano::publish const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::publish> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void confirm_req (nano::confirm_req const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::confirm_req> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void confirm_ack (nano::confirm_ack const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::confirm_ack> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void bulk_pull (nano::bulk_pull const &) override
 | |
| 	{
 | |
| 		auto response (std::make_shared<nano::bulk_pull_server> (connection, std::unique_ptr<nano::bulk_pull> (static_cast<nano::bulk_pull *> (connection->requests.front ().release ()))));
 | |
| 		response->send_next ();
 | |
| 	}
 | |
| 	void bulk_pull_account (nano::bulk_pull_account const &) override
 | |
| 	{
 | |
| 		auto response (std::make_shared<nano::bulk_pull_account_server> (connection, std::unique_ptr<nano::bulk_pull_account> (static_cast<nano::bulk_pull_account *> (connection->requests.front ().release ()))));
 | |
| 		response->send_frontier ();
 | |
| 	}
 | |
| 	void bulk_push (nano::bulk_push const &) override
 | |
| 	{
 | |
| 		auto response (std::make_shared<nano::bulk_push_server> (connection));
 | |
| 		response->throttled_receive ();
 | |
| 	}
 | |
| 	void frontier_req (nano::frontier_req const &) override
 | |
| 	{
 | |
| 		auto response (std::make_shared<nano::frontier_req_server> (connection, std::unique_ptr<nano::frontier_req> (static_cast<nano::frontier_req *> (connection->requests.front ().release ()))));
 | |
| 		response->send_next ();
 | |
| 	}
 | |
| 	void telemetry_req (nano::telemetry_req const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::telemetry_req> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void telemetry_ack (nano::telemetry_ack const & message_a) override
 | |
| 	{
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::telemetry_ack> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	void node_id_handshake (nano::node_id_handshake const & message_a) override
 | |
| 	{
 | |
| 		if (connection->node->config.logging.network_node_id_handshake_logging ())
 | |
| 		{
 | |
| 			connection->node->logger.try_log (boost::str (boost::format ("Received node_id_handshake message from %1%") % connection->remote_endpoint));
 | |
| 		}
 | |
| 		if (message_a.query)
 | |
| 		{
 | |
| 			boost::optional<std::pair<nano::account, nano::signature>> response (std::make_pair (connection->node->node_id.pub, nano::sign_message (connection->node->node_id.prv, connection->node->node_id.pub, *message_a.query)));
 | |
| 			debug_assert (!nano::validate_message (response->first, *message_a.query, response->second));
 | |
| 			auto cookie (connection->node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (connection->remote_endpoint)));
 | |
| 			nano::node_id_handshake response_message (cookie, response);
 | |
| 			auto shared_const_buffer = response_message.to_shared_const_buffer ();
 | |
| 			connection->socket->async_write (shared_const_buffer, [connection = std::weak_ptr<nano::bootstrap_server> (connection)] (boost::system::error_code const & ec, size_t size_a) {
 | |
| 				if (auto connection_l = connection.lock ())
 | |
| 				{
 | |
| 					if (ec)
 | |
| 					{
 | |
| 						if (connection_l->node->config.logging.network_node_id_handshake_logging ())
 | |
| 						{
 | |
| 							connection_l->node->logger.try_log (boost::str (boost::format ("Error sending node_id_handshake to %1%: %2%") % connection_l->remote_endpoint % ec.message ()));
 | |
| 						}
 | |
| 						// Stop invalid handshake
 | |
| 						connection_l->stop ();
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						connection_l->node->stats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::out);
 | |
| 						connection_l->finish_request ();
 | |
| 					}
 | |
| 				}
 | |
| 			});
 | |
| 		}
 | |
| 		else if (message_a.response)
 | |
| 		{
 | |
| 			nano::account const & node_id (message_a.response->first);
 | |
| 			if (!connection->node->network.syn_cookies.validate (nano::transport::map_tcp_to_endpoint (connection->remote_endpoint), node_id, message_a.response->second) && node_id != connection->node->node_id.pub)
 | |
| 			{
 | |
| 				connection->remote_node_id = node_id;
 | |
| 				connection->type = nano::bootstrap_server_type::realtime;
 | |
| 				++connection->node->bootstrap.realtime_count;
 | |
| 				connection->finish_request_async ();
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				// Stop invalid handshake
 | |
| 				connection->stop ();
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			connection->finish_request_async ();
 | |
| 		}
 | |
| 		nano::account node_id (connection->remote_node_id);
 | |
| 		nano::bootstrap_server_type type (connection->type);
 | |
| 		debug_assert (node_id.is_zero () || type == nano::bootstrap_server_type::realtime);
 | |
| 		connection->node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::make_shared<nano::node_id_handshake> (message_a), connection->remote_endpoint, connection->remote_node_id, connection->socket, connection->type });
 | |
| 	}
 | |
| 	std::shared_ptr<nano::bootstrap_server> connection;
 | |
| };
 | |
| }
 | |
| 
 | |
| void nano::bootstrap_server::run_next (nano::unique_lock<nano::mutex> & lock_a)
 | |
| {
 | |
| 	debug_assert (!requests.empty ());
 | |
| 	request_response_visitor visitor (shared_from_this ());
 | |
| 	auto type (requests.front ()->header.type);
 | |
| 	if (type == nano::message_type::bulk_pull || type == nano::message_type::bulk_pull_account || type == nano::message_type::bulk_push || type == nano::message_type::frontier_req || type == nano::message_type::node_id_handshake)
 | |
| 	{
 | |
| 		// Bootstrap & node ID (realtime start)
 | |
| 		// Request removed from queue in request_response_visitor. For bootstrap with requests.front ().release (), for node ID with finish_request ()
 | |
| 		requests.front ()->visit (visitor);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		// Realtime
 | |
| 		auto request (std::move (requests.front ()));
 | |
| 		requests.pop ();
 | |
| 		auto timeout_check (requests.empty ());
 | |
| 		lock_a.unlock ();
 | |
| 		request->visit (visitor);
 | |
| 		if (timeout_check)
 | |
| 		{
 | |
| 			std::weak_ptr<nano::bootstrap_server> this_w (shared_from_this ());
 | |
| 			node->workers.add_timed_task (std::chrono::steady_clock::now () + (node->config.tcp_io_timeout * 2) + std::chrono::seconds (1), [this_w] () {
 | |
| 				if (auto this_l = this_w.lock ())
 | |
| 				{
 | |
| 					this_l->timeout ();
 | |
| 				}
 | |
| 			});
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool nano::bootstrap_server::is_bootstrap_connection ()
 | |
| {
 | |
| 	if (type == nano::bootstrap_server_type::undefined && !node->flags.disable_bootstrap_listener && node->bootstrap.bootstrap_count < node->config.bootstrap_connections_max)
 | |
| 	{
 | |
| 		++node->bootstrap.bootstrap_count;
 | |
| 		type = nano::bootstrap_server_type::bootstrap;
 | |
| 	}
 | |
| 	return type == nano::bootstrap_server_type::bootstrap;
 | |
| }
 | |
| 
 | |
| bool nano::bootstrap_server::is_realtime_connection ()
 | |
| {
 | |
| 	return type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server;
 | |
| }
 | 
