1use crate::chaindef::BlockHash;
2use crate::def::{PROTOCOL_VERSION_MAX, PROTOCOL_VERSION_MIN};
3use crate::discovery::discoverer::PeerDiscoverer;
4use crate::discovery::peer::{CandidatePeer, ServerPeer};
5use crate::errors::rpc_invalid_params;
6use crate::query::Query;
7use crate::rpc::parseutil::{parse_version_str, str_from_value};
8use anyhow::{Context, Result};
9use ip_rfc;
10use serde_json::Value;
11use std::net::ToSocketAddrs;
12use std::sync::Arc;
13
14use version_compare::Version;
15
16use super::parseutil::hash_from_value;
17
18const SPEC_DEFAULT_VERSION: &str = "1.4";
25const MAX_CANDIDATES_PER_REQ: usize = 10;
27
28pub struct ServerRPC {
29 server_donation_address: Option<String>,
30}
31
32impl ServerRPC {
36 pub fn new(server_donation_address: Option<String>) -> ServerRPC {
37 ServerRPC {
38 server_donation_address,
39 }
40 }
41
42 pub fn server_donation_address(&self) -> Result<Value> {
46 Ok(json!(self.server_donation_address))
47 }
48}
49
50fn best_match(client_min: &Version, client_max: &Version) -> String {
51 let our_min = Version::from(PROTOCOL_VERSION_MIN).unwrap();
52 let our_max = Version::from(PROTOCOL_VERSION_MAX).unwrap();
53
54 if *client_max >= our_max {
55 return our_max.as_str().into();
56 }
57
58 if *client_max <= our_min {
59 return our_min.as_str().into();
60 }
61
62 if *client_min >= our_min && *client_max <= our_max {
63 return client_max.as_str().into();
64 }
65
66 our_min.as_str().into()
67}
68
69fn best_match_response(version: &str, client_min: &Version, client_max: &Version) -> Value {
70 json!([version, best_match(client_min, client_max)])
71}
72
73pub fn parse_version(version: &str) -> Result<Version> {
74 let version =
75 Version::from(version).context(rpc_invalid_params("invalid version string".to_string()))?;
76 Ok(version)
77}
78
79pub fn server_version(params: &[Value], server_version: &str) -> Result<Value> {
80 let default_version = json!(SPEC_DEFAULT_VERSION);
82 let val = params.get(1).unwrap_or(&default_version);
83
84 if let Ok(versionstr) = str_from_value(Some(val), "version") {
85 let version = parse_version(&versionstr)?;
86 return Ok(best_match_response(server_version, &version, &version));
87 }
88
89 if let Some(minmax_list) = val.as_array() {
90 let min = str_from_value(Some(&minmax_list[0]), "version")?;
91 let min = parse_version(&min)?;
92 let max = str_from_value(Some(&minmax_list[1]), "version")?;
93 let max = parse_version(&max)?;
94 return Ok(best_match_response(server_version, &min, &max));
95 }
96
97 Err(rpc_invalid_params(
98 "invalid value in version argument".to_string(),
99 ))
100}
101
102pub async fn server_banner(query: &Arc<Query>) -> Result<Value> {
103 Ok(json!(query.get_banner().await?))
104}
105
106pub async fn server_peers_subscribe(discoverer: &PeerDiscoverer) -> Value {
107 json!(discoverer
108 .get_peers()
109 .await
110 .iter()
111 .map(|p| {
112 let ip = p.ip().to_string();
113 let host = p.hostname().unwrap_or(ip.clone());
114
115 let mut properties: Vec<String> = Vec::default();
116 if let Some(port) = p.tcp_port() {
117 properties.push(format!("t{}", port));
118 }
119 if let Some(port) = p.ssl_port() {
120 properties.push(format!("s{}", port));
121 }
122 properties.push(format!("v{}", p.version()));
123 json!(vec![json!(ip), json!(host), json!(properties)])
124 })
125 .collect::<Vec<Value>>())
126}
127
128pub fn server_features(query: &Arc<Query>) -> Result<Value> {
129 Ok(json!(query.announcer().server_features()))
130}
131
132pub async fn server_add_peer(
133 params: &[Value],
134 addr: &std::net::SocketAddr,
135 genesis_hash: &BlockHash,
136 discoverer: &PeerDiscoverer,
137) -> Result<Value> {
138 debug!("Add peer attempt by server peer {addr}");
139
140 if !ip_rfc::global(&addr.ip()) {
142 debug!("Rejecting server peer '{addr}': Peer does not have a globally routable IP.");
143 return Ok(json!(false));
144 }
145
146 let peer_features = params.first().context("argument missing")?;
147 let peer_hosts = peer_features.get("hosts").unwrap_or(&Value::Null);
148 if peer_hosts.is_null() {
149 debug!("Rejecting server peer '{addr}': No hosts provided");
151 return Ok(json!(false));
152 }
153
154 let peer_genesis: BlockHash = hash_from_value(peer_features.get("genesis_hash"))?;
155 if peer_genesis != *genesis_hash {
156 bail!("Genesis mismatch {peer_genesis} != {genesis_hash}");
157 }
158
159 let peer_version = peer_features
160 .get("protocol_max")
161 .context("No protocol version provided")?
162 .as_str()
163 .context("Protocol version not a string")?;
164
165 let peer_version = parse_version_str(peer_version).context("invalid version")?;
166
167 for (i, (claimed_hostname, ports)) in peer_hosts
169 .as_object()
170 .context("Unable to parse hosts")?
171 .into_iter()
172 .enumerate()
173 {
174 if i >= MAX_CANDIDATES_PER_REQ {
175 break;
176 }
177 let tcp_port = match ports.get("tcp_port").and_then(|p| p.as_u64()) {
178 Some(p) => {
179 if p > u16::MAX as u64 {
180 bail!("Invalid TCP port {p}");
181 }
182 Some(p as u16)
183 }
184 None => None,
185 };
186
187 let ssl_port = match ports.get("ssl_port").and_then(|p| p.as_u64()) {
188 Some(p) => {
189 if p > u16::MAX as u64 {
190 bail!("Invalid SSL port {p}");
191 }
192 Some(p as u16)
193 }
194 None => None,
195 };
196
197 if tcp_port.is_none() && ssl_port.is_none() {
198 bail!("TCP port or SSL port not provided");
199 }
200
201 let resolve_test = format!(
202 "{claimed_hostname}:{}",
203 tcp_port.unwrap_or_else(|| ssl_port.unwrap())
204 );
205 let hostname = match resolve_test.to_socket_addrs() {
206 Ok(resolves) => {
207 if !resolves
210 .into_iter()
211 .any(|host_addr| host_addr.ip() == addr.ip())
212 {
213 trace!("Hostname '{claimed_hostname}' does not resolve to the address peer is connected from");
214 None
215 } else {
216 Some(claimed_hostname)
217 }
218 }
219 Err(e) => {
220 trace!("Hostname '{claimed_hostname}' resolve error: {e}");
221 None
222 }
223 };
224
225 discoverer
226 .add_candidate(CandidatePeer {
227 tcp_port: tcp_port as Option<u16>,
228 ssl_port,
229 hostname: hostname.cloned(),
230 ip: addr.ip(),
231 version: Some(peer_version.clone()),
232 })
233 .await?;
234 }
235
236 Ok(json!(true))
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn test_server_version_noarg() {
245 let resp = server_version(&[], "dummy server").unwrap();
246 let resp = resp.as_array().unwrap();
247
248 assert!(resp[0].is_string());
249 assert_eq!(resp[1].as_str().unwrap(), SPEC_DEFAULT_VERSION);
250 }
251
252 #[test]
253 fn test_server_version_strarg() {
254 let clientver = json!("bestclient 1.0");
255 let resp = server_version(&[clientver.clone(), json!("1.3")], "dummy server").unwrap();
256 assert_eq!(resp[1].as_str().unwrap(), PROTOCOL_VERSION_MIN);
257 let resp = server_version(&[clientver.clone(), json!("13.3.7")], "dummy server").unwrap();
258 assert_eq!(resp[1].as_str().unwrap(), PROTOCOL_VERSION_MAX);
259 }
260
261 #[test]
262 fn test_server_version_minmax() {
263 let clientver = json!("bestclient 1.0");
264 let resp = server_version(
266 &[clientver.clone(), json!(["1.4", "13.3.7"])],
267 "dummy server",
268 )
269 .unwrap();
270 assert_eq!(resp[1].as_str().unwrap(), PROTOCOL_VERSION_MAX);
271
272 let resp =
274 server_version(&[clientver.clone(), json!(["1.2", "1.3"])], "dummy server").unwrap();
275 assert_eq!(resp[1].as_str().unwrap(), PROTOCOL_VERSION_MIN);
276
277 let client_max = "1.4.1";
279 let resp = server_version(
280 &[clientver.clone(), json!([PROTOCOL_VERSION_MIN, client_max])],
281 "dummy server",
282 )
283 .unwrap();
284 assert_eq!(resp[1].as_str().unwrap(), client_max);
285 }
286
287 #[test]
288 fn test_donation_address() {
289 let rpc = ServerRPC::new(None);
290 let result: Option<String> = None;
291 assert_eq!(rpc.server_donation_address().unwrap(), json!(result));
292 let rpc = ServerRPC::new(Some(
293 "bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqu08dsyxz98whc".to_string(),
294 ));
295 assert_eq!(
296 rpc.server_donation_address().unwrap(),
297 json!("bitcoincash:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqu08dsyxz98whc")
298 );
299 }
300}