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 std::sync::LazyLock;
91
92 use libp2p_core::multiaddr::Protocol;
93 use rand::Rng;
94
95 use super::*;
96
97 #[test]
98 fn new_external_addr_returns_correct_changed_value() {
99 let mut addresses = ExternalAddresses::default();
100
101 let changed = addresses.on_swarm_event(&new_external_addr1());
102 assert!(changed);
103
104 let changed = addresses.on_swarm_event(&new_external_addr1());
105 assert!(!changed)
106 }
107
108 #[test]
109 fn expired_external_addr_returns_correct_changed_value() {
110 let mut addresses = ExternalAddresses::default();
111 addresses.on_swarm_event(&new_external_addr1());
112
113 let changed = addresses.on_swarm_event(&expired_external_addr1());
114 assert!(changed);
115
116 let changed = addresses.on_swarm_event(&expired_external_addr1());
117 assert!(!changed)
118 }
119
120 #[test]
121 fn more_recent_external_addresses_are_prioritized() {
122 let mut addresses = ExternalAddresses::default();
123
124 addresses.on_swarm_event(&new_external_addr1());
125 addresses.on_swarm_event(&new_external_addr2());
126
127 assert_eq!(
128 addresses.as_slice(),
129 &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]
130 );
131 }
132
133 #[test]
134 fn when_pushing_more_than_max_addresses_oldest_is_evicted() {
135 let mut addresses = ExternalAddresses::default();
136
137 while addresses.as_slice().len() < MAX_LOCAL_EXTERNAL_ADDRS {
138 let random_address =
139 Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000)));
140 addresses.on_swarm_event(&FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
141 addr: &random_address,
142 }));
143 }
144
145 addresses.on_swarm_event(&new_external_addr2());
146
147 assert_eq!(addresses.as_slice().len(), 20);
148 assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone());
149 }
150
151 #[test]
152 fn reporting_existing_external_address_moves_it_to_the_front() {
153 let mut addresses = ExternalAddresses::default();
154
155 addresses.on_swarm_event(&new_external_addr1());
156 addresses.on_swarm_event(&new_external_addr2());
157 addresses.on_swarm_event(&new_external_addr1());
158
159 assert_eq!(
160 addresses.as_slice(),
161 &[(*MEMORY_ADDR_1000).clone(), (*MEMORY_ADDR_2000).clone()]
162 );
163 }
164
165 fn new_external_addr1() -> FromSwarm<'static> {
166 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
167 addr: &MEMORY_ADDR_1000,
168 })
169 }
170
171 fn new_external_addr2() -> FromSwarm<'static> {
172 FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed {
173 addr: &MEMORY_ADDR_2000,
174 })
175 }
176
177 fn expired_external_addr1() -> FromSwarm<'static> {
178 FromSwarm::ExternalAddrExpired(ExternalAddrExpired {
179 addr: &MEMORY_ADDR_1000,
180 })
181 }
182
183 static MEMORY_ADDR_1000: LazyLock<Multiaddr> =
184 LazyLock::new(|| Multiaddr::empty().with(Protocol::Memory(1000)));
185 static MEMORY_ADDR_2000: LazyLock<Multiaddr> =
186 LazyLock::new(|| Multiaddr::empty().with(Protocol::Memory(2000)));
187}