diff --git a/include/bitcoin/node/protocols/protocol_header_in_70014.hpp b/include/bitcoin/node/protocols/protocol_header_in_70014.hpp index d917784d..dbaea833 100644 --- a/include/bitcoin/node/protocols/protocol_header_in_70014.hpp +++ b/include/bitcoin/node/protocols/protocol_header_in_70014.hpp @@ -45,6 +45,7 @@ class BCN_API protocol_header_in_70014 void start() NOEXCEPT override; protected: + using send_compact = network::messages::peer::send_compact; using compact_transactions = network::messages::peer::compact_transactions; using compact_block = network::messages::peer::compact_block; using short_id_t = system::chain::short_id::integer; @@ -68,6 +69,8 @@ class BCN_API protocol_header_in_70014 /// Invoked when initial headers sync is complete. void complete() NOEXCEPT override; + virtual bool handle_receive_send_compact(const code& ec, + const send_compact::cptr& message) NOEXCEPT; virtual bool handle_receive_compact_block(const code& ec, const compact_block::cptr& message) NOEXCEPT; virtual bool handle_receive_compact_transactions(const code& ec, @@ -88,6 +91,7 @@ class BCN_API protocol_header_in_70014 bool to_hashes(system::hashes& txids, system::hashes& wtxids, const fill& block) NOEXCEPT; + void upgrade() NOEXCEPT; void request() NOEXCEPT; void identify() NOEXCEPT; bool resolve(const database::header_link& link) NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_header_out_70014.hpp b/include/bitcoin/node/protocols/protocol_header_out_70014.hpp index 42d716ed..1500f255 100644 --- a/include/bitcoin/node/protocols/protocol_header_out_70014.hpp +++ b/include/bitcoin/node/protocols/protocol_header_out_70014.hpp @@ -49,10 +49,6 @@ class BCN_API protocol_header_out_70014 virtual bool handle_receive_send_compact(const code& ec, const network::messages::peer::send_compact::cptr& message) NOEXCEPT; - -private: - // This is protected by strand. - bool high_bandwidth_{}; }; } // namespace node diff --git a/src/protocols/protocol_header_in_70014.cpp b/src/protocols/protocol_header_in_70014.cpp index f45bd338..5a308aea 100644 --- a/src/protocols/protocol_header_in_70014.cpp +++ b/src/protocols/protocol_header_in_70014.cpp @@ -45,6 +45,7 @@ void protocol_header_in_70014::start() NOEXCEPT if (started()) return; + SUBSCRIBE_CHANNEL(send_compact, handle_receive_send_compact, _1, _2); SUBSCRIBE_CHANNEL(compact_block, handle_receive_compact_block, _1, _2); SUBSCRIBE_CHANNEL(compact_transactions, handle_receive_compact_transactions, _1, _2); @@ -52,22 +53,43 @@ void protocol_header_in_70014::start() NOEXCEPT protocol_header_in_70012::start(); } -// Compact block announcements are requested once current (high bandwidth). void protocol_header_in_70014::complete() NOEXCEPT +{ + BC_ASSERT(stranded()); + upgrade(); + protocol_header_in_70012::complete(); +} + +// Inbound (sendcmpct). +// ---------------------------------------------------------------------------- + +bool protocol_header_in_70014::handle_receive_send_compact(const code& ec, + const send_compact::cptr&) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (stopped(ec)) + return false; + + upgrade(); + return true; +} + +// High bandwidth is requested of a peer that signaled compact blocks, once the +// chain is current, whichever comes last. +void protocol_header_in_70014::upgrade() NOEXCEPT { BC_ASSERT(stranded()); // TODO: this is hardwired in handshake. constexpr auto version = send_compact::compact_version_2; - if (!compact_ && is_current_chain(true)) - { - compact_ = true; - SEND((send_compact{ true, version }), handle_send, _1); - LOGP("Subscribed to compact blocks at [" << opposite() << "]."); - } + if (compact_ || !accepts_compact_blocks() || !is_current_chain(true)) + return; - protocol_header_in_70012::complete(); + compact_ = true; + SEND((send_compact{ true, version }), handle_send, _1); + LOGP("Subscribed to compact blocks at [" << opposite() << "]."); } // Inbound (cmpctblock). diff --git a/src/protocols/protocol_header_out_70014.cpp b/src/protocols/protocol_header_out_70014.cpp index a8c2a177..fc1cfc00 100644 --- a/src/protocols/protocol_header_out_70014.cpp +++ b/src/protocols/protocol_header_out_70014.cpp @@ -57,7 +57,7 @@ bool protocol_header_out_70014::do_announce(header_t link) NOEXCEPT if (stopped()) return false; - if (high_bandwidth_) + if (wants_compact_blocks()) { // Don't announce to peer that announced to us. const auto hash = archive().get_header_key(link); @@ -88,15 +88,10 @@ bool protocol_header_out_70014::handle_receive_send_compact(const code& ec, if (stopped(ec)) return false; - // Only version 2 is supported, the peer may change bandwidth mode. - if (message->compact_version == send_compact::compact_version_2) - { - high_bandwidth_ = message->high_bandwidth; - if (high_bandwidth_) - announce(); - } - - // TODO: drop peer on non-negotiated version. + // The signal is recorded on the channel by the version protocol. + if ((message->compact_version == send_compact::compact_version_2) && + message->high_bandwidth) + announce(); return true; } diff --git a/test/functional/p2p_compact.cpp b/test/functional/p2p_compact.cpp index 369f4475..5bb8edeb 100644 --- a/test/functional/p2p_compact.cpp +++ b/test/functional/p2p_compact.cpp @@ -54,10 +54,11 @@ BOOST_AUTO_TEST_CASE(functional_p2p_compact__handshake__verack__send_compact_low BOOST_REQUIRE_EQUAL(message->compact_version, send_compact::compact_version_2); } -// Headers completion when current requests compact block announcement. +// Headers completion when current requests announcement of a signaled peer. BOOST_AUTO_TEST_CASE(functional_p2p_compact__headers__complete__send_compact_high_bandwidth) { BOOST_REQUIRE(handshake(full_node)); + send(send_compact{ false, send_compact::compact_version_2 }, node_version->value); receive(get_headers::command); send(headers{}, node_version->value); @@ -68,6 +69,35 @@ BOOST_AUTO_TEST_CASE(functional_p2p_compact__headers__complete__send_compact_hig BOOST_REQUIRE_EQUAL(message->compact_version, send_compact::compact_version_2); } +// A peer that has not signaled compact blocks is not asked to announce. +BOOST_AUTO_TEST_CASE(functional_p2p_compact__headers__complete_unsignaled__no_high_bandwidth) +{ + BOOST_REQUIRE(handshake(full_node)); + receive(send_compact::command); + + receive(get_headers::command); + send(headers{}, node_version->value); + send(ping{ 42 }, node_version->value); + + BOOST_REQUIRE(!received(send_compact::command, pong::command)); +} + +// A peer signaling after headers completion is then asked to announce. +BOOST_AUTO_TEST_CASE(functional_p2p_compact__send_compact__after_complete__send_compact_high_bandwidth) +{ + BOOST_REQUIRE(handshake(full_node)); + receive(send_compact::command); + + receive(get_headers::command); + send(headers{}, node_version->value); + send(send_compact{ false, send_compact::compact_version_2 }, node_version->value); + + const auto message = send_compact::deserialize(node_version->value, receive(send_compact::command)); + BOOST_REQUIRE(message); + BOOST_REQUIRE(message->high_bandwidth); + BOOST_REQUIRE_EQUAL(message->compact_version, send_compact::compact_version_2); +} + // cmpctblock (out) // ----------------------------------------------------------------------------