Skip to content

Commit 16a419d

Browse files
committed
don't send the full rejected block inside closing_connection_message
When a node disconnects a peer for offering a bad block, it sends the exception in the goodbye message. The generic capture embedded the entire block; a block with transactions serializes deeper than the unpack-side recursion limit (MAX_RECURSION_DEPTH=20), so no peer on the network could actually decode these goodbyes -- during the 2026-07-09 incident one node logged 6176 such failed unpacks, hiding the real disconnect reasons. Capture only the block num and id (still useful to the disconnected peer). Also make the receiver tolerant: if a closing_connection_message can't be deserialized, log it and treat it as a close request without details instead of tearing down the read loop with an assert -- the peer is closing the connection either way.
1 parent b31dd12 commit 16a419d

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

libraries/net/node.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,8 +2052,25 @@ namespace graphene { namespace net {
20522052
on_item_ids_inventory_message(originating_peer, received_message.as<item_ids_inventory_message>());
20532053
break;
20542054
case core_message_type_enum::closing_connection_message_type:
2055-
on_closing_connection_message(originating_peer, received_message.as<closing_connection_message>());
2056-
break;
2055+
{
2056+
closing_connection_message closing_message_received;
2057+
try
2058+
{
2059+
closing_message_received = received_message.as<closing_connection_message>();
2060+
}
2061+
catch (const fc::exception& e)
2062+
{
2063+
// The peer is closing the connection regardless of whether we can read its reason for
2064+
// doing so, so don't treat an undecodable goodbye as a protocol error (e.g. older peers
2065+
// embed an exception whose serialization can nest deeper than our recursion limit).
2066+
wlog("Unable to decode closing_connection_message from peer ${peer}, treating it as a close request "
2067+
"with no details: ${e}",
2068+
("peer", originating_peer->get_remote_endpoint())("e", e.to_detail_string()));
2069+
closing_message_received.reason_for_closing = "<unable to decode the closing_connection_message this peer sent us>";
2070+
}
2071+
on_closing_connection_message(originating_peer, closing_message_received);
2072+
break;
2073+
}
20572074
case core_message_type_enum::block_message_type:
20582075
case core_message_type_enum::compressed_block_message_type:
20592076
fc::async( [=]() { process_block_message(originating_peer, received_message, message_hash); }, "process_block_msg");

libraries/plugins/chain/chain_plugin.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,7 @@ bool chain_plugin_impl::push_block( const block_flow_control& block_ctrl, uint32
10431043
const signed_block_header& new_block_header = new_block;
10441044

10451045
uint32_t block_num = full_block->get_block_num();
1046+
const block_id_type block_id = full_block->get_block_id();
10461047
if( !checkpoints.empty() && block_num <= checkpoints.rbegin()->first )
10471048
{
10481049
skip = database::skip_witness_signature
@@ -1083,7 +1084,10 @@ bool chain_plugin_impl::push_block( const block_flow_control& block_ctrl, uint32
10831084
// we only need header (if anything at all) in most common case of block from unknown fork
10841085
FC_RETHROW_EXCEPTION( er, warn, "", FC_FORMAT_ARG_PARAMS( (new_block_header) ) );
10851086
}
1086-
FC_CAPTURE_AND_RETHROW( (new_block) )
1087+
// capture only the block's identity, not the full block: this exception is sent to the
1088+
// offending peer inside a closing_connection_message, and a full block nests deeper than
1089+
// the serializer's recursion limit, making the message undecodable by the receiver
1090+
FC_CAPTURE_AND_RETHROW( (block_num)(block_id) )
10871091

10881092
db.check_free_memory( false, full_block->get_block_num() );
10891093
});

0 commit comments

Comments
 (0)