1use crate::{
6 Error,
7 model::{
8 OverlayStatus, PeerLink as LinkStatus, Reachability as ReachabilityStatus, RecordPackage,
9 },
10 peer::{Answer, CALL_TIMEOUT, Command, request},
11 peer_gossip::Shared,
12 peer_nat::{PeerLink, Reachability},
13 peer_protocol::Kind,
14 peerbook::PeerAddress,
15 recorded::RecordedError,
16 retrieval::RecordSource,
17};
18use bitcoin::Txid;
19use libp2p::{Multiaddr, PeerId};
20use std::{
21 sync::{
22 Arc,
23 atomic::{AtomicBool, AtomicUsize, Ordering},
24 mpsc,
25 },
26 time::Instant,
27};
28use tokio::sync::mpsc as async_mpsc;
29
30#[derive(Clone)]
33pub(crate) struct Link {
34 pub(crate) commands: async_mpsc::Sender<Command>,
35 pub(crate) stop: Arc<AtomicBool>,
36 pub(crate) shared: Arc<Shared>,
37}
38
39impl Link {
40 pub(crate) fn source(&self, peer: PeerId, addresses: Vec<Multiaddr>) -> PeerSource {
42 PeerSource {
43 peer,
44 addresses,
45 link: self.clone(),
46 }
47 }
48}
49
50#[derive(Clone)]
54pub struct PeerSource {
55 peer: PeerId,
56 addresses: Vec<Multiaddr>,
57 link: Link,
58}
59
60impl PeerSource {
61 pub(crate) fn request<T>(
62 &self,
63 kind: Kind,
64 limit: Instant,
65 answer: fn(mpsc::SyncSender<Result<T, Error>>) -> Answer,
66 ) -> Result<T, Error> {
67 let target = (self.peer, self.addresses.clone());
68 request(
69 &self.link.commands,
70 &self.link.stop,
71 target,
72 kind,
73 limit,
74 answer,
75 )
76 }
77
78 pub(crate) fn connected(&self) -> bool {
80 self.link.shared.is_connected(self.peer)
81 }
82
83 pub(crate) fn has_data(&self) -> bool {
85 self.link.shared.has_direct(self.peer)
86 }
87
88 pub(crate) fn outbound(&self) -> bool {
90 self.link.shared.is_outbound(self.peer)
91 }
92
93 pub(crate) fn configured(&self) -> bool {
95 !self.addresses.is_empty()
96 }
97
98 pub(crate) fn disconnect(&self) {
100 let _ = self.link.commands.try_send(Command::Disconnect(self.peer));
101 }
102
103 pub(crate) fn release(&self) {
106 let _ = self.link.commands.try_send(Command::Release(self.peer));
107 }
108}
109
110impl RecordSource for PeerSource {
111 fn fetch(&self, txid: Txid) -> Result<Option<RecordPackage>, Error> {
112 self.fetch_until(txid, Instant::now() + CALL_TIMEOUT)
113 }
114}
115
116#[derive(Clone)]
120pub struct SyncPeers {
121 link: Link,
122 manual: Vec<PeerAddress>,
123 next: Arc<AtomicUsize>,
124 next_inbound: Arc<AtomicUsize>,
125}
126
127impl SyncPeers {
128 pub const RECORD_PEERS: usize = 4;
130
131 pub(crate) fn new(link: Link, manual: Vec<PeerAddress>) -> Self {
132 Self {
133 link,
134 manual,
135 next: Arc::new(AtomicUsize::new(0)),
136 next_inbound: Arc::new(AtomicUsize::new(0)),
137 }
138 }
139
140 pub fn sources(&self, omitting: &[String]) -> Vec<(String, PeerSource)> {
142 let configured = self
143 .manual
144 .iter()
145 .map(|address| (address.peer_id(), vec![address.address().clone()]));
146 let live = self
147 .link
148 .shared
149 .live()
150 .into_iter()
151 .map(|peer| (peer, Vec::new()));
152 let mut sources: Vec<(String, PeerSource)> = Vec::new();
153 for (peer, addresses) in configured.chain(live) {
154 let label = peer.to_string();
155 if omitting.contains(&label) || sources.iter().any(|(known, _)| *known == label) {
156 continue;
157 }
158 sources.push((label, self.link.source(peer, addresses)));
159 }
160 sources
161 }
162
163 pub(crate) fn window(&self, size: usize) -> Vec<PeerSource> {
168 let peers = self.sources(&[]);
169 let (configured, live): (Vec<PeerSource>, Vec<PeerSource>) = peers
170 .into_iter()
171 .map(|(_, source)| source)
172 .partition(PeerSource::configured);
173 let (outbound, inbound): (Vec<PeerSource>, Vec<PeerSource>) =
174 live.into_iter().partition(PeerSource::outbound);
175 let mut window: Vec<PeerSource> = configured.into_iter().take(size).collect();
176 for (group, next) in [(&outbound, &self.next), (&inbound, &self.next_inbound)] {
177 let room = size.saturating_sub(window.len()).min(group.len());
178 if room > 0 {
179 let start = next.fetch_add(room, Ordering::Relaxed) % group.len();
180 window
181 .extend((0..room).map(|offset| group[(start + offset) % group.len()].clone()));
182 }
183 }
184 window
185 }
186
187 pub fn record_sources(&self) -> Vec<PeerSource> {
189 self.window(Self::RECORD_PEERS)
190 }
191
192 pub fn record_count(&self) -> usize {
194 self.sources(&[]).len().min(Self::RECORD_PEERS)
195 }
196
197 pub(crate) fn link_of(&self, peer: PeerId) -> Option<LinkStatus> {
199 let links = self.link.shared.links();
200 let (_, link) = links.iter().find(|(known, _)| *known == peer)?;
201 Some(match link {
202 PeerLink::Direct => LinkStatus::Direct,
203 PeerLink::Relayed => LinkStatus::Relayed,
204 })
205 }
206
207 pub(crate) fn nat_status(&self, status: &mut OverlayStatus) {
209 let nat = &self.link.shared.nat;
210 status.reachability = match nat.reachability() {
211 Reachability::Unknown => ReachabilityStatus::Unknown,
212 Reachability::Public => ReachabilityStatus::Public,
213 Reachability::Private => ReachabilityStatus::Private,
214 };
215 status.relays = nat.relays().iter().map(PeerId::to_string).collect();
216 let punch = nat.hole_punch();
217 let (last_error, last_error_at) = RecordedError::split(punch.last_error);
218 status.hole_punch.attempts = punch.attempts;
219 status.hole_punch.successes = punch.successes;
220 status.hole_punch.last_error = last_error;
221 status.hole_punch.last_error_at = last_error_at;
222 }
223}