diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index ca695c9e..80fa44d1 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -41,6 +41,7 @@ class BCN_API protocol_block_in_31800 session->system_settings().top_checkpoint().height()), block_type_(session->network_settings().witness_node() ? type_id::witness_block : type_id::block), + node_pruned_(session->network_settings().pruned_node()), map_(chaser_check::empty_map()), network::tracker(session->log) { @@ -84,6 +85,7 @@ class BCN_API protocol_block_in_31800 void restore(const map_ptr& map) NOEXCEPT; bool is_under_checkpoint(size_t height) const NOEXCEPT; + type_id to_block_type(const database::association& item) const NOEXCEPT; void handle_put_hashes(const code& ec, size_t count) NOEXCEPT; void handle_get_hashes(const code& ec, const map_ptr& map, const job::ptr& job) NOEXCEPT; @@ -91,6 +93,7 @@ class BCN_API protocol_block_in_31800 // These are thread safe. const size_t top_checkpoint_height_; const type_id block_type_; + const bool node_pruned_; // These are protected by strand. map_ptr map_; diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index e65f2c72..ae5d2055 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -36,6 +36,9 @@ class BCN_API protocol_block_out_106 protocol_block_out_106(const auto& session, const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), + top_checkpoint_height_( + session->system_settings().top_checkpoint().height()), + node_pruned_(session->network_settings().pruned_node()), node_witness_(session->network_settings().witness_node()), allow_overlapped_(session->node_settings().allow_overlapped), network::tracker(session->log) @@ -73,10 +76,13 @@ class BCN_API protocol_block_out_106 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; inventory create_inventory(const get_blocks& locator) const NOEXCEPT; void merge_inventory(const inventory_items& items) NOEXCEPT; // These are thread safe. + const size_t top_checkpoint_height_; + const bool node_pruned_; const bool node_witness_; const bool allow_overlapped_; diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 78d78d00b..c6709af5 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -254,7 +254,7 @@ get_data protocol_block_in_31800::create_get_data( // bip144: get_data uses witness type_id but inv does not. std::for_each(map.pos_begin(), map.pos_end(), [&](const auto& item) NOEXCEPT { - data.items.emplace_back(block_type_, item.hash); + data.items.emplace_back(to_block_type(item), item.hash); }); return data; @@ -277,8 +277,6 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, const auto& block = message->block; const auto hash = block.hash(); const auto it = map_->find(hash); - auto& query = archive(); - if (it == map_->end()) { // Allow unrequested block, not counted toward performance. @@ -287,15 +285,21 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, return true; } + auto& query = archive(); const auto link = it->link; const auto height = it->context.height; + const auto checked = is_under_checkpoint(height); + const auto bypass = checked || query.is_milestone(link); + if (bypass && node_pruned_ && block.is_segregated()) + { + LOGR("Unexpected witness from [" << opposite() << "]."); + stop(system::error::unexpected_witness); + return false; + } // Identify block. // ........................................................................ - const auto checked = is_under_checkpoint(height); - const auto bypass = checked || query.is_milestone(link); - // Tx commitments and malleation are checked under bypass. Invalidity is // only stored when a strong header has been stored, later to be found out // as invalid and not malleable. Stored invalidity prevents repeat @@ -363,11 +367,16 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, // While check could be called here, it's more optimal to defer to validate, as // requiring only identity here allows the use of the simplified block_view. code protocol_block_in_31800::identify(const chain::block_view& block, - const chain::context& ctx, bool) const NOEXCEPT + const chain::context& ctx, bool bypass) const NOEXCEPT { - code ec{}; + if (const auto ec = block.identify()) + return ec; + + // Bypass witness commitment check for stripped blocks. + if (bypass && node_pruned_) + return error::success; - if (((ec = block.identify())) || ((ec = block.identify(ctx)))) + if (const auto ec = block.identify(ctx)) return ec; return error::success; @@ -420,7 +429,7 @@ void protocol_block_in_31800::handle_get_hashes(const code& ec, POST(send_get_data, map, job); } -// checkpoint +// utility // ---------------------------------------------------------------------------- bool protocol_block_in_31800::is_under_checkpoint(size_t height) const NOEXCEPT @@ -428,6 +437,16 @@ bool protocol_block_in_31800::is_under_checkpoint(size_t height) const NOEXCEPT return height <= top_checkpoint_height_; } +type_id protocol_block_in_31800::to_block_type( + const association& item) const NOEXCEPT +{ + const auto stripped = node_pruned_ && + (is_under_checkpoint(item.context.height) || + archive().is_milestone(item.link)); + + return stripped ? type_id::block : block_type_; +} + BC_POP_WARNING() BC_POP_WARNING() BC_POP_WARNING() diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp index 13c90ebe..6c97fd08 100644 --- a/src/protocols/protocol_block_out_106.cpp +++ b/src/protocols/protocol_block_out_106.cpp @@ -32,9 +32,15 @@ using namespace network; using namespace std::chrono; using namespace std::placeholders; +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED) BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) +// This protocol provides witness support despite 106 being much older than +// bip144. This is because protocols are splot on the version negotiated in the +// p2p handshake. Witness is enabled via a service bit. Protocols should also +// be factored based on relevant service bits but that is not yet implemented. + // start/stop // ---------------------------------------------------------------------------- @@ -202,7 +208,7 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT if (backlog_.empty()) return; const auto& item = backlog_.front(); const auto witness = item.is_witness_type(); - if (!node_witness_ && witness) + if (witness && !node_witness_) { LOGR("Unsupported witness get_data from [" << opposite() << "]."); stop(network::error::protocol_violation); @@ -210,9 +216,19 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT } const auto& query = archive(); - const auto start = logger::now(); const auto link = query.to_header(item.hash); + if (witness && node_witness_ && node_pruned_ && + (is_under_checkpoint(link) || query.is_milestone(link))) + { + LOGR("Request of witness for stripped block " + << encode_hash(item.hash) << " from [" << opposite() << "]."); + + // The peer should not be asking for this block with witness. + stop(system::error::not_found); + return; + } + const auto start = logger::now(); node::messages::block out{ query.get_wire_block(link, witness), witness }; if (out.block_data.empty()) { @@ -255,6 +271,20 @@ protocol_block_out_106::inventory protocol_block_out_106::create_inventory( ); } +bool protocol_block_out_106::is_under_checkpoint( + const database::header_link& link) NOEXCEPT +{ + const auto height = archive().get_height(link); + if (height.is_terminal()) + { + fault(database::error::integrity); + return false; + } + + return height <= top_checkpoint_height_; +} + +BC_POP_WARNING() BC_POP_WARNING() BC_POP_WARNING()