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(&mut self, event: &FromSwarm) -> 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 tracing::debug!(address=%addr, "Refreshed external address");
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 tracing::debug!(
51 external_address=%expired,
52 address_limit=%MAX_LOCAL_EXTERNAL_ADDRS,
53 "Removing previously confirmed external address because we reached the address limit"
54 );
55 }
56
57 return true;
58 }
59 FromSwarm::ExternalAddrExpired(ExternalAddrExpired {
60 addr: expired_addr, ..
61 }) => {
62 let pos = match self
63 .addresses
64 .iter()
65 .position(|candidate| candidate == *expired_addr)
66 {
67 None => return false,
68 Some(p) => p,
69 };
70
71 self.addresses.remove(pos);
72 return true;
73 }
74 _ => {}
75 }
76
77 false
78 }
79
80 fn push_front(&mut self, addr: &Multiaddr) {
81 self.addresses.insert(0, addr.clone()); }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use libp2p_core::multiaddr::Protocol;
89 use once_cell::sync::Lazy;
90 use rand::Rng;
91
92 #[test]
93 fn new_external_addr_returns_correct_changed_value() {
94 let mut addresses = ExternalAddresses::default();
95
96 let changed = addresses.on_swarm_event(&new_external_addr1());
97 assert!(changed);
98
99 let changed = addresses.on_swarm_event(&new_external_addr1());
100 assert!(!changed)
101 }
102
103 #[test]
104 fn expired_external_addr_returns_correct_changed_value() {
105 let mut addresses = ExternalAddresses::default();
106 addresses.on_swarm_event(&new_external_addr1());
107
108 let changed = addresses.on_swarm_event(&expired_external_addr1());
109 assert!(changed);
110
111 let changed = addresses.on_swarm_event(&expired_external_addr1());
112 assert!(!changed)
113 }
114
115 #[test]
116 fn more_recent_external_addresses_are_prioritized() {
117 let mut addresses = ExternalAddresses::default();
118
119 addresses.on_swarm_event(&new_external_addr1());
120 addresses.on_swarm_event(&new_external_addr2());
121
122 assert_eq!(
123 addresses.as_slice(),
124 &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]
125 );
126 }
127
128 #[test]
129 fn when_pushing_more_than_max_addresses_oldest_is_evicted() {
130 let mut addresses = ExternalAddresses::default();
131
132 while addresses.as_slice().len() < MAX_LOCAL_EXTERNAL_ADDRS {
133 let random_address =
134 Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000)));
135 addresses.on_swarm_event(&FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
136 addr: &random_address,
137 }));
138 }
139
140 addresses.on_swarm_event(&new_external_addr2());
141
142 assert_eq!(addresses.as_slice().len(), 20);
143 assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone());
144 }
145
146 #[test]
147 fn reporting_existing_external_address_moves_it_to_the_front() {
148 let mut addresses = ExternalAddresses::default();
149
150 addresses.on_swarm_event(&new_external_addr1());
151 addresses.on_swarm_event(&new_external_addr2());
152 addresses.on_swarm_event(&new_external_addr1());
153
154 assert_eq!(
155 addresses.as_slice(),
156 &[(*MEMORY_ADDR_1000).clone(), (*MEMORY_ADDR_2000).clone()]
157 );
158 }
159
160 fn new_external_addr1() -> FromSwarm<'static> {
161 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
162 addr: &MEMORY_ADDR_1000,
163 })
164 }
165
166 fn new_external_addr2() -> FromSwarm<'static> {
167 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
168 addr: &MEMORY_ADDR_2000,
169 })
170 }
171
172 fn expired_external_addr1() -> FromSwarm<'static> {
173 FromSwarm::ExternalAddrExpired(ExternalAddrExpired {
174 addr: &MEMORY_ADDR_1000,
175 })
176 }
177
178 static MEMORY_ADDR_1000: Lazy<Multiaddr> =
179 Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000)));
180 static MEMORY_ADDR_2000: Lazy<Multiaddr> =
181 Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(2000)));
182}