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