Skip to content

Commit 08db539

Browse files
authored
test(peerstore): Address TTL 1 (#2662)
1 parent bd9e168 commit 08db539

1 file changed

Lines changed: 173 additions & 0 deletions

File tree

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# SPDX-License-Identifier: Apache-2.0 OR MIT
2+
# Copyright (c) Status Research & Development GmbH
3+
4+
{.used.}
5+
6+
import std/[sequtils, tables]
7+
import chronos
8+
import ../../libp2p/[peerstore, peerinfo, multiaddress, switch, protocols/kademlia]
9+
import ../tools/[unittest, multiaddress, switch_builder, lifecycle, topology]
10+
import ./kademlia/utils
11+
12+
suite "PeerStore Address TTL - Component":
13+
teardown:
14+
checkTrackers()
15+
16+
asyncTest "identify stores peer addresses at Medium confidence":
17+
let
18+
listener = makeStandardSwitch(TcpAutoAddress)
19+
dialer = makeStandardSwitch(TcpAutoAddress)
20+
startAndDeferStop(@[listener, dialer])
21+
22+
await dialer.connect(listener.peerInfo.peerId, listener.peerInfo.addrs)
23+
24+
# The listener only accepts the connection, so identify is its only source for the dialer's addresses.
25+
# The listener never dials the dialer, so nothing upgrades it to High.
26+
checkUntilTimeout:
27+
listener.peerStore[AddressBook].entries(dialer.peerInfo.peerId).len > 0
28+
listener.peerStore[AddressBook].entries(dialer.peerInfo.peerId).allIt(
29+
it.confidence == AddressConfidence.Medium
30+
)
31+
32+
asyncTest "successful dial records address at High confidence":
33+
let
34+
listener = makeStandardSwitch(TcpAutoAddress)
35+
dialer = makeStandardSwitch(TcpAutoAddress)
36+
startAndDeferStop(@[listener, dialer])
37+
38+
await dialer.connect(listener.peerInfo.peerId, listener.peerInfo.addrs)
39+
40+
# A successful dial marks the dialed address High.
41+
checkUntilTimeout:
42+
dialer.peerStore[AddressBook].entries(listener.peerInfo.peerId).anyIt(
43+
it.confidence == AddressConfidence.High
44+
)
45+
46+
asyncTest "kademlia discovery stores discovered addresses at Low confidence":
47+
# kads[0] is a hub that knows both others.
48+
# kads[1] and kads[2] are not directly connected, so kads[1] can only learn
49+
# kads[2] through a DHT lookup.
50+
let kads = setupKadSwitches(3)
51+
startAndDeferStop(kads)
52+
await connectHub(kads[0], kads[1 ..^ 1])
53+
54+
check kads[2].switch.peerInfo.peerId notin kads[1].switch.peerStore[AddressBook]
55+
56+
# Stop kads[2] so the lookup cannot dial it.
57+
await kads[2].switch.stop()
58+
59+
# kads[1] discovers kads[2] through kads[0]'s FIND_NODE response.
60+
discard await kads[1].findNode(kads[2].rtable.selfId)
61+
62+
# The addresses arrive in a DHT response, so they are stored at Low confidence.
63+
let entries =
64+
kads[1].switch.peerStore[AddressBook].entries(kads[2].switch.peerInfo.peerId)
65+
check:
66+
entries.len == 1
67+
entries[0].confidence == AddressConfidence.Low
68+
69+
asyncTest "later identify update does not downgrade a verified High address":
70+
let
71+
listener = makeStandardSwitch(TcpAutoAddress)
72+
dialer = makeStandardSwitch(TcpAutoAddress)
73+
startAndDeferStop(@[listener, dialer])
74+
75+
await dialer.connect(listener.peerInfo.peerId, listener.peerInfo.addrs)
76+
77+
# Wait for the dialed address to reach High before the update.
78+
checkUntilTimeout:
79+
dialer.peerStore[AddressBook].entries(listener.peerInfo.peerId).anyIt(
80+
it.confidence == AddressConfidence.High
81+
)
82+
83+
# The listener announces an extra address.
84+
# Its IdentifyPusher pushes the updated PeerInfo to the dialer at Medium.
85+
let extra = ma("/ip4/127.0.0.1/tcp/4099")
86+
listener.peerInfo.listenAddrs.add(extra)
87+
await listener.peerInfo.update()
88+
89+
# The extra address is stored at Medium.
90+
# The already dialed address stays High.
91+
checkUntilTimeout:
92+
dialer.peerStore[AddressBook].entries(listener.peerInfo.peerId).anyIt(
93+
it.address == extra and it.confidence == AddressConfidence.Medium
94+
)
95+
dialer.peerStore[AddressBook].entries(listener.peerInfo.peerId).anyIt(
96+
it.confidence == AddressConfidence.High
97+
)
98+
99+
asyncTest "addresses of every confidence are pruned after their TTL":
100+
let switch = makeStandardSwitchBuilder(TcpAutoAddress)
101+
.withAddressConfidenceTtls(
102+
AddressConfidenceTtls(
103+
low: 10.milliseconds, medium: 20.milliseconds, high: 30.milliseconds
104+
)
105+
)
106+
.build()
107+
startAndDeferStop(@[switch])
108+
109+
let
110+
lowPeer = randomPeerId()
111+
mediumPeer = randomPeerId()
112+
highPeer = randomPeerId()
113+
switch.peerStore[AddressBook].extend(
114+
lowPeer, @[ma("/ip4/1.2.3.4/tcp/4001")], AddressConfidence.Low
115+
)
116+
switch.peerStore[AddressBook].extend(
117+
mediumPeer, @[ma("/ip4/5.6.7.8/tcp/4001")], AddressConfidence.Medium
118+
)
119+
switch.peerStore[AddressBook].extend(
120+
highPeer, @[ma("/ip4/9.10.11.12/tcp/4001")], AddressConfidence.High
121+
)
122+
check:
123+
lowPeer in switch.peerStore[AddressBook]
124+
mediumPeer in switch.peerStore[AddressBook]
125+
highPeer in switch.peerStore[AddressBook]
126+
127+
# The background pruning loop applies each entry's own TTL.
128+
# Asserting on the raw entries proves the loop ran, not just lazy filtering.
129+
checkUntilTimeout:
130+
switch.peerStore[AddressBook].entries(lowPeer).len == 0
131+
switch.peerStore[AddressBook].entries(mediumPeer).len == 0
132+
switch.peerStore[AddressBook].entries(highPeer).len == 0
133+
134+
asyncTest "non-expiring addresses survive the pruning loop":
135+
# A zero TTL disables expiry for that confidence band.
136+
# Infinite confidence never expires regardless of the configured TTLs.
137+
# The High TTL is intentionally short so the control entry expires quickly.
138+
let switch = makeStandardSwitchBuilder(TcpAutoAddress)
139+
.withAddressConfidenceTtls(
140+
AddressConfidenceTtls(
141+
low: 0.seconds, medium: 10.milliseconds, high: 20.milliseconds
142+
)
143+
)
144+
.build()
145+
startAndDeferStop(@[switch])
146+
147+
let
148+
zeroTtlPeer = randomPeerId()
149+
infinitePeer = randomPeerId()
150+
controlPeer = randomPeerId()
151+
switch.peerStore[AddressBook].extend(
152+
zeroTtlPeer, @[ma("/ip4/1.2.3.4/tcp/4001")], AddressConfidence.Low
153+
)
154+
switch.peerStore[AddressBook].extend(
155+
infinitePeer, @[ma("/ip4/5.6.7.8/tcp/4001")], AddressConfidence.Infinite
156+
)
157+
switch.peerStore[AddressBook].extend(
158+
controlPeer, @[ma("/ip4/9.10.11.12/tcp/4001")], AddressConfidence.High
159+
)
160+
# Back-date the non-expiring entries well beyond any positive TTL.
161+
switch.peerStore[AddressBook].book[zeroTtlPeer][0].lastUpdated =
162+
Moment.now() - 48.hours
163+
switch.peerStore[AddressBook].book[infinitePeer][0].lastUpdated =
164+
Moment.now() - 48.hours
165+
166+
checkUntilTimeout:
167+
# The control was pruned, proving the loop actively expires entries.
168+
switch.peerStore[AddressBook].entries(controlPeer).len == 0
169+
170+
check:
171+
# The non-expiring entries were spared.
172+
switch.peerStore[AddressBook].entries(zeroTtlPeer).len == 1
173+
switch.peerStore[AddressBook].entries(infinitePeer).len == 1

0 commit comments

Comments
 (0)