New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify network-adjusted time warning logic #29623
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
d1e51c4
to
db55a31
Compare
🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the Possibly this is due to a silent merge conflict (the changes in this pull request being Leave a comment here, if you need help tracking down a confusing failure. |
Concept ACK Thanks for picking this up! |
Concept ACK |
d15b847
to
55b476b
Compare
Force pushed to:
|
Code review ACK 55b476b I also:
|
55b476b
to
59e7dbd
Compare
Force pushed to:
FYI this is not new functionality, you should see the same behaviour on |
59e7dbd
to
5c8e71c
Compare
Concept ACK, I love simplifications or refactors that are net removing code (+213 −363). |
on the 2 value selections (50 outbound peers):
|
utACK 5c8e71c |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm 2ef71c7 🛴
Show signature
Signature:
untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
trusted comment: lgtm 2ef71c73582be554e565ada3f8a6ca77c2cd79f1 🛴
Q9sgCN5S+/6cPhk6lYNjDonkzAs6Gm8QI7XynAlXDMCM29pQnMy67XGFMTPuBfZCMwdyVCo/S1sFWJI8l5xkCw==
Reminder for myself: #29623 (comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach ACK 2ef71c7
Wrt adjusting the peer's offset with local clock corrections in order to stop the warnings immediately after the local clock is fixed (instead of after 4-5h) #29623 (comment), consider this:
[patch] adjust peer time with local clock corrections (untested)
diff --git i/src/node/timeoffsets.cpp w/src/node/timeoffsets.cpp
index 8488e47ff5..4dce6f9edd 100644
--- i/src/node/timeoffsets.cpp
+++ w/src/node/timeoffsets.cpp
@@ -24,27 +24,31 @@ void TimeOffsets::Add(std::chrono::seconds offset)
{
LOCK(m_mutex);
if (m_offsets.size() >= N) {
m_offsets.pop_front();
}
- m_offsets.push_back(offset);
+ m_offsets.emplace_back(offset, std::chrono::steady_clock::now(), std::chrono::system_clock::now());
LogDebug(BCLog::NET, "Added time offset %+ds, total samples %d\n",
Ticks<std::chrono::seconds>(offset), m_offsets.size());
}
std::chrono::seconds TimeOffsets::Median() const
{
LOCK(m_mutex);
// Only calculate the median if we have 5 or more offsets
if (m_offsets.size() < 5) return 0s;
+ const auto steady_now = std::chrono::steady_clock::now();
+ const auto system_now = std::chrono::system_clock::now();
auto sorted_copy = m_offsets;
- std::sort(sorted_copy.begin(), sorted_copy.end());
- return sorted_copy[m_offsets.size() / 2]; // approximate median is good enough, keep it simple
+ std::sort(sorted_copy.begin(), sorted_copy.end(), [&](const peer_time_data& a, const peer_time_data& b) {
+ return a.Offset(steady_now, system_now) < b.Offset(steady_now, system_now);
+ });
+ return sorted_copy[m_offsets.size() / 2].Offset(steady_now, system_now); // approximate median is good enough, keep it simple
}
bool TimeOffsets::WarnIfOutOfSync() const
{
// when median == std::numeric_limits<int64_t>::min(), calling std::chrono::abs is UB
auto median{std::max(Median(), std::chrono::seconds(std::numeric_limits<int64_t>::min() + 1))};
diff --git i/src/node/timeoffsets.h w/src/node/timeoffsets.h
index 8bccae20e8..1d7a9f0153 100644
--- i/src/node/timeoffsets.h
+++ w/src/node/timeoffsets.h
@@ -21,13 +21,29 @@ private:
//! Log the last time a GUI warning was raised to avoid flooding user with them
mutable std::optional<std::chrono::steady_clock::time_point> m_warning_emitted GUARDED_BY(m_mutex){};
//! Minimum time to wait before raising a new GUI warning
static constexpr std::chrono::minutes m_warning_wait{60};
mutable Mutex m_mutex;
- std::deque<std::chrono::seconds> m_offsets GUARDED_BY(m_mutex){};
+ struct peer_time_data {
+ //! The difference between peer's time and our time, at the time of the P2P handshake.
+ std::chrono::seconds handshake_offset;
+ // The following two are used to detect system clock corrections/adjustments.
+ std::chrono::time_point<std::chrono::steady_clock> handshake_steady;
+ std::chrono::time_point<std::chrono::system_clock> handshake_system;
+
+ /// Calculate the offset as of now, taking into account any system clock corrections that
+ /// have happened since the peer connected.
+ std::chrono::seconds Offset(std::chrono::time_point<std::chrono::steady_clock> steady_now,
+ std::chrono::time_point<std::chrono::system_clock> system_now) const {
+ return handshake_offset +
+ std::chrono::duration_cast<std::chrono::seconds>(steady_now - handshake_steady) -
+ std::chrono::duration_cast<std::chrono::seconds>(system_now - handshake_system);
+ }
+ };
+ std::deque<peer_time_data> m_offsets GUARDED_BY(m_mutex){};
public:
/** Add a new time offset sample. */
void Add(std::chrono::seconds offset) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/** Compute and return the median of the collected time offset samples. */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approach ACK
The test requires that limited nodes are not peered with when the node's system time exceeds ~ 24h of the node's chaintip timestamp, as per PeerManagerImpl::GetDesirableServiceFlags. By patching this test to modify the timestamp of the chaintip as opposed to mocking the node's system time, we make it resilient to future commits where the node raises a warning if it detects its system time is too much out of sync with its outbound peers. See bitcoin/bitcoin#29623
2ef71c7
to
5362db1
Compare
This helper class is an alternative to CMedianFilter, but without a lot of the special logic and exceptions that we needed while it was still used for consensus.
For querying statistics/info from PeerManager. The median outbound time offset is the only initial field.
With the introduction and usage of TimeOffsets, there is no more need for this file. Remove it.
5362db1
to
c6be144
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much @sr-gi @maflcko @vasild @glozow @mzumsande for your extensive review.
Force pushed to address all review comments:
- in GUI: display the warning along with other warnings, instead of in a messagebox. This prevents the warning from Simplify network-adjusted time warning logic #29623 (comment) (and is a nicer approach in general)
- various nits around improved documentation, variable naming, references, help messages, ...
- I probably won't be addressing nits in this PR anymore to minimize review burden, so if there are any left (please keep posting them if you see any) I'll keep them for a follow-up.
Also, qt/guiutils.h still refers to CNodeCombinedStats.nTimeOffset
References updated, thanks
Wrt adjusting the peer's offset with local clock corrections in order to stop the warnings immediately after the local clock is fixed (instead of after 4-5h) #29623 (comment), consider this:
I like the idea of using steady_clock here, but I would prefer to do it in a follow-up if there's appetite for it. It's nice to have the warning disappear more quickly when the issue is resolved, but I think a downside is that the timeoffset peer stats in GUI and RPC will be inconsistent with the ones we use for the warning. I'd started implementing a PeerClock approach based on your suggestion, and I think it's quite elegant, but it's a lot more code overhaul so not really ideal for this PR. WIP commit at master...stickies-v:bitcoin:2024-03/remove-timedata-keep-clock
#include <limits> | ||
#include <optional> | ||
|
||
using namespace std::chrono_literals; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is for if (m_offsets.size() < 5) return 0s;
?
Re-ACK c6be144 |
reACK c6be144 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK c6be144
Ported to the CMake-based build system in hebasto#186. |
…tcoin-config.h includes fa09451 Add lint check for bitcoin-config.h include IWYU pragma (MarcoFalke) dddd40b scripted-diff: Add IWYU pragma keep to bitcoin-config.h includes (MarcoFalke) Pull request description: The `bitcoin-config.h` includes have issues: * The header is incompatible with iwyu, because symbols may be defined or not defined. So the `IWYU pragma: keep` is needed to keep the include when a symbol is not defined on a platform. Compare the previous discussion in #29408 (comment) * Guarding the includes by `HAVE_CONFIG_H` is verbose and brittle. Now that all build config dependencies have been removed from low level headers, the benefits are questionable, and the guard can be removed. The linter could also be tricked by guarding the include by `#if defined(HAVE_C0NFIG_H)` (`O` replaced by `0`). Compare the previous discussion in #29404 (comment) . ACKs for top commit: achow101: ACK fa09451 TheCharlatan: ACK fa09451 hebasto: re-ACK fa09451, only rebased since my recent [review](#29494 (review)) (`timedata.cpp` removed in #29623). Tree-SHA512: 47cb973f7f24bc625acc4e78683371863675d186780236d55d886cf4130e05a78bb04f1d731aae7088313b8e963a9677cc77cf518187dbd99d776f6421ca9b52
An earlier approach in #28956 involved simplifying and refactoring the network-adjusted time calculation logic, but this was eventually left out of the PR to make it easier for reviewers to focus on consensus logic changes.
Since network-adjusted time is now only used for warning/informational purposes, cleaning up the logic (building on @dergoegge's approach in #28956) should be quite straightforward and uncontroversial. The main changes are:
2010 minutes (which is an arbitrary number).-maxtimeadjustment
startup argCloses #4521