1use crate::{
6 Error,
7 model::RecordPackage,
8 peer::{Answer, CALL_TIMEOUT, Command, request},
9 peer_gossip::Shared,
10 peer_protocol::Kind,
11 peerbook::PeerAddress,
12 retrieval::RecordSource,
13};
14use bitcoin::Txid;
15use libp2p::{Multiaddr, PeerId};
16use std::{
17 sync::{
18 Arc,
19 atomic::{AtomicBool, AtomicUsize, Ordering},
20 mpsc,
21 },
22 time::Instant,
23};
24use tokio::sync::mpsc as async_mpsc;
25
26#[derive(Clone)]
29pub(crate) struct Link {
30 pub(crate) commands: async_mpsc::Sender<Command>,
31 pub(crate) stop: Arc<AtomicBool>,
32 pub(crate) shared: Arc<Shared>,
33}
34
35impl Link {
36 pub(crate) fn source(&self, peer: PeerId, addresses: Vec<Multiaddr>) -> PeerSource {
38 PeerSource {
39 peer,
40 addresses,
41 link: self.clone(),
42 }
43 }
44}
45
46#[derive(Clone)]
50pub struct PeerSource {
51 peer: PeerId,
52 addresses: Vec<Multiaddr>,
53 link: Link,
54}
55
56impl PeerSource {
57 pub(crate) fn request<T>(
58 &self,
59 kind: Kind,
60 limit: Instant,
61 answer: fn(mpsc::SyncSender<Result<T, Error>>) -> Answer,
62 ) -> Result<T, Error> {
63 let target = (self.peer, self.addresses.clone());
64 request(
65 &self.link.commands,
66 &self.link.stop,
67 target,
68 kind,
69 limit,
70 answer,
71 )
72 }
73
74 pub(crate) fn connected(&self) -> bool {
76 self.link.shared.is_connected(self.peer)
77 }
78
79 pub(crate) fn outbound(&self) -> bool {
81 self.link.shared.is_outbound(self.peer)
82 }
83
84 pub(crate) fn configured(&self) -> bool {
86 !self.addresses.is_empty()
87 }
88
89 pub(crate) fn disconnect(&self) {
91 let _ = self.link.commands.try_send(Command::Disconnect(self.peer));
92 }
93}
94
95impl RecordSource for PeerSource {
96 fn fetch(&self, txid: Txid) -> Result<Option<RecordPackage>, Error> {
97 self.fetch_until(txid, Instant::now() + CALL_TIMEOUT)
98 }
99}
100
101#[derive(Clone)]
105pub struct SyncPeers {
106 link: Link,
107 manual: Vec<PeerAddress>,
108 next: Arc<AtomicUsize>,
109 next_inbound: Arc<AtomicUsize>,
110}
111
112impl SyncPeers {
113 pub const RECORD_PEERS: usize = 4;
115
116 pub(crate) fn new(link: Link, manual: Vec<PeerAddress>) -> Self {
117 Self {
118 link,
119 manual,
120 next: Arc::new(AtomicUsize::new(0)),
121 next_inbound: Arc::new(AtomicUsize::new(0)),
122 }
123 }
124
125 pub fn sources(&self, omitting: &[String]) -> Vec<(String, PeerSource)> {
127 let configured = self
128 .manual
129 .iter()
130 .map(|address| (address.peer_id(), vec![address.address().clone()]));
131 let live = self
132 .link
133 .shared
134 .live()
135 .into_iter()
136 .map(|peer| (peer, Vec::new()));
137 let mut sources: Vec<(String, PeerSource)> = Vec::new();
138 for (peer, addresses) in configured.chain(live) {
139 let label = peer.to_string();
140 if omitting.contains(&label) || sources.iter().any(|(known, _)| *known == label) {
141 continue;
142 }
143 sources.push((label, self.link.source(peer, addresses)));
144 }
145 sources
146 }
147
148 pub(crate) fn window(&self, size: usize) -> Vec<PeerSource> {
153 let peers = self.sources(&[]);
154 let (configured, live): (Vec<PeerSource>, Vec<PeerSource>) = peers
155 .into_iter()
156 .map(|(_, source)| source)
157 .partition(PeerSource::configured);
158 let (outbound, inbound): (Vec<PeerSource>, Vec<PeerSource>) =
159 live.into_iter().partition(PeerSource::outbound);
160 let mut window: Vec<PeerSource> = configured.into_iter().take(size).collect();
161 for (group, next) in [(&outbound, &self.next), (&inbound, &self.next_inbound)] {
162 let room = size.saturating_sub(window.len()).min(group.len());
163 if room > 0 {
164 let start = next.fetch_add(room, Ordering::Relaxed) % group.len();
165 window
166 .extend((0..room).map(|offset| group[(start + offset) % group.len()].clone()));
167 }
168 }
169 window
170 }
171
172 pub fn record_sources(&self) -> Vec<PeerSource> {
174 self.window(Self::RECORD_PEERS)
175 }
176
177 pub fn record_count(&self) -> usize {
179 self.sources(&[]).len().min(Self::RECORD_PEERS)
180 }
181}