Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/bitcoin/node/protocols/protocol_block_out_106.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class BCN_API protocol_block_out_106
node_pruned_(session->node_settings().limited_blocks),
node_witness_(session->node_settings().provide_witness),
allow_overlapped_(session->node_settings().allow_overlapped),
not_found_allowed_
(
std::dynamic_pointer_cast<network::channel_peer>(channel)->
is_negotiated(network::messages::peer::level::bip37)
),
network::tracker<protocol_block_out_106>(session->log)
{
}
Expand Down Expand Up @@ -73,10 +78,12 @@ class BCN_API protocol_block_out_106

private:
using inventory = network::messages::peer::inventory;
using not_found = network::messages::peer::not_found;
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;
void send_not_found(const inventory_item& item) NOEXCEPT;
inventory create_inventory(const get_blocks& locator) const NOEXCEPT;
void merge_inventory(const inventory_items& items) NOEXCEPT;

Expand All @@ -85,6 +92,7 @@ class BCN_API protocol_block_out_106
const bool node_pruned_;
const bool node_witness_;
const bool allow_overlapped_;
const bool not_found_allowed_;

// This is protected by strand.
std::deque<inventory_item> backlog_{};
Expand Down
45 changes: 40 additions & 5 deletions src/protocols/protocol_block_out_106.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,13 @@ bool protocol_block_out_106::handle_receive_get_data(const code& ec,
void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return;

if (backlog_.empty()) return;
if (backlog_.empty())
return;

const auto& item = backlog_.front();
const auto witness = item.is_witness_type();
if (witness && !node_witness_)
Expand All @@ -216,11 +219,23 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT

const auto& query = archive();
const auto link = query.to_header(item.hash);

// 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.");

send_not_found(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);

send_not_found(item);
return;
}

Expand All @@ -229,14 +244,13 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
{
{ query.get_wire_block(link, witness), witness }
};

if (!out.block.is_valid())
{
LOGR("Requested block " << encode_hash(item.hash) << " from ["
<< 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);
send_not_found(item);
return;
}

Expand All @@ -248,6 +262,27 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT
// utilities
// ----------------------------------------------------------------------------

// Block-out protocols are selected on send_headers, so a peer above bip37
// reaches this one through the default arm. not_found is undefined below
// bip37, so the negotiated level is captured at construction and tested here.
void protocol_block_out_106::send_not_found(
const inventory_item& item) NOEXCEPT
{
BC_ASSERT(stranded());

if (!not_found_allowed_)
{
stop(system::error::not_found);
return;
}

// The message is created before the item is dequeued, as item references
// the front of the backlog.
not_found out{ { item } };
backlog_.pop_front();
SEND(std::move(out), send_block, _1);
}

void protocol_block_out_106::merge_inventory(
const inventory_items& items) NOEXCEPT
{
Expand Down
5 changes: 3 additions & 2 deletions src/protocols/protocol_transaction_out_106.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,9 @@ void protocol_transaction_out_106::send_transaction(const code& ec,
LOGR("Requested tx " << encode_hash(item.hash)
<< " from [" << opposite() << "] not found.");

// This tx could not have been advertised to the peer.
stop(system::error::not_found);
// The protocol is attached above bip37, where not_found is defined.
BC_ASSERT(negotiated_version() >= level::bip37);
SEND(not_found{ { item } }, send_transaction, _1, add1(index), message);
return;
}

Expand Down
41 changes: 41 additions & 0 deletions test/functional/p2p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,45 @@ BOOST_AUTO_TEST_CASE(functional_p2p__get_data__genesis_block__expected_bytes)
BOOST_REQUIRE(payload == expected);
}

BOOST_AUTO_TEST_CASE(functional_p2p__get_data__unknown_block__not_found)
{
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);
}

// A limited node answers for a block it has pruned, and remains unfaulted.
struct p2p_limited_setup_fixture
: p2p_setup_fixture
{
p2p_limited_setup_fixture() NOEXCEPT
: p2p_setup_fixture({}, [](configuration& config) NOEXCEPT
{
config.node.limited_blocks = true;
})
{
}
};

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_AUTO_TEST_SUITE_END()
Loading