Skip to content

Commit 7e7e3d2

Browse files
committed
server: fix timestamp comparison in setSelfNode
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.
1 parent 9fcd7c2 commit 7e7e3d2

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

server.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5556,6 +5556,16 @@ func (s *server) AttemptRBFCloseUpdate(ctx context.Context,
55565556
return updates, nil
55575557
}
55585558

5559+
// needsTimestampIncrement determines whether we need to increment the
5560+
// timestamp for a node announcement to ensure it's at least one second after
5561+
// the previously persisted timestamp. We compare at second precision to match
5562+
// how timestamps are stored in the database (both SQL and KV stores truncate
5563+
// to second precision). This ensures BOLT-07 compliance, which requires node
5564+
// announcements to have strictly increasing timestamps.
5565+
func needsTimestampIncrement(persistedTime, currentTime time.Time) bool {
5566+
return persistedTime.Unix() >= currentTime.Unix()
5567+
}
5568+
55595569
// setSelfNode configures and sets the server's self node. It sets the node
55605570
// announcement, signs it, and updates the source node in the graph. When
55615571
// determining values such as color and alias, the method prioritizes values
@@ -5623,7 +5633,7 @@ func (s *server) setSelfNode(ctx context.Context, nodePub route.Vertex,
56235633
// If we have a source node persisted in the DB already, then we
56245634
// just need to make sure that the new LastUpdate time is at
56255635
// least one second after the last update time.
5626-
if srcNode.LastUpdate.Second() >= nodeLastUpdate.Second() {
5636+
if needsTimestampIncrement(srcNode.LastUpdate, nodeLastUpdate) {
56275637
nodeLastUpdate = srcNode.LastUpdate.Add(time.Second)
56285638
}
56295639

server_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package lnd
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
// TestNodeAnnouncementTimestampComparison tests the timestamp comparison
11+
// logic used in setSelfNode to ensure node announcements have strictly
12+
// increasing timestamps at second precision (as required by BOLT-07 and
13+
// enforced by the database storage).
14+
func TestNodeAnnouncementTimestampComparison(t *testing.T) {
15+
t.Parallel()
16+
17+
baseTime := int64(1700000000)
18+
19+
tests := []struct {
20+
name string
21+
srcNodeLastUpdate time.Time
22+
nodeLastUpdate time.Time
23+
shouldIncrement bool
24+
description string
25+
}{
26+
{
27+
name: "same second different nanoseconds",
28+
srcNodeLastUpdate: time.Unix(baseTime, 0),
29+
nodeLastUpdate: time.Unix(baseTime, 500_000_000),
30+
shouldIncrement: true,
31+
description: "Edge case: timestamps in same second " +
32+
"but different nanoseconds. Must increment " +
33+
"to avoid persisting same second-level " +
34+
"timestamp.",
35+
},
36+
{
37+
name: "different seconds",
38+
srcNodeLastUpdate: time.Unix(baseTime, 0),
39+
nodeLastUpdate: time.Unix(baseTime+2, 0),
40+
shouldIncrement: false,
41+
description: "Normal case: current time is already " +
42+
"in a different (later) second. No increment " +
43+
"needed.",
44+
},
45+
{
46+
name: "exactly equal",
47+
srcNodeLastUpdate: time.Unix(baseTime, 123456789),
48+
nodeLastUpdate: time.Unix(baseTime, 123456789),
49+
shouldIncrement: true,
50+
description: "Timestamps are identical. Must " +
51+
"increment to ensure strictly greater " +
52+
"timestamp.",
53+
},
54+
{
55+
name: "clock skew - persisted is newer",
56+
srcNodeLastUpdate: time.Unix(baseTime+5, 0),
57+
nodeLastUpdate: time.Unix(baseTime+3, 0),
58+
shouldIncrement: true,
59+
description: "Clock went backwards: persisted " +
60+
"timestamp is newer than current time. Must " +
61+
"increment from persisted timestamp.",
62+
},
63+
{
64+
name: "clock skew - same second",
65+
srcNodeLastUpdate: time.Unix(baseTime+5, 100_000_000),
66+
nodeLastUpdate: time.Unix(baseTime+5, 900_000_000),
67+
shouldIncrement: true,
68+
description: "Clock skew within same second. Must " +
69+
"increment to ensure strictly greater " +
70+
"second-level timestamp.",
71+
},
72+
}
73+
74+
for _, tc := range tests {
75+
t.Run(tc.name, func(t *testing.T) {
76+
t.Parallel()
77+
78+
shouldIncrement := needsTimestampIncrement(
79+
tc.srcNodeLastUpdate,
80+
tc.nodeLastUpdate,
81+
)
82+
83+
require.Equal(
84+
t, tc.shouldIncrement, shouldIncrement,
85+
"needsTimestampIncrement returned unexpected "+
86+
"result: %s", tc.description,
87+
)
88+
})
89+
}
90+
}

0 commit comments

Comments
 (0)