Skip to content

Conversation

LarryRuane
Copy link
Contributor

@LarryRuane LarryRuane commented Feb 6, 2023

This PR adds last_block_announcement to the per-peer getpeerinfo RPC result. This is the most recent time that this peer was the first to notify our node of a new block (one that we didn't already know about), or zero if this peer has never been the first to notify us of a new block. This timestamp already exists internally and is used for stale-tip eviction logic; this PR exposes it at the RPC layer.

This PR started out as a suggestion for additional test coverage, see #26172 (comment). It turned out that the easiest way to test (already-merged) #26172 is to add this field to getpeerinfo and have a functional test verify its value. But it may also be useful to have this result in its own right, similar to that RPC's existing last_block field -- it indicates something about the quality of our peers. It allows one to predict which peer will be evicted when the stale tip logic activates. (I'm not sure if that would be useful, but it may be.)

The functional test added here fails without #26172, which is the main goal.

This PR does not test the actual stale-tip eviction logic; that's difficult to do with a functional test. But it does test the correctness of the timestamp that the eviction logic depends on. #23352 is an attempt to test the eviction logic using a unit test, it's not ready to be merged yet. I think having both kinds of tests would be beneficial.

@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 6, 2023

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/27052.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK naiyoma
Concept ACK satsie, kristapsk
Stale ACK rkrux

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

@LarryRuane
Copy link
Contributor Author

Force pushed to fix CI failure

@satsie
Copy link
Contributor

satsie commented Feb 7, 2023

Concept ACK. I spent quite a bit of time trying to test #26172 (my findings are documented here) and can confirm the difficulty of testing it with the current state of the test framework.

I also support the suggestion to add last_block_announcement to getpeerinfo not only because it seems to be the easiest way to test 26172, but I agree it can be useful information to have when trying to learn more about one's peers, especially when this timestamp is already available internally.

src/net.h Outdated
@@ -505,6 +505,10 @@ class CNode
* peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<std::chrono::seconds> m_last_block_time{0s};

/** UNIX epoch time of the last block announcement from this peer that we had
* not yet seen from any peer. Used for potential stale-tip eviction. */
std::atomic<std::chrono::seconds> m_last_block_announcement{0s};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you change the type, why not use NodeSeconds (a time point)? While std::chrono::seconds (a duration) is used widely in the codebase, because all durations that denote a time point are since epoch, using a dedicated type such as NodeSeconds might be clearer.

src/net.h Outdated
@@ -505,6 +506,10 @@ class CNode
* peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<std::chrono::seconds> m_last_block_time{0s};

/** UNIX epoch time of the last block announcement from this peer that we had
* not yet seen from any peer. Used for potential stale-tip eviction. */
std::atomic<std::chrono::seconds> m_last_block_announcement{0s};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this belongs in CNode. It is used in our outbound eviction logic which is part of net processing and it isn't used in net at all (imo, the eviction logic should be encapsulated in its own module entirely #25268 😉).

Also afaict, there is no need to move m_last_block_announcement, because you can just return it in PeerManager::GetNodeStateStats.

@LarryRuane
Copy link
Contributor Author

Force pushed from becf380 to

I'll take this out of draft now since I think it's ready for review.

@DrahtBot
Copy link
Contributor

Needs rebase, if still relevant

@LarryRuane LarryRuane force-pushed the 2023-02-getpeerinfo branch from 21ad9f8 to 559b091 Compare March 31, 2025 22:45
@LarryRuane
Copy link
Contributor Author

Rebased for merge conflicts and to adopt @naiyoma's review suggestion (thank you!).

@LarryRuane LarryRuane force-pushed the 2023-02-getpeerinfo branch from 5bcf849 to f7788f6 Compare April 30, 2025 20:34
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Jun 6, 2025
Copy its value from CNodeState. Unused until the next commit.

Github-Pull: bitcoin#27052
Rebased-From: ff69990
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Jun 6, 2025
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Jun 6, 2025
Github-Pull: bitcoin#27052
Rebased-From: b15ecab
luke-jr pushed a commit to luke-jr/bitcoin that referenced this pull request Jun 6, 2025
Github-Pull: bitcoin#27052
Rebased-From: 5bcf849
time (currently 30 minutes, see TipMayBeStale()), the client, suspecting
that there may be new blocks that its peers are not announcing, will
add an extra outbound peer and disconnect (evict) the peer that has
least recently been the first to announced a new block to us. (If there
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first to announced a new block → first to announce a new block

@@ -5189,7 +5189,7 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now)
// Protect peers from eviction if we don't have another connection
// to their network, counting both outbound-full-relay and manual peers.
NodeId worst_peer = -1;
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
std::optional<NodeSeconds> oldest_block_announcement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand why this is optional. Originally, it was set to a sentinel value.
I don't see a situation where it wouldn't have a value.
My suggestion → NodeSeconds oldest_block_announcement = NodeSeconds::max();
That way .has_value() checks and *oldest_block_announcement can be avoided.

Copy link
Member

@theuni theuni Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional + has_value() communicates intent much more clearly than the sentinel imo. Though admittedly it's awkward as long as worst_peer uses a sentinel as well.

I'd expect to see std::optional<std::pair<NodeId, NodeSeconds>> worst_peer, personally.

Copy link
Contributor

@naiyoma naiyoma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK cbe4603

Changes since last review: rebased and addressed nit fixes.

Also tested with msg_cmpctblock - test passed as expected.

@DrahtBot DrahtBot requested a review from rkrux August 22, 2025 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants