Skip to content

Commit 4441386

Browse files
ziggie1984Roasbeef
authored andcommitted
multi: fix time.Time initialization.
ChanUpdate timestamps are now restircted so that they cannot be more than two weeks into the future. Moreover channels with both timestamps in the ReplyChannelRange msg either too far in the past or too far in the future are not queried. Moreover fix unitests.
1 parent b23e69c commit 4441386

File tree

5 files changed

+208
-38
lines changed

5 files changed

+208
-38
lines changed

channeldb/graph.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,29 @@ type ChannelUpdateInfo struct {
22112211
Node2UpdateTimestamp time.Time
22122212
}
22132213

2214+
// NewChannelUpdateInfo is a constructor which makes sure we initialize the
2215+
// timestamps with zero seconds unix timestamp which equals
2216+
// `January 1, 1970, 00:00:00 UTC` in case the value is `time.Time{}`.
2217+
func NewChannelUpdateInfo(scid lnwire.ShortChannelID, node1Timestamp,
2218+
node2Timestamp time.Time) ChannelUpdateInfo {
2219+
2220+
chanInfo := ChannelUpdateInfo{
2221+
ShortChannelID: scid,
2222+
Node1UpdateTimestamp: node1Timestamp,
2223+
Node2UpdateTimestamp: node2Timestamp,
2224+
}
2225+
2226+
if node1Timestamp.IsZero() {
2227+
chanInfo.Node1UpdateTimestamp = time.Unix(0, 0)
2228+
}
2229+
2230+
if node2Timestamp.IsZero() {
2231+
chanInfo.Node2UpdateTimestamp = time.Unix(0, 0)
2232+
}
2233+
2234+
return chanInfo
2235+
}
2236+
22142237
// BlockChannelRange represents a range of channels for a given block height.
22152238
type BlockChannelRange struct {
22162239
// Height is the height of the block all of the channels below were
@@ -2284,9 +2307,9 @@ func (c *ChannelGraph) FilterChannelRange(startHeight,
22842307
rawCid := byteOrder.Uint64(k)
22852308
cid := lnwire.NewShortChanIDFromInt(rawCid)
22862309

2287-
chanInfo := ChannelUpdateInfo{
2288-
ShortChannelID: cid,
2289-
}
2310+
chanInfo := NewChannelUpdateInfo(
2311+
cid, time.Time{}, time.Time{},
2312+
)
22902313

22912314
if !withTimestamps {
22922315
channelsPerBlock[cid.BlockHeight] = append(

channeldb/graph_test.go

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,9 +1980,9 @@ func TestFilterKnownChanIDs(t *testing.T) {
19801980
t.Fatalf("unable to create channel edge: %v", err)
19811981
}
19821982

1983-
chanIDs = append(chanIDs, ChannelUpdateInfo{
1984-
ShortChannelID: chanID,
1985-
})
1983+
chanIDs = append(chanIDs, NewChannelUpdateInfo(
1984+
chanID, time.Time{}, time.Time{},
1985+
))
19861986
}
19871987

19881988
const numZombies = 5
@@ -2024,20 +2024,28 @@ func TestFilterKnownChanIDs(t *testing.T) {
20242024
// should get the same set back.
20252025
{
20262026
queryIDs: []ChannelUpdateInfo{
2027-
{ShortChannelID: lnwire.ShortChannelID{
2028-
BlockHeight: 99,
2029-
}},
2030-
{ShortChannelID: lnwire.ShortChannelID{
2031-
BlockHeight: 100,
2032-
}},
2027+
{
2028+
ShortChannelID: lnwire.ShortChannelID{
2029+
BlockHeight: 99,
2030+
},
2031+
},
2032+
{
2033+
ShortChannelID: lnwire.ShortChannelID{
2034+
BlockHeight: 100,
2035+
},
2036+
},
20332037
},
20342038
resp: []ChannelUpdateInfo{
2035-
{ShortChannelID: lnwire.ShortChannelID{
2036-
BlockHeight: 99,
2037-
}},
2038-
{ShortChannelID: lnwire.ShortChannelID{
2039-
BlockHeight: 100,
2040-
}},
2039+
{
2040+
ShortChannelID: lnwire.ShortChannelID{
2041+
BlockHeight: 99,
2042+
},
2043+
},
2044+
{
2045+
ShortChannelID: lnwire.ShortChannelID{
2046+
BlockHeight: 100,
2047+
},
2048+
},
20412049
},
20422050
},
20432051

@@ -2419,7 +2427,7 @@ func TestFilterChannelRange(t *testing.T) {
24192427
)
24202428
)
24212429

2422-
updateTimeSeed := int64(1)
2430+
updateTimeSeed := time.Now().Unix()
24232431
maybeAddPolicy := func(chanID uint64, node *LightningNode,
24242432
node2 bool) time.Time {
24252433

@@ -2428,7 +2436,7 @@ func TestFilterChannelRange(t *testing.T) {
24282436
chanFlags = lnwire.ChanUpdateDirection
24292437
}
24302438

2431-
var updateTime time.Time
2439+
var updateTime = time.Unix(0, 0)
24322440
if rand.Int31n(2) == 0 {
24332441
updateTime = time.Unix(updateTimeSeed, 0)
24342442
err = graph.UpdateEdgePolicy(&models.ChannelEdgePolicy{
@@ -2456,11 +2464,16 @@ func TestFilterChannelRange(t *testing.T) {
24562464
)
24572465
require.NoError(t, graph.AddChannelEdge(&channel2))
24582466

2467+
chanInfo1 := NewChannelUpdateInfo(
2468+
chanID1, time.Time{}, time.Time{},
2469+
)
2470+
chanInfo2 := NewChannelUpdateInfo(
2471+
chanID2, time.Time{}, time.Time{},
2472+
)
24592473
channelRanges = append(channelRanges, BlockChannelRange{
24602474
Height: chanHeight,
24612475
Channels: []ChannelUpdateInfo{
2462-
{ShortChannelID: chanID1},
2463-
{ShortChannelID: chanID2},
2476+
chanInfo1, chanInfo2,
24642477
},
24652478
})
24662479

@@ -2471,20 +2484,17 @@ func TestFilterChannelRange(t *testing.T) {
24712484
time4 = maybeAddPolicy(channel2.ChannelID, node2, true)
24722485
)
24732486

2487+
chanInfo1 = NewChannelUpdateInfo(
2488+
chanID1, time1, time2,
2489+
)
2490+
chanInfo2 = NewChannelUpdateInfo(
2491+
chanID2, time3, time4,
2492+
)
24742493
channelRangesWithTimestamps = append(
24752494
channelRangesWithTimestamps, BlockChannelRange{
24762495
Height: chanHeight,
24772496
Channels: []ChannelUpdateInfo{
2478-
{
2479-
ShortChannelID: chanID1,
2480-
Node1UpdateTimestamp: time1,
2481-
Node2UpdateTimestamp: time2,
2482-
},
2483-
{
2484-
ShortChannelID: chanID2,
2485-
Node1UpdateTimestamp: time3,
2486-
Node2UpdateTimestamp: time4,
2487-
},
2497+
chanInfo1, chanInfo2,
24882498
},
24892499
},
24902500
)

discovery/gossiper.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,6 +2745,22 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg,
27452745
return nil, true
27462746
}
27472747

2748+
// Check that the ChanUpdate is not too far into the future, this could
2749+
// reveal some faulty implementation therefore we log an error.
2750+
if time.Until(timestamp) > graph.DefaultChannelPruneExpiry {
2751+
log.Errorf("Skewed timestamp (%v) for edge policy of "+
2752+
"short_chan_id(%v), timestamp too far in the future: "+
2753+
"peer=%v, msg=%s, is_remote=%v", timestamp.Unix(),
2754+
shortChanID, nMsg.peer, nMsg.msg.MsgType(),
2755+
nMsg.isRemote,
2756+
)
2757+
2758+
nMsg.err <- fmt.Errorf("skewed timestamp of edge policy, "+
2759+
"timestamp too far in the future: %v", timestamp.Unix())
2760+
2761+
return nil, false
2762+
}
2763+
27482764
// Get the node pub key as far since we don't have it in the channel
27492765
// update announcement message. We'll need this to properly verify the
27502766
// message's signature.

discovery/syncer.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/btcsuite/btcd/chaincfg/chainhash"
1414
"github.com/lightningnetwork/lnd/channeldb"
15+
"github.com/lightningnetwork/lnd/graph"
1516
"github.com/lightningnetwork/lnd/lnpeer"
1617
"github.com/lightningnetwork/lnd/lnwire"
1718
"golang.org/x/time/rate"
@@ -787,6 +788,16 @@ func isLegacyReplyChannelRange(query *lnwire.QueryChannelRange,
787788
// reply to the initial range query to discover new channels that it didn't
788789
// previously know of.
789790
func (g *GossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) error {
791+
// isStale returns whether the timestamp is too far into the past.
792+
isStale := func(timestamp time.Time) bool {
793+
return time.Since(timestamp) > graph.DefaultChannelPruneExpiry
794+
}
795+
796+
// isSkewed returns whether the timestamp is too far into the future.
797+
isSkewed := func(timestamp time.Time) bool {
798+
return time.Until(timestamp) > graph.DefaultChannelPruneExpiry
799+
}
800+
790801
// If we're not communicating with a legacy node, we'll apply some
791802
// further constraints on their reply to ensure it satisfies our query.
792803
if !isLegacyReplyChannelRange(g.curQueryRangeMsg, msg) {
@@ -838,16 +849,42 @@ func (g *GossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) erro
838849
}
839850

840851
for i, scid := range msg.ShortChanIDs {
841-
info := channeldb.ChannelUpdateInfo{
842-
ShortChannelID: scid,
843-
}
852+
info := channeldb.NewChannelUpdateInfo(
853+
scid, time.Time{}, time.Time{},
854+
)
844855

845856
if len(msg.Timestamps) != 0 {
846857
t1 := time.Unix(int64(msg.Timestamps[i].Timestamp1), 0)
847858
info.Node1UpdateTimestamp = t1
848859

849860
t2 := time.Unix(int64(msg.Timestamps[i].Timestamp2), 0)
850861
info.Node2UpdateTimestamp = t2
862+
863+
// Sort out all channels with outdated or skewed
864+
// timestamps. Both timestamps need to be out of
865+
// boundaries for us to skip the channel and not query
866+
// it later on.
867+
switch {
868+
case isStale(info.Node1UpdateTimestamp) &&
869+
isStale(info.Node2UpdateTimestamp):
870+
871+
continue
872+
873+
case isSkewed(info.Node1UpdateTimestamp) &&
874+
isSkewed(info.Node2UpdateTimestamp):
875+
876+
continue
877+
878+
case isStale(info.Node1UpdateTimestamp) &&
879+
isSkewed(info.Node2UpdateTimestamp):
880+
881+
continue
882+
883+
case isStale(info.Node2UpdateTimestamp) &&
884+
isSkewed(info.Node1UpdateTimestamp):
885+
886+
continue
887+
}
851888
}
852889

853890
g.bufferedChanRangeReplies = append(

discovery/syncer_test.go

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,12 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
12291229
query, err := syncer.genChanRangeQuery(true)
12301230
require.NoError(t, err, "unable to generate channel range query")
12311231

1232+
currentTimestamp := time.Now().Unix()
1233+
// Timestamp more than 2 weeks in the past hence expired.
1234+
expiredTimestamp := time.Unix(0, 0).Unix()
1235+
// Timestamp three weeks in the future.
1236+
skewedTimestamp := time.Now().Add(time.Hour * 24 * 18).Unix()
1237+
12321238
// When interpreting block ranges, the first reply should start from
12331239
// our requested first block, and the last should end at our requested
12341240
// last block.
@@ -1253,14 +1259,78 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
12531259
},
12541260
{
12551261
FirstBlockHeight: 12,
1256-
NumBlocks: query.NumBlocks - 12,
1257-
Complete: 1,
1262+
NumBlocks: 1,
12581263
ShortChanIDs: []lnwire.ShortChannelID{
12591264
{
12601265
BlockHeight: 12,
12611266
},
12621267
},
12631268
},
1269+
{
1270+
FirstBlockHeight: 13,
1271+
NumBlocks: query.NumBlocks - 13,
1272+
Complete: 1,
1273+
ShortChanIDs: []lnwire.ShortChannelID{
1274+
{
1275+
BlockHeight: 13,
1276+
TxIndex: 1,
1277+
},
1278+
{
1279+
BlockHeight: 13,
1280+
TxIndex: 2,
1281+
},
1282+
{
1283+
BlockHeight: 13,
1284+
TxIndex: 3,
1285+
},
1286+
{
1287+
BlockHeight: 13,
1288+
TxIndex: 4,
1289+
},
1290+
{
1291+
BlockHeight: 13,
1292+
TxIndex: 5,
1293+
},
1294+
{
1295+
BlockHeight: 13,
1296+
TxIndex: 6,
1297+
},
1298+
},
1299+
Timestamps: []lnwire.ChanUpdateTimestamps{
1300+
{
1301+
// Both timestamps are valid.
1302+
Timestamp1: uint32(currentTimestamp),
1303+
Timestamp2: uint32(currentTimestamp),
1304+
},
1305+
{
1306+
// One of the timestamps is valid.
1307+
Timestamp1: uint32(currentTimestamp),
1308+
Timestamp2: uint32(expiredTimestamp),
1309+
},
1310+
{
1311+
// Both timestamps are expired.
1312+
Timestamp1: uint32(expiredTimestamp),
1313+
Timestamp2: uint32(expiredTimestamp),
1314+
},
1315+
{
1316+
// Both timestamps are skewed.
1317+
Timestamp1: uint32(skewedTimestamp),
1318+
Timestamp2: uint32(skewedTimestamp),
1319+
},
1320+
{
1321+
// One timestamp is skewed the other
1322+
// expired.
1323+
Timestamp1: uint32(expiredTimestamp),
1324+
Timestamp2: uint32(skewedTimestamp),
1325+
},
1326+
{
1327+
// One timestamp is skewed the other
1328+
// expired.
1329+
Timestamp1: uint32(skewedTimestamp),
1330+
Timestamp2: uint32(expiredTimestamp),
1331+
},
1332+
},
1333+
},
12641334
}
12651335

12661336
// Each reply query is the same as the original query in the legacy
@@ -1274,6 +1344,9 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
12741344

12751345
replies[2].FirstBlockHeight = query.FirstBlockHeight
12761346
replies[2].NumBlocks = query.NumBlocks
1347+
1348+
replies[3].FirstBlockHeight = query.FirstBlockHeight
1349+
replies[3].NumBlocks = query.NumBlocks
12771350
}
12781351

12791352
// We'll begin by sending the syncer a set of non-complete channel
@@ -1284,6 +1357,9 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
12841357
if err := syncer.processChanRangeReply(replies[1]); err != nil {
12851358
t.Fatalf("unable to process reply: %v", err)
12861359
}
1360+
if err := syncer.processChanRangeReply(replies[2]); err != nil {
1361+
t.Fatalf("unable to process reply: %v", err)
1362+
}
12871363

12881364
// At this point, we should still be in our starting state as the query
12891365
// hasn't finished.
@@ -1301,6 +1377,14 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
13011377
{
13021378
BlockHeight: 12,
13031379
},
1380+
{
1381+
BlockHeight: 13,
1382+
TxIndex: 1,
1383+
},
1384+
{
1385+
BlockHeight: 13,
1386+
TxIndex: 2,
1387+
},
13041388
}
13051389

13061390
// As we're about to send the final response, we'll launch a goroutine
@@ -1335,7 +1419,7 @@ func testGossipSyncerProcessChanRangeReply(t *testing.T, legacy bool) {
13351419

13361420
// If we send the final message, then we should transition to
13371421
// queryNewChannels as we've sent a non-empty set of new channels.
1338-
if err := syncer.processChanRangeReply(replies[2]); err != nil {
1422+
if err := syncer.processChanRangeReply(replies[3]); err != nil {
13391423
t.Fatalf("unable to process reply: %v", err)
13401424
}
13411425

0 commit comments

Comments
 (0)