diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am
index 8daa8051..753dc613 100644
--- a/builds/gnu/Makefile.am
+++ b/builds/gnu/Makefile.am
@@ -77,6 +77,7 @@ src_libbitcoin_node_la_SOURCES = \
${srcdir}/../../src/protocols/protocol_block_in_106.cpp \
${srcdir}/../../src/protocols/protocol_block_in_31800.cpp \
${srcdir}/../../src/protocols/protocol_block_out_106.cpp \
+ ${srcdir}/../../src/protocols/protocol_block_out_70001.cpp \
${srcdir}/../../src/protocols/protocol_block_out_70012.cpp \
${srcdir}/../../src/protocols/protocol_filter_out_70015.cpp \
${srcdir}/../../src/protocols/protocol_header_in_31800.cpp \
@@ -88,6 +89,7 @@ src_libbitcoin_node_la_SOURCES = \
${srcdir}/../../src/protocols/protocol_performer.cpp \
${srcdir}/../../src/protocols/protocol_transaction_in_106.cpp \
${srcdir}/../../src/protocols/protocol_transaction_out_106.cpp \
+ ${srcdir}/../../src/protocols/protocol_transaction_out_70001.cpp \
${srcdir}/../../src/sessions/session.cpp \
${srcdir}/../../src/sessions/session_inbound.cpp \
${srcdir}/../../src/sessions/session_manual.cpp \
@@ -162,6 +164,7 @@ include_bitcoin_node_protocols_HEADERS = \
${srcdir}/../../include/bitcoin/node/protocols/protocol_block_in_106.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_block_in_31800.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_block_out_106.hpp \
+ ${srcdir}/../../include/bitcoin/node/protocols/protocol_block_out_70001.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_block_out_70012.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_filter_out_70015.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_header_in_31800.hpp \
@@ -173,6 +176,7 @@ include_bitcoin_node_protocols_HEADERS = \
${srcdir}/../../include/bitcoin/node/protocols/protocol_performer.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_transaction_in_106.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocol_transaction_out_106.hpp \
+ ${srcdir}/../../include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp \
${srcdir}/../../include/bitcoin/node/protocols/protocols.hpp
include_bitcoin_node_sessionsdir = \
diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
index c355ac35..750d08b1 100644
--- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj
@@ -146,6 +146,7 @@
+
@@ -157,6 +158,7 @@
+
@@ -196,6 +198,7 @@
+
@@ -207,6 +210,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
index a6dbdb30..10876c36 100644
--- a/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-node/libbitcoin-node.vcxproj.filters
@@ -132,6 +132,9 @@
src\protocols
+
+ src\protocols
+
src\protocols
@@ -165,6 +168,9 @@
src\protocols
+
+ src\protocols
+
src\sessions
@@ -278,6 +284,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
@@ -311,6 +320,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
diff --git a/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj b/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj
index 2d05d8d6..c43c94a9 100644
--- a/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj
@@ -146,6 +146,7 @@
+
@@ -157,6 +158,7 @@
+
@@ -196,6 +198,7 @@
+
@@ -207,6 +210,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj.filters
index a6dbdb30..10876c36 100644
--- a/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-node/libbitcoin-node.vcxproj.filters
@@ -132,6 +132,9 @@
src\protocols
+
+ src\protocols
+
src\protocols
@@ -165,6 +168,9 @@
src\protocols
+
+ src\protocols
+
src\sessions
@@ -278,6 +284,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
@@ -311,6 +320,9 @@
include\bitcoin\node\protocols
+
+ include\bitcoin\node\protocols
+
include\bitcoin\node\protocols
diff --git a/include/bitcoin/node/impl/sessions/session_peer.ipp b/include/bitcoin/node/impl/sessions/session_peer.ipp
index fde9fa10..c45216ee 100644
--- a/include/bitcoin/node/impl/sessions/session_peer.ipp
+++ b/include/bitcoin/node/impl/sessions/session_peer.ipp
@@ -140,22 +140,25 @@ inline void CLASS::attach_protocols(const channel_ptr& channel) NOEXCEPT
channel->attach(self)->start();
channel->attach(self)->start();
}
- else if (headers && peer->is_negotiated(level::headers_protocol))
- {
- channel->attach(self)->start();
- channel->attach(self)->start();
- }
else
{
- channel->attach(self)->start();
+ if (headers && peer->is_negotiated(level::headers_protocol))
+ channel->attach(self)->start();
+
+ // not_found is defined at bip37.
+ if (peer->is_negotiated(level::bip37))
+ channel->attach(self)->start();
+ else
+ channel->attach(self)->start();
}
}
// Relay is configured, active, and txs are ready (txs in/out).
if (txs_in_out)
{
+ // Attached above bip37, where not_found is defined.
if (peer->peer_version()->relay)
- channel->attach(self)->start();
+ channel->attach(self)->start();
}
}
diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp
index 7c254ce7..df64b055 100644
--- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp
+++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp
@@ -54,6 +54,7 @@ class BCN_API protocol_block_out_106
protected:
using get_data = network::messages::peer::get_data;
using get_blocks = network::messages::peer::get_blocks;
+ using inventory_item = network::messages::peer::inventory_item;
/// Block announcements are superseded by send_headers.
virtual bool superseded() const NOEXCEPT;
@@ -71,9 +72,11 @@ class BCN_API protocol_block_out_106
const get_data::cptr& message) NOEXCEPT;
virtual void send_block(const code& ec) NOEXCEPT;
+ /// The requested item cannot be served, stops the channel.
+ virtual void handle_unservable(const inventory_item& item) NOEXCEPT;
+
private:
using inventory = network::messages::peer::inventory;
- using inventory_item = network::messages::peer::inventory_item;
using inventory_items = network::messages::peer::inventory_items;
bool is_under_checkpoint(const database::header_link& link) NOEXCEPT;
diff --git a/include/bitcoin/node/protocols/protocol_block_out_70001.hpp b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp
new file mode 100644
index 00000000..b764bca6
--- /dev/null
+++ b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_70001_HPP
+#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_70001_HPP
+
+#include
+#include
+
+namespace libbitcoin {
+namespace node {
+
+class BCN_API protocol_block_out_70001
+ : public protocol_block_out_106,
+ protected network::tracker
+{
+public:
+ typedef std::shared_ptr ptr;
+
+ protocol_block_out_70001(const auto& session,
+ const network::channel::ptr& channel) NOEXCEPT
+ : protocol_block_out_106(session, channel),
+ enable_not_found_(session->network_settings().enable_not_found),
+ network::tracker(session->log)
+ {
+ }
+
+protected:
+ /// The requested item cannot be served, replies not_found.
+ void handle_unservable(const inventory_item& item) NOEXCEPT override;
+
+private:
+ // This is thread safe.
+ const bool enable_not_found_;
+};
+
+} // namespace node
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/node/protocols/protocol_block_out_70012.hpp b/include/bitcoin/node/protocols/protocol_block_out_70012.hpp
index 4cbe70e3..41265988 100644
--- a/include/bitcoin/node/protocols/protocol_block_out_70012.hpp
+++ b/include/bitcoin/node/protocols/protocol_block_out_70012.hpp
@@ -20,13 +20,13 @@
#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_70012_HPP
#include
-#include
+#include
namespace libbitcoin {
namespace node {
class BCN_API protocol_block_out_70012
- : public protocol_block_out_106,
+ : public protocol_block_out_70001,
protected network::tracker
{
public:
@@ -34,7 +34,7 @@ class BCN_API protocol_block_out_70012
protocol_block_out_70012(const auto& session,
const network::channel::ptr& channel) NOEXCEPT
- : protocol_block_out_106(session, channel),
+ : protocol_block_out_70001(session, channel),
network::tracker(session->log)
{
}
diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp
index 23aa4c44..05dcf659 100644
--- a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp
+++ b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp
@@ -63,6 +63,11 @@ class BCN_API protocol_transaction_out_106
virtual void send_transaction(const code& ec, size_t index,
const network::messages::peer::get_data::cptr& message) NOEXCEPT;
+ /// The requested item cannot be served, stops the channel.
+ virtual void handle_unservable(
+ const network::messages::peer::inventory_item& item, size_t index,
+ const network::messages::peer::get_data::cptr& message) NOEXCEPT;
+
virtual bool announce(const system::hash_digest& hash) NOEXCEPT;
private:
diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp
new file mode 100644
index 00000000..d37d2960
--- /dev/null
+++ b/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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_NODE_PROTOCOLS_PROTOCOL_TRANSACTION_OUT_70001_HPP
+#define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_TRANSACTION_OUT_70001_HPP
+
+#include
+#include
+
+namespace libbitcoin {
+namespace node {
+
+class BCN_API protocol_transaction_out_70001
+ : public protocol_transaction_out_106,
+ protected network::tracker
+{
+public:
+ typedef std::shared_ptr ptr;
+
+ protocol_transaction_out_70001(const auto& session,
+ const network::channel::ptr& channel) NOEXCEPT
+ : protocol_transaction_out_106(session, channel),
+ enable_not_found_(session->network_settings().enable_not_found),
+ network::tracker(session->log)
+ {
+ }
+
+protected:
+ /// The requested item cannot be served, replies not_found.
+ void handle_unservable(
+ const network::messages::peer::inventory_item& item, size_t index,
+ const network::messages::peer::get_data::cptr& message) NOEXCEPT
+ override;
+
+private:
+ // This is thread safe.
+ const bool enable_not_found_;
+};
+
+} // namespace node
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/node/protocols/protocols.hpp b/include/bitcoin/node/protocols/protocols.hpp
index 21ded064..9ff2dc25 100644
--- a/include/bitcoin/node/protocols/protocols.hpp
+++ b/include/bitcoin/node/protocols/protocols.hpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -34,5 +35,6 @@
#include
#include
#include
+#include
#endif
diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp
index 51495482..07e0d01b 100644
--- a/src/protocols/protocol_block_out_106.cpp
+++ b/src/protocols/protocol_block_out_106.cpp
@@ -205,7 +205,9 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
return;
if (backlog_.empty()) return;
- const auto& item = backlog_.front();
+
+ // Copied because the item is answered after the backlog is popped.
+ const auto item = backlog_.front();
const auto witness = item.is_witness_type();
if (witness && !node_witness_)
{
@@ -216,11 +218,26 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
const auto& query = archive();
const auto link = query.to_header(item.hash);
+
+ // A hash that resolves to no header is ordinary peer input, and the
+ // checkpoint height query faults the store on a terminal link.
+ if (link.is_terminal())
+ {
+ LOGR("Requested block " << encode_hash(item.hash) << " from ["
+ << opposite() << "] not stored.");
+
+ backlog_.pop_front();
+ handle_unservable(item);
+ return;
+ }
+
if (node_pruned_ && (is_under_checkpoint(link) || query.is_milestone(link)))
{
LOGR("Requested pruned block " << encode_hash(item.hash)
<< " from [" << opposite() << "].");
- stop(system::error::not_found);
+
+ backlog_.pop_front();
+ handle_unservable(item);
return;
}
@@ -235,8 +252,8 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
<< opposite() << "] not found.");
// This block could not have been advertised to the peer.
- // TODO: send not_found message in protocol override.
- stop(system::error::not_found);
+ backlog_.pop_front();
+ handle_unservable(item);
return;
}
@@ -245,6 +262,13 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
SEND(std::move(out), send_block, _1);
}
+// not_found is undefined below bip37, so the channel is stopped instead.
+void protocol_block_out_106::handle_unservable(const inventory_item&) NOEXCEPT
+{
+ BC_ASSERT(stranded());
+ stop(system::error::not_found);
+}
+
// utilities
// ----------------------------------------------------------------------------
diff --git a/src/protocols/protocol_block_out_70001.cpp b/src/protocols/protocol_block_out_70001.cpp
new file mode 100644
index 00000000..69710684
--- /dev/null
+++ b/src/protocols/protocol_block_out_70001.cpp
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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
+
+namespace libbitcoin {
+namespace node {
+
+#define CLASS protocol_block_out_70001
+
+using namespace system;
+using namespace network;
+using namespace network::messages::peer;
+using namespace std::placeholders;
+
+BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
+BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
+
+// Outbound (not_found).
+// ----------------------------------------------------------------------------
+
+// The item is answered and the send loop resumed, as with a block, so nothing
+// is produced until the prior write completes.
+void protocol_block_out_70001::handle_unservable(
+ const inventory_item& item) NOEXCEPT
+{
+ BC_ASSERT(stranded());
+
+ if (!enable_not_found_)
+ {
+ protocol_block_out_106::handle_unservable(item);
+ return;
+ }
+
+ SEND(not_found{ { item } }, send_block, _1);
+}
+
+BC_POP_WARNING()
+BC_POP_WARNING()
+
+} // namespace node
+} // namespace libbitcoin
diff --git a/src/protocols/protocol_block_out_70012.cpp b/src/protocols/protocol_block_out_70012.cpp
index ccd750cb..f341d534 100644
--- a/src/protocols/protocol_block_out_70012.cpp
+++ b/src/protocols/protocol_block_out_70012.cpp
@@ -44,7 +44,7 @@ void protocol_block_out_70012::start() NOEXCEPT
return;
SUBSCRIBE_CHANNEL(send_headers, handle_receive_send_headers, _1, _2);
- protocol_block_out_106::start();
+ protocol_block_out_70001::start();
}
// Inbound (send_headers).
diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp
index 9f2423f3..3d7c5f41 100644
--- a/src/protocols/protocol_transaction_out_106.cpp
+++ b/src/protocols/protocol_transaction_out_106.cpp
@@ -203,13 +203,21 @@ void protocol_transaction_out_106::send_transaction(const code& ec,
<< " from [" << opposite() << "] not found.");
// This tx could not have been advertised to the peer.
- stop(system::error::not_found);
+ handle_unservable(item, index, message);
return;
}
SEND(transaction{ ptr }, send_transaction, _1, add1(index), message);
}
+// not_found is undefined below bip37, so the channel is stopped instead.
+void protocol_transaction_out_106::handle_unservable(const inventory_item&,
+ size_t, const get_data::cptr&) NOEXCEPT
+{
+ BC_ASSERT(stranded());
+ stop(system::error::not_found);
+}
+
BC_POP_WARNING()
BC_POP_WARNING()
diff --git a/src/protocols/protocol_transaction_out_70001.cpp b/src/protocols/protocol_transaction_out_70001.cpp
new file mode 100644
index 00000000..76008da8
--- /dev/null
+++ b/src/protocols/protocol_transaction_out_70001.cpp
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * 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
+
+namespace libbitcoin {
+namespace node {
+
+#define CLASS protocol_transaction_out_70001
+
+using namespace system;
+using namespace network::messages::peer;
+using namespace std::placeholders;
+
+// Shared pointers required for lifetime in handler parameters.
+BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
+BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
+
+// Outbound (not_found).
+// ----------------------------------------------------------------------------
+
+// The item is answered and the send loop resumed, as with a transaction, so
+// nothing is produced until the prior write completes.
+void protocol_transaction_out_70001::handle_unservable(
+ const inventory_item& item, size_t index,
+ const get_data::cptr& message) NOEXCEPT
+{
+ BC_ASSERT(stranded());
+
+ if (!enable_not_found_)
+ {
+ protocol_transaction_out_106::handle_unservable(item, index, message);
+ return;
+ }
+
+ SEND(not_found{ { item } }, send_transaction, _1, add1(index), message);
+}
+
+BC_POP_WARNING()
+BC_POP_WARNING()
+
+} // namespace node
+} // namespace libbitcoin
diff --git a/test/functional/p2p.cpp b/test/functional/p2p.cpp
index 61fffefa..813dbbf3 100644
--- a/test/functional/p2p.cpp
+++ b/test/functional/p2p.cpp
@@ -58,4 +58,89 @@ BOOST_AUTO_TEST_CASE(functional_p2p__get_data__genesis_block__expected_bytes)
BOOST_REQUIRE(payload == expected);
}
+BOOST_FIXTURE_TEST_CASE(functional_p2p__get_data__unknown_block__not_found,
+ p2p_not_found_setup_fixture)
+{
+ BOOST_REQUIRE(handshake());
+
+ const get_data get{ { { inventory_item::type_id::block, system::one_hash } } };
+ send(get, node_version->value);
+
+ const auto payload = receive(not_found::command);
+ const auto message = not_found::deserialize(node_version->value, payload);
+ BOOST_REQUIRE(message);
+ BOOST_REQUIRE_EQUAL(message->items.size(), one);
+ BOOST_REQUIRE(message->items.front().hash == system::one_hash);
+}
+
+BOOST_FIXTURE_TEST_CASE(functional_p2p__get_data__pruned_block__not_found,
+ p2p_limited_setup_fixture)
+{
+ BOOST_REQUIRE(handshake());
+
+ const system::chain::block& genesis = config_.bitcoin.genesis_block;
+ const get_data get{ { { inventory_item::type_id::block, genesis.hash() } } };
+ send(get, node_version->value);
+
+ const auto payload = receive(not_found::command);
+ const auto message = not_found::deserialize(node_version->value, payload);
+ BOOST_REQUIRE(message);
+ BOOST_REQUIRE_EQUAL(message->items.size(), one);
+}
+
+BOOST_FIXTURE_TEST_CASE(functional_p2p__get_data__unassociated_block__not_found,
+ p2p_unassociated_setup_fixture)
+{
+ BOOST_REQUIRE(handshake());
+
+ const auto hash = unassociated().hash();
+ const get_data get{ { { inventory_item::type_id::block, hash } } };
+ send(get, node_version->value);
+
+ const auto payload = receive(not_found::command);
+ const auto message = not_found::deserialize(node_version->value, payload);
+ BOOST_REQUIRE(message);
+ BOOST_REQUIRE_EQUAL(message->items.size(), one);
+ BOOST_REQUIRE(message->items.front().hash == hash);
+}
+
+BOOST_FIXTURE_TEST_CASE(functional_p2p__get_data__unknown_transaction__not_found,
+ p2p_relay_setup_fixture)
+{
+ BOOST_REQUIRE(handshake(0, level::maximum_protocol, true));
+
+ const get_data get{ { { inventory_item::type_id::transaction, system::one_hash } } };
+ send(get, node_version->value);
+
+ const auto payload = receive(not_found::command);
+ const auto message = not_found::deserialize(node_version->value, payload);
+ BOOST_REQUIRE(message);
+ BOOST_REQUIRE_EQUAL(message->items.size(), one);
+ BOOST_REQUIRE(message->items.front().hash == system::one_hash);
+}
+
+// not_found is undefined below bip37, so the channel is stopped instead.
+BOOST_FIXTURE_TEST_CASE(functional_p2p__get_data__unknown_block_106__stopped,
+ p2p_not_found_setup_fixture)
+{
+ BOOST_REQUIRE(handshake(0, level::bip35));
+
+ const get_data get{ { { inventory_item::type_id::block, system::one_hash } } };
+ send(get, level::bip35);
+
+ // The channel is stopped, so the socket closes without a not_found.
+ BOOST_REQUIRE_THROW(receive(not_found::command), boost::system::system_error);
+}
+
+// The option is off by default, so the channel is stopped instead.
+BOOST_AUTO_TEST_CASE(functional_p2p__get_data__unknown_block_disabled__stopped)
+{
+ BOOST_REQUIRE(handshake());
+
+ const get_data get{ { { inventory_item::type_id::block, system::one_hash } } };
+ send(get, node_version->value);
+
+ BOOST_REQUIRE_THROW(receive(not_found::command), boost::system::system_error);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/functional/p2p_setup_fixture.cpp b/test/functional/p2p_setup_fixture.cpp
index a4acf10d..ddfddb1f 100644
--- a/test/functional/p2p_setup_fixture.cpp
+++ b/test/functional/p2p_setup_fixture.cpp
@@ -134,7 +134,8 @@ data_chunk p2p_setup_fixture::receive(const std::string& command)
}
}
-bool p2p_setup_fixture::handshake(uint64_t services, uint32_t value)
+bool p2p_setup_fixture::handshake(uint64_t services, uint32_t value,
+ bool relay)
{
version out{};
out.value = value;
@@ -143,7 +144,7 @@ bool p2p_setup_fixture::handshake(uint64_t services, uint32_t value)
out.nonce = 42424242;
out.user_agent = "/test/";
out.start_height = 0;
- out.relay = false;
+ out.relay = relay;
send(out, value);
// The node sends its version upon attach and verack upon our version.
@@ -170,4 +171,18 @@ bool p2p_setup_fixture::handshake(uint64_t services, uint32_t value)
return true;
}
+system::chain::header p2p_unassociated_setup_fixture::unassociated() NOEXCEPT
+{
+ const system::settings bitcoin{ chain::selection::mainnet };
+ return
+ {
+ 1u,
+ bitcoin.genesis_block.hash(),
+ system::null_hash,
+ 0u,
+ 0u,
+ 0u
+ };
+}
+
BC_POP_WARNING()
diff --git a/test/functional/p2p_setup_fixture.hpp b/test/functional/p2p_setup_fixture.hpp
index b4a7f80c..ae12f910 100644
--- a/test/functional/p2p_setup_fixture.hpp
+++ b/test/functional/p2p_setup_fixture.hpp
@@ -55,7 +55,8 @@ struct p2p_setup_fixture
/// Perform the version handshake, retains the node's version message.
bool handshake(uint64_t services=0,
- uint32_t version=network::messages::peer::level::maximum_protocol);
+ uint32_t version=network::messages::peer::level::maximum_protocol,
+ bool relay=false);
/// The node's version message (set by handshake).
network::messages::peer::version::cptr node_version{};
@@ -72,4 +73,65 @@ struct p2p_setup_fixture
boost::asio::ip::tcp::socket socket_{ io_ };
};
+// A node configured to reply not_found.
+struct p2p_not_found_setup_fixture
+ : p2p_setup_fixture
+{
+ inline p2p_not_found_setup_fixture()
+ : p2p_setup_fixture({}, [](configuration& config)
+ {
+ config.network.enable_not_found = true;
+ })
+ {
+ }
+};
+
+// A node that relays transactions and replies not_found.
+struct p2p_relay_setup_fixture
+ : p2p_setup_fixture
+{
+ inline p2p_relay_setup_fixture()
+ : p2p_setup_fixture({}, [](configuration& config)
+ {
+ config.network.enable_relay = true;
+ config.network.enable_not_found = true;
+ })
+ {
+ }
+};
+
+// A node that does not store the blocks it has pruned.
+struct p2p_limited_setup_fixture
+ : p2p_setup_fixture
+{
+ inline p2p_limited_setup_fixture()
+ : p2p_setup_fixture({}, [](configuration& config)
+ {
+ config.node.limited_blocks = true;
+ config.network.enable_not_found = true;
+ })
+ {
+ }
+};
+
+// A header is archived before its block is associated, so the header link
+// resolves while the block remains absent from the archive. This is the
+// steady state of headers-first synchronization.
+struct p2p_unassociated_setup_fixture
+ : p2p_setup_fixture
+{
+ static system::chain::header unassociated() NOEXCEPT;
+
+ inline p2p_unassociated_setup_fixture()
+ : p2p_setup_fixture([](node::query& query)
+ {
+ return query.set(unassociated(), database::context{}, false);
+ }, [](configuration& config)
+ {
+ config.network.enable_not_found = true;
+ })
+ {
+ }
+};
+
#endif