libp2p_swarm/behaviour/
external_addresses.rs1use libp2p_core::Multiaddr;
2
3use crate::behaviour::{ExternalAddrConfirmed, ExternalAddrExpired, FromSwarm};
4
5const MAX_LOCAL_EXTERNAL_ADDRS: usize = 20;
9
10#[derive(Debug, Clone, Default)]
12pub struct ExternalAddresses {
13 addresses: Vec<Multiaddr>,
14}
15
16impl ExternalAddresses {
17 pub fn iter(&self) -> impl ExactSizeIterator<Item = &Multiaddr> {
19 self.addresses.iter()
20 }
21
22 pub fn as_slice(&self) -> &[Multiaddr] {
23 self.addresses.as_slice()
24 }
25
26 pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool {
30 match event {
31 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => {
32 if let Some(pos) = self
33 .addresses
34 .iter()
35 .position(|candidate| candidate == *addr)
36 {
37 self.addresses.remove(pos);
39 self.push_front(addr);
40
41 tracing::debug!(address=%addr, "Refreshed external address");
42
43 return false; }
45
46 self.push_front(addr);
47
48 if self.addresses.len() > MAX_LOCAL_EXTERNAL_ADDRS {
49 let expired = self.addresses.pop().expect("list to be not empty");
50
51 tracing::debug!(
52 external_address=%expired,
53 address_limit=%MAX_LOCAL_EXTERNAL_ADDRS,
54 "Removing previously confirmed external address because we reached the address limit"
55 );
56 }
57
58 return true;
59 }
60 FromSwarm::ExternalAddrExpired(ExternalAddrExpired {
61 addr: expired_addr, ..
62 }) => {
63 let pos = match self
64 .addresses
65 .iter()
66 .position(|candidate| candidate == *expired_addr)
67 {
68 None => return false,
69 Some(p) => p,
70 };
71
72 self.addresses.remove(pos);
73 return true;
74 }
75 _ => {}
76 }
77
78 false
79 }
80
81 fn push_front(&mut self, addr: &Multiaddr) {
82 self.addresses.insert(0, addr.clone());
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use libp2p_core::multiaddr::Protocol;
91 use once_cell::sync::Lazy;
92 use rand::Rng;
93
94 use super::*;
95
96 #[test]
97 fn new_external_addr_returns_correct_changed_value() {
98 let mut addresses = ExternalAddresses::default();
99
100 let changed = addresses.on_swarm_event(&new_external_addr1());
101 assert!(changed);
102
103 let changed = addresses.on_swarm_event(&new_external_addr1());
104 assert!(!changed)
105 }
106
107 #[test]
108 fn expired_external_addr_returns_correct_changed_value() {
109 let mut addresses = ExternalAddresses::default();
110 addresses.on_swarm_event(&new_external_addr1());
111
112 let changed = addresses.on_swarm_event(&expired_external_addr1());
113 assert!(changed);
114
115 let changed = addresses.on_swarm_event(&expired_external_addr1());
116 assert!(!changed)
117 }
118
119 #[test]
120 fn more_recent_external_addresses_are_prioritized() {
121 let mut addresses = ExternalAddresses::default();
122
123 addresses.on_swarm_event(&new_external_addr1());
124 addresses.on_swarm_event(&new_external_addr2());
125
126 assert_eq!(
127 addresses.as_slice(),
128 &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]
129 );
130 }
131
132 #[test]
133 fn when_pushing_more_than_max_addresses_oldest_is_evicted() {
134 let mut addresses = ExternalAddresses::default();
135
136 while addresses.as_slice().len() < MAX_LOCAL_EXTERNAL_ADDRS {
137 let random_address =
138 Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000)));
139 addresses.on_swarm_event(&FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
140 addr: &random_address,
141 }));
142 }
143
144 addresses.on_swarm_event(&new_external_addr2());
145
146 assert_eq!(addresses.as_slice().len(), 20);
147 assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone());
148 }
149
150 #[test]
151 fn reporting_existing_external_address_moves_it_to_the_front() {
152 let mut addresses = ExternalAddresses::default();
153
154 addresses.on_swarm_event(&new_external_addr1());
155 addresses.on_swarm_event(&new_external_addr2());
156 addresses.on_swarm_event(&new_external_addr1());
157
158 assert_eq!(
159 addresses.as_slice(),
160 &[(*MEMORY_ADDR_1000).clone(), (*MEMORY_ADDR_2000).clone()]
161 );
162 }
163
164 fn new_external_addr1() -> FromSwarm<'static> {
165 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
166 addr: &MEMORY_ADDR_1000,
167 })
168 }
169
170 fn new_external_addr2() -> FromSwarm<'static> {
171 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
172 addr: &MEMORY_ADDR_2000,
173 })
174 }
175
176 fn expired_external_addr1() -> FromSwarm<'static> {
177 FromSwarm::ExternalAddrExpired(ExternalAddrExpired {
178 addr: &MEMORY_ADDR_1000,
179 })
180 }
181
182 static MEMORY_ADDR_1000: Lazy<Multiaddr> =
183 Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000)));
184 static MEMORY_ADDR_2000: Lazy<Multiaddr> =
185 Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(2000)));
186}