diff --git a/Makefile.am b/Makefile.am index 4c4228838..c01022a25 100644 --- a/Makefile.am +++ b/Makefile.am @@ -109,6 +109,7 @@ src_libbitcoin_network_la_SOURCES = \ src/protocols/protocol_version_31402.cpp \ src/protocols/protocol_version_70001.cpp \ src/protocols/protocol_version_70002.cpp \ + src/protocols/protocol_version_70016.cpp \ src/sessions/session.cpp \ src/sessions/session_inbound.cpp \ src/sessions/session_manual.cpp \ @@ -209,6 +210,7 @@ test_libbitcoin_network_test_SOURCES = \ test/protocols/protocol_version_31402.cpp \ test/protocols/protocol_version_70001.cpp \ test/protocols/protocol_version_70002.cpp \ + test/protocols/protocol_version_70016.cpp \ test/sessions/session.cpp \ test/sessions/session_inbound.cpp \ test/sessions/session_manual.cpp \ @@ -365,6 +367,7 @@ include_bitcoin_network_protocols_HEADERS = \ include/bitcoin/network/protocols/protocol_version_31402.hpp \ include/bitcoin/network/protocols/protocol_version_70001.hpp \ include/bitcoin/network/protocols/protocol_version_70002.hpp \ + include/bitcoin/network/protocols/protocol_version_70016.hpp \ include/bitcoin/network/protocols/protocols.hpp include_bitcoin_network_sessionsdir = ${includedir}/bitcoin/network/sessions diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index b246788a2..58732e133 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -291,6 +291,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/protocols/protocol_version_31402.cpp" "../../src/protocols/protocol_version_70001.cpp" "../../src/protocols/protocol_version_70002.cpp" + "../../src/protocols/protocol_version_70016.cpp" "../../src/sessions/session.cpp" "../../src/sessions/session_inbound.cpp" "../../src/sessions/session_manual.cpp" @@ -415,6 +416,7 @@ if (with-tests) "../../test/protocols/protocol_version_31402.cpp" "../../test/protocols/protocol_version_70001.cpp" "../../test/protocols/protocol_version_70002.cpp" + "../../test/protocols/protocol_version_70016.cpp" "../../test/sessions/session.cpp" "../../test/sessions/session_inbound.cpp" "../../test/sessions/session_manual.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj b/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj index 0c795067c..98e9bd7a9 100644 --- a/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj @@ -203,6 +203,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj.filters index c4ae92dbe..31b82f2db 100644 --- a/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-network-test/libbitcoin-network-test.vcxproj.filters @@ -279,6 +279,9 @@ src\protocols + + src\protocols + src\sessions diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj index 24890a483..5b764ef51 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj @@ -198,6 +198,7 @@ + @@ -306,6 +307,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters index 7be69461f..628ec805f 100644 --- a/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-network/libbitcoin-network.vcxproj.filters @@ -300,6 +300,9 @@ src\protocols + + src\protocols + src\sessions @@ -620,6 +623,9 @@ include\bitcoin\network\protocols + + include\bitcoin\network\protocols + include\bitcoin\network\protocols diff --git a/include/bitcoin/network.hpp b/include/bitcoin/network.hpp index cf9b11382..16ddeb11d 100644 --- a/include/bitcoin/network.hpp +++ b/include/bitcoin/network.hpp @@ -116,6 +116,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bitcoin/network/protocols/protocol_version_70016.hpp b/include/bitcoin/network/protocols/protocol_version_70016.hpp new file mode 100644 index 000000000..16d99a9d1 --- /dev/null +++ b/include/bitcoin/network/protocols/protocol_version_70016.hpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef LIBBITCOIN_NETWORK_PROTOCOL_VERSION_70016_HPP +#define LIBBITCOIN_NETWORK_PROTOCOL_VERSION_70016_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace network { + +class BCT_API protocol_version_70016 + : public protocol_version_70002, protected tracker +{ +public: + typedef std::shared_ptr ptr; + + /// Construct a version protocol instance using configured values. + protocol_version_70016(const session::ptr& session, + const channel::ptr& channel) NOEXCEPT; + + /// Construct a version protocol instance using parameterized services. + protocol_version_70016(const session::ptr& session, + const channel::ptr& channel, uint64_t minimum_services, + uint64_t maximum_services, bool relay, bool reject) NOEXCEPT; + + /// Perform the handshake (strand required), handler invoked on completion. + void shake(result_handler&& handle_event) NOEXCEPT override; + +protected: + void handle_send_version(const code& ec) NOEXCEPT override; + bool handle_receive_acknowledge(const code& ec, + const messages::version_acknowledge::cptr& message) NOEXCEPT override; + virtual bool handle_receive_send_address_v2(const code& ec, + const messages::send_address_v2::cptr& message) NOEXCEPT; + +private: + // This is thread safe. + const bool reject_; + + // This is protected by strand. + bool complete_{}; +}; + +} // namespace network +} // namespace libbitcoin + +#endif diff --git a/include/bitcoin/network/protocols/protocols.hpp b/include/bitcoin/network/protocols/protocols.hpp index cbeff1f9e..94e0c8515 100644 --- a/include/bitcoin/network/protocols/protocols.hpp +++ b/include/bitcoin/network/protocols/protocols.hpp @@ -30,5 +30,6 @@ #include #include #include +#include #endif diff --git a/src/protocols/protocol_version_31402.cpp b/src/protocols/protocol_version_31402.cpp index 026daa5e9..d5d41766e 100644 --- a/src/protocols/protocol_version_31402.cpp +++ b/src/protocols/protocol_version_31402.cpp @@ -54,7 +54,8 @@ protocol_version_31402::protocol_version_31402(const session::ptr& session, // Used for seeding (should probably not override these). protocol_version_31402::protocol_version_31402(const session::ptr& session, - const channel::ptr& channel, uint64_t minimum_services, + const channel::ptr& channel, + uint64_t minimum_services, uint64_t maximum_services) NOEXCEPT : protocol(session, channel), inbound_(channel->inbound()), @@ -335,7 +336,7 @@ bool protocol_version_31402::handle_receive_version(const code& ec, if (is_disallowed_deviation(message->timestamp)) { - LOGP("Timestamp out of range (" << message->value << ") " + LOGR("Timestamp out of range (" << message->value << ") " "for [" << authority() << "]."); rejection(error::peer_timestamp); diff --git a/src/protocols/protocol_version_70001.cpp b/src/protocols/protocol_version_70001.cpp index 535335162..eacee49d1 100644 --- a/src/protocols/protocol_version_70001.cpp +++ b/src/protocols/protocol_version_70001.cpp @@ -45,10 +45,11 @@ protocol_version_70001::protocol_version_70001(const session::ptr& session, } protocol_version_70001::protocol_version_70001(const session::ptr& session, - const channel::ptr& channel, uint64_t minimum_services, - uint64_t maximum_services, bool relay) NOEXCEPT - : protocol_version_31402(session, channel, minimum_services, - maximum_services), + const channel::ptr& channel, + uint64_t minimum_services, + uint64_t maximum_services, + bool relay) NOEXCEPT + : protocol_version_31402(session, channel, minimum_services, maximum_services), relay_(relay), tracker(session->log) { diff --git a/src/protocols/protocol_version_70002.cpp b/src/protocols/protocol_version_70002.cpp index f652e03f0..e08b62bd1 100644 --- a/src/protocols/protocol_version_70002.cpp +++ b/src/protocols/protocol_version_70002.cpp @@ -27,11 +27,6 @@ #include #include -// TODO: incorporate "sendaddrv2" into a new protocol_version_70016. -// TODO: sendaddrv2 is a a broken protocol in that it is a formally unversioned -// TODO: message, though Satoshi doesn't send until receiving version >= 70016. -// TODO: must be sent/received before verack (and sent after version receipt). - namespace libbitcoin { namespace network { @@ -51,10 +46,11 @@ protocol_version_70002::protocol_version_70002(const session::ptr& session, } protocol_version_70002::protocol_version_70002(const session::ptr& session, - const channel::ptr& channel, uint64_t minimum_services, - uint64_t maximum_services, bool relay) NOEXCEPT - : protocol_version_70001(session, channel, minimum_services, - maximum_services, relay), + const channel::ptr& channel, + uint64_t minimum_services, + uint64_t maximum_services, + bool relay) NOEXCEPT + : protocol_version_70001(session, channel, minimum_services, maximum_services, relay), tracker(session->log) { } @@ -76,6 +72,7 @@ void protocol_version_70002::shake(result_handler&& handle_event) NOEXCEPT // Outgoing [(in)sufficient_peer => send_reject]. // ---------------------------------------------------------------------------- +// Reject is the only difference at protocol level 70002. void protocol_version_70002::rejection(const code& ec) NOEXCEPT { @@ -104,7 +101,7 @@ void protocol_version_70002::rejection(const code& ec) NOEXCEPT "timestamp" }), handle_send, _1); } - return protocol_version_70001::rejection(ec); + protocol_version_70001::rejection(ec); } // Incoming [receive_reject => log]. diff --git a/src/protocols/protocol_version_70016.cpp b/src/protocols/protocol_version_70016.cpp new file mode 100644 index 000000000..e347b07a2 --- /dev/null +++ b/src/protocols/protocol_version_70016.cpp @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// sendaddrv2 is a a broken protocol in that it is a formally unversioned +// message, though Satoshi doesn't send until receiving version >= 70016. +// It must be sent/received before verack (and sent after version receipt). + +namespace libbitcoin { +namespace network { + +#define CLASS protocol_version_70016 + +using namespace system; +using namespace bc::network::messages; +using namespace std::placeholders; + +protocol_version_70016::protocol_version_70016(const session::ptr& session, + const channel::ptr& channel) NOEXCEPT + : protocol_version_70016(session, channel, + session->settings().services_minimum, + session->settings().services_maximum, + session->settings().enable_transaction, + session->settings().enable_reject) +{ +} + +protocol_version_70016::protocol_version_70016(const session::ptr& session, + const channel::ptr& channel, + uint64_t minimum_services, + uint64_t maximum_services, + bool relay, + bool reject) NOEXCEPT + : protocol_version_70002(session, channel, minimum_services, maximum_services, relay), + reject_(reject), + tracker(session->log) +{ +} + +// Start. +// ---------------------------------------------------------------------------- + +void protocol_version_70016::shake(result_handler&& handle_event) NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "protocol_version_70016"); + + if (started()) + return; + + // Protocol versions are cumulative, but reject is deprecated. + if (reject_) + { + protocol_version_70002::shake(std::move(handle_event)); + return; + } + + protocol_version_70001::shake(std::move(handle_event)); +} + + +// Incoming [send_address_v2 => negotiated state change]. +// ---------------------------------------------------------------------------- +// send_address_v2 receipt is the only difference at protocol level 70016. + +void protocol_version_70016::handle_send_version(const code& ec) NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "protocol_version_70016"); + + SUBSCRIBE_CHANNEL(send_address_v2, handle_receive_send_address_v2, _1, _2); + protocol_version_70002::handle_send_version(ec); +} + +bool protocol_version_70016::handle_receive_acknowledge(const code& ec, + const messages::version_acknowledge::cptr& message) NOEXCEPT +{ + complete_ = true; + + // Channel will pause after this and then be restarted as connected. + return protocol_version_70002::handle_receive_acknowledge(ec, message); +} + +bool protocol_version_70016::handle_receive_send_address_v2(const code& ec, + const send_address_v2::cptr&) NOEXCEPT +{ + BC_ASSERT_MSG(stranded(), "protocol_version_70016"); + + if (stopped(ec)) + return false; + + // Late or already received send_address_v2. + if (complete_) + { + rejection(error::protocol_violation); + return false; + } + + // TODO: set channel addressv2 property and use to attach protocols. + // TODO: implement updated protocol_address_in|out_70016. + complete_ = true; + return true; +} + +} // namespace network +} // namespace libbitcoin diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index 87a990778..d550c845e 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -149,19 +149,24 @@ void session::attach_handshake(const channel::ptr& channel, BC_ASSERT_MSG(channel->paused(), "channel not paused for handshake attach"); const auto self = shared_from_this(); - const auto maximum_version = settings().protocol_maximum; - const auto extended_version = maximum_version >= messages::level::bip37; - const auto enable_reject = settings().enable_reject && - maximum_version >= messages::level::bip61; + const auto maximum = settings().protocol_maximum; + const auto bip37 = maximum >= messages::level::bip37; + const auto bip61 = maximum >= messages::level::bip61; + const auto bip155 = maximum >= messages::level::bip155; // Protocol must pause the channel after receiving version and verack. - // Reject is deprecated. - if (enable_reject) + // Address (in/out) can be disabled, independent of version. + if (bip155 && settings().enable_address) + channel->attach(self) + ->shake(std::move(handler)); + + // Protocol versions are cumulative, but reject is deprecated. + else if (bip61 && settings().enable_reject) channel->attach(self) ->shake(std::move(handler)); - else if (extended_version) + else if (bip37) channel->attach(self) ->shake(std::move(handler)); @@ -280,30 +285,38 @@ void session::attach_protocols(const channel::ptr& channel) NOEXCEPT BC_ASSERT_MSG(channel->paused(), "channel not paused for protocol attach"); const auto self = shared_from_this(); - const auto enable_alert = settings().enable_alert; - const auto enable_address = settings().enable_address; - const auto negotiated_version = channel->negotiated_version(); - const auto enable_pong = negotiated_version >= messages::level::bip31; - const auto enable_reject = settings().enable_reject && - negotiated_version >= messages::level::bip61; - - if (enable_pong) - channel->attach(self)->start(); - else - channel->attach(self)->start(); + const auto negotiated = channel->negotiated_version(); + const auto bip31 = negotiated >= messages::level::bip31; + const auto bip61 = negotiated >= messages::level::bip61; + const auto bip155 = negotiated >= messages::level::bip155; - // Alert is deprecated. - if (enable_alert) + // Alert is deprecated, independent of version. + if (settings().enable_alert) channel->attach(self)->start(); - // Reject is deprecated. - if (enable_reject) + // Reject is deprecated, independent of version. + if (bip61 && settings().enable_reject) channel->attach(self)->start(); - if (enable_address) + if (bip31) + channel->attach(self)->start(); + else + channel->attach(self)->start(); + + // Address (in/out) can be disabled, independent of version. + if (settings().enable_address) { - channel->attach(self)->start(); - channel->attach(self)->start(); + ////// Receive based on config and negotiated version only (sent). + ////if (bip155) + //// channel->attach(self)->start(); + ////else + channel->attach(self)->start(); + + ////// Peer requested (stoopid because version support is negotiated). + ////if (bip155 && channel->send_address_v2()) + //// channel->attach(self)->start(); + ////else + channel->attach(self)->start(); } } diff --git a/test/protocols/protocol_version_70016.cpp b/test/protocols/protocol_version_70016.cpp new file mode 100644 index 000000000..e1e43ac78 --- /dev/null +++ b/test/protocols/protocol_version_70016.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +BOOST_AUTO_TEST_SUITE(protocol_tests) + +BOOST_AUTO_TEST_CASE(protocol_version_70016_tests) +{ + BOOST_REQUIRE(true); +} + +BOOST_AUTO_TEST_SUITE_END()