p2p: update peer's height on new block & widen window for relay
What changed, and why it matters
This Monero patch fixes how the node keeps track of other peers' blockchain heights and who gets to receive new transactions. Previously, a node might think a peer was behind and stop sending it transactions, even when the peer actually had the latest block. The patch updates the peer's height when a new block is validated and allows a small two-block tolerance when choosing transaction-relay peers. The main risk if this were buggy or absent is degraded network propagation: transactions or blocks might spread more slowly, which can hurt network health and potentially be exploited to gain timing advantages (for example, in mining or double-spend scenarios).
Treat as a normal network-layer reliability fix. Reviewers should verify that the +2 window does not enable transaction relay to significantly out-of-sync peers and that the height update logic does not incorrectly advance a peer's height on alternate-chain blocks. No urgent security response is indicated by the commit alone.
Security signals we found
P2P propagation robustness improvement
Peer height tracking correction
Transaction relay candidate window widened
Comment references prior PR discussion about height semantics
No explicit vulnerability or CVE mentioned in commit
Evidence from the diff
The commit modifies fluffy-block handling in cryptonote_protocol_handler.inl and peer selection in levin_notify.cpp. It (A) updates context.m_remote_blockchain_height to the peer’s reported height when a received block is added to the main chain or already exists, and (B) widens the outgoing-connection selection window for transaction relay from remote_height >= blockchain_height to (remote_height + 2) >= blockchain_height. The change also corrects the height used in missing-tx requests and empty-block relay to new_block_height + 1. The security relevance is indirect: poor propagation can create eclipse/partitioning-like conditions or give an attacker a timing edge, but the diff itself is a robustness improvement rather than a clear vulnerability fix.
Changed components
src/cryptonote_protocol/cryptonote_protocol_handler.inlsrc/cryptonote_protocol/levin_notify.cppInspect captured patch +17 / −3
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 825bb17..2a32295 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -636,8 +636,10 @@ namespace cryptonote
}
// Log block info
+ const uint64_t new_block_height = get_block_height(new_block);
+ const uint64_t peer_height = arg.current_blockchain_height;
MLOG_P2P_MESSAGE(context << "Received NOTIFY_NEW_FLUFFY_BLOCK " << new_block_hash << " (height "
- << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
+ << new_block_height << ", " << arg.b.txs.size() << " txes, peer's height: " << peer_height << ")");
// Pause mining and resume after block verification to prevent wasted mining cycles while
// validating the next block. Needs more research into if this is a DoS vector or not. Invalid
@@ -726,7 +728,7 @@ namespace cryptonote
MDEBUG(" tx " << new_block.tx_hashes[txidx]);
NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req;
missing_tx_req.block_hash = new_block_hash;
- missing_tx_req.current_blockchain_height = arg.current_blockchain_height;
+ missing_tx_req.current_blockchain_height = new_block_height + 1;
missing_tx_req.missing_tx_indices = std::move(need_tx_indices);
// Post NOTIFY_REQUEST_FLUFFY_MISSING_TX request to peer
@@ -743,6 +745,7 @@ namespace cryptonote
}
else if( bvc.m_added_to_main_chain )
{
+ arg.current_blockchain_height = new_block_height + 1;
// Relay an empty block
arg.b.txs.clear();
relay_block(arg, context);
@@ -762,6 +765,15 @@ namespace cryptonote
MLOG_PEER_STATE("requesting chain");
}
+ if (bvc.m_added_to_main_chain || bvc.m_already_exists)
+ {
+ // Update peer's sync height using this block we just validated
+ // Note: peer_height is not guaranteed to be the height of the block we just validated + 1.
+ // See https://github.com/monero-project/monero/pull/11048#discussion_r3720736824
+ if (peer_height == (new_block_height + 1))
+ context.m_remote_blockchain_height = peer_height;
+ }
+
// load json & DNS checkpoints every 10min/hour respectively,
// and verify them with respect to what blocks we already have
CHECK_AND_ASSERT_MES(m_core.update_checkpoints(), 1, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index 91db981..583c3da 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -148,7 +148,9 @@ namespace levin
of waiting in here. */
p2p.foreach_connection([&outs, blockchain_height] (detail::p2p_context& context) {
- if (!context.m_is_income && context.m_remote_blockchain_height >= blockchain_height)
+ // Give a +2 allowable window for candidate peers, since we may not have updated the peer's
+ // known sync height yet (e.g. if we haven't received or finished processing their new block(s) yet)
+ if (!context.m_is_income && (context.m_remote_blockchain_height + 2) >= blockchain_height)
outs.emplace_back(context.m_connection_id);
return true;
});
Why this scored 38/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.