-
Notifications
You must be signed in to change notification settings - Fork 2.2k
server: fix timestamp comparison in setSelfNode #10449
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
server: fix timestamp comparison in setSelfNode #10449
Conversation
Summary of ChangesHello @ziggie1984, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical bug within the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request addresses a critical bug in timestamp comparison within the setSelfNode function. The original code incorrectly compared only the seconds component of timestamps, which could lead to using an older timestamp and causing errors on restart. The fix correctly uses time.Time.Before for a full timestamp comparison, which is the proper and idiomatic approach in Go. This change is accurate and effectively resolves the described issue. The code quality is good, and I have no further suggestions.
ellemouton
left a 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.
nice catch 🙏
|
test flake is already tracked here: #8460 |
starius
left a 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.
I think there is an issue in the PR.
Also I propose to document when the bug was introduced, commit 75895db ("lnd: use saved node ann config from previous run", 2024-06-09).
Also I propose to cover this logic with regression tests as it proved to be fragile.
server.go
Outdated
| // just need to make sure that the new LastUpdate time is at | ||
| // least one second after the last update time. | ||
| if srcNode.LastUpdate.Second() >= nodeLastUpdate.Second() { | ||
| if !srcNode.LastUpdate.Before(nodeLastUpdate) { |
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.
What if srcNode.LastUpdate.Before(nodeLastUpdate) but at the same time srcNode.LastUpdate.Unix() == nodeLastUpdate.Unix() (i.e. they fall into the same second)?
IMHO in this case get a violation of the "at least one second after" requirement of the comment above and [BOLT‑07 (P2P Node and Channel Discovery)]:
MUST set timestamp to be greater than that of any previous node_announcement it has previously created
IMHO the proper fix is:
if srcNode.LastUpdate.Unix() >= nodeLastUpdate.Unix() {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.
@gemini-code-assist can you explain what the problem is with the current fix ?
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.
The problem with the current fix, !srcNode.LastUpdate.Before(nodeLastUpdate), as pointed out by @starius, lies in how time.Time.Before() handles sub-second precision compared to the stated requirement of being "at least one second after".
The code comment explicitly states: "just need to make sure that the new LastUpdate time is at least one second after the last update time."
Consider the following scenario:
srcNode.LastUpdate(the existing timestamp in the DB) =2025-12-15 19:00:00.500000000 UTCnodeLastUpdate(the new proposed timestamp) =2025-12-15 19:00:00.800000000 UTC
With the current fix:
srcNode.LastUpdate.Before(nodeLastUpdate)evaluates totrue(0.5s is indeed before 0.8s).- Therefore, the condition
!srcNode.LastUpdate.Before(nodeLastUpdate)evaluates tofalse. - The
ifblock is skipped, andnodeLastUpdateremains2025-12-15 19:00:00.800000000 UTC.
However, 2025-12-15 19:00:00.800000000 UTC is only 0.3 seconds after srcNode.LastUpdate, which violates the requirement of being "at least one second after".
@starius's suggestion, srcNode.LastUpdate.Unix() >= nodeLastUpdate.Unix(), addresses this by comparing the timestamps at second-level granularity. Let's re-evaluate with the same example:
srcNode.LastUpdate.Unix()(seconds part of 19:00:00.5s) =1765834800nodeLastUpdate.Unix()(seconds part of 19:00:00.8s) =1765834800
With @starius's suggested fix:
- The condition
srcNode.LastUpdate.Unix() >= nodeLastUpdate.Unix()evaluates to1765834800 >= 1765834800, which istrue. - The
ifblock would execute:nodeLastUpdate = srcNode.LastUpdate.Add(time.Second). nodeLastUpdatewould then become2025-12-15 19:00:01.500000000 UTC.
This new nodeLastUpdate (19:00:01.5s) is correctly "at least one second after" srcNode.LastUpdate (19:00:00.5s), aligning with the stated intent. This approach also correctly handles the original issue described in the PR where only the .Second() component was compared.
Therefore, @starius's suggestion provides a more robust solution that adheres to the "at least one second after" logic, considering the practical granularity of such timestamp comparisons.
| if !srcNode.LastUpdate.Before(nodeLastUpdate) { | |
| if srcNode.LastUpdate.Unix() >= nodeLastUpdate.Unix() { |
0a67307 to
90f4999
Compare
|
/gemini review |
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.
Code Review
This pull request correctly fixes a bug in setSelfNode where an incorrect timestamp comparison could lead to startup failures. The change replaces the flawed comparison of only the seconds component with a full Unix timestamp comparison by introducing a new helper function, needsTimestampIncrement. This approach is clean and improves code clarity. The accompanying new test, TestNodeAnnouncementTimestampComparison, is thorough, covering a good range of edge cases including clock skew, which ensures the fix is robust. The release note is also clear and accurately describes the change. Overall, this is a high-quality contribution.
server.go
Outdated
| // how timestamps are stored in the database (both SQL and KV stores truncate | ||
| // to second precision). This ensures BOLT-07 compliance, which requires node | ||
| // announcements to have strictly increasing timestamps. | ||
| func needsTimestampIncrement(persistedTime, currentTime time.Time) bool { |
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.
move this in a separate function so that I can test it as well.
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 propose to include the if into the function. So it returns the final nodeLastUpdate value.
starius
left a 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.
LGTM! 🏆
I left few comments with refactoring and tests proposals.
server.go
Outdated
| // how timestamps are stored in the database (both SQL and KV stores truncate | ||
| // to second precision). This ensures BOLT-07 compliance, which requires node | ||
| // announcements to have strictly increasing timestamps. | ||
| func needsTimestampIncrement(persistedTime, currentTime time.Time) bool { |
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 propose to include the if into the function. So it returns the final nodeLastUpdate value.
Fix bug where setSelfNode compared only the seconds component of timestamps instead of the full timestamp. This caused the node to attempt persisting an older timestamp than what existed in the database during restart, resulting in "sql: no rows in result set" errors.
90f4999 to
e5b0704
Compare
|
Successfully created backport PR for |
Fix bug where setSelfNode compared only the seconds component of
timestamps instead of the full timestamp. This caused the node to
attempt persisting an older timestamp than what existed in the
database during restart, resulting in "sql: no rows in result set"
errors.
Fixes a flake:
https://github.com/lightningnetwork/lnd/actions/runs/20183761355/job/57949717763
The issue occurred when:
evaluated 30 >= 2, causing nodeLastUpdate to be set to 10:00:31