ckb_rpc/module/net.rs
1use crate::error::RPCError;
2use async_trait::async_trait;
3use ckb_chain::ChainController;
4use ckb_jsonrpc_types::{
5 BannedAddr, LocalNode, LocalNodeProtocol, NodeAddress, PeerSyncState, RemoteNode,
6 RemoteNodeProtocol, SyncState, Timestamp,
7};
8use ckb_network::{NetworkController, extract_peer_id, multiaddr::Multiaddr};
9use ckb_sync::SyncShared;
10use ckb_systemtime::unix_time_as_millis;
11use ckb_types::packed;
12use jsonrpc_core::Result;
13use jsonrpc_utils::rpc;
14use std::collections::HashSet;
15use std::sync::Arc;
16
17const MAX_ADDRS: usize = 50;
18const DEFAULT_BAN_DURATION: u64 = 24 * 60 * 60 * 1000; // 1 day
19
20/// RPC Module Net for P2P network.
21#[rpc(openrpc)]
22#[async_trait]
23pub trait NetRpc {
24 /// Returns the local node information.
25 ///
26 /// The local node means the node itself which is serving the RPC.
27 ///
28 /// ## Examples
29 ///
30 /// Request
31 ///
32 /// ```json
33 /// {
34 /// "id": 42,
35 /// "jsonrpc": "2.0",
36 /// "method": "local_node_info",
37 /// "params": []
38 /// }
39 /// ```
40 ///
41 /// Response
42 ///
43 /// ```json
44 /// {
45 /// "id": 42,
46 /// "jsonrpc": "2.0",
47 /// "result": {
48 /// "active": true,
49 /// "addresses": [
50 /// {
51 /// "address": "/ip4/192.168.0.2/tcp/8112/p2p/QmTRHCdrRtgUzYLNCin69zEvPvLYdxUZLLfLYyHVY3DZAS",
52 /// "score": "0xff"
53 /// },
54 /// {
55 /// "address": "/ip4/0.0.0.0/tcp/8112/p2p/QmTRHCdrRtgUzYLNCin69zEvPvLYdxUZLLfLYyHVY3DZAS",
56 /// "score": "0x1"
57 /// }
58 /// ],
59 /// "connections": "0xb",
60 /// "node_id": "QmTRHCdrRtgUzYLNCin69zEvPvLYdxUZLLfLYyHVY3DZAS",
61 /// "protocols": [
62 /// {
63 /// "id": "0x0",
64 /// "name": "/ckb/ping",
65 /// "support_versions": [
66 /// "0.0.1"
67 /// ]
68 /// },
69 /// {
70 /// "id": "0x1",
71 /// "name": "/ckb/discovery",
72 /// "support_versions": [
73 /// "0.0.1"
74 /// ]
75 /// }
76 /// ],
77 /// "version": "0.34.0 (f37f598 2020-07-17)"
78 /// }
79 /// }
80 /// ```
81 #[rpc(name = "local_node_info")]
82 fn local_node_info(&self) -> Result<LocalNode>;
83
84 /// Returns the connected peers' information.
85 ///
86 /// ## Examples
87 ///
88 /// Request
89 ///
90 /// ```json
91 /// {
92 /// "id": 42,
93 /// "jsonrpc": "2.0",
94 /// "method": "get_peers",
95 /// "params": []
96 /// }
97 /// ```
98 ///
99 /// Response
100 ///
101 /// ```json
102 /// {
103 /// "id": 42,
104 /// "jsonrpc": "2.0",
105 /// "result": [
106 /// {
107 /// "addresses": [
108 /// {
109 /// "address": "/ip6/::ffff:18.185.102.19/tcp/8115/p2p/QmXwUgF48ULy6hkgfqrEwEfuHW7WyWyWauueRDAYQHNDfN",
110 /// "score": "0x64"
111 /// },
112 /// {
113 /// "address": "/ip4/18.185.102.19/tcp/8115/p2p/QmXwUgF48ULy6hkgfqrEwEfuHW7WyWyWauueRDAYQHNDfN",
114 /// "score": "0x64"
115 /// }
116 /// ],
117 /// "connected_duration": "0x2f",
118 /// "is_outbound": true,
119 /// "last_ping_duration": "0x1a",
120 /// "node_id": "QmXwUgF48ULy6hkgfqrEwEfuHW7WyWyWauueRDAYQHNDfN",
121 /// "protocols": [
122 /// {
123 /// "id": "0x4",
124 /// "version": "0.0.1"
125 /// },
126 /// {
127 /// "id": "0x2",
128 /// "version": "0.0.1"
129 /// },
130 /// {
131 /// "id": "0x1",
132 /// "version": "0.0.1"
133 /// },
134 /// {
135 /// "id": "0x64",
136 /// "version": "1"
137 /// },
138 /// {
139 /// "id": "0x6e",
140 /// "version": "1"
141 /// },
142 /// {
143 /// "id": "0x66",
144 /// "version": "1"
145 /// },
146 /// {
147 /// "id": "0x65",
148 /// "version": "1"
149 /// },
150 /// {
151 /// "id": "0x0",
152 /// "version": "0.0.1"
153 /// }
154 /// ],
155 /// "sync_state": {
156 /// "best_known_header_hash": null,
157 /// "best_known_header_number": null,
158 /// "can_fetch_count": "0x80",
159 /// "inflight_count": "0xa",
160 /// "last_common_header_hash": null,
161 /// "last_common_header_number": null,
162 /// "unknown_header_list_size": "0x20"
163 /// },
164 /// "version": "0.34.0 (f37f598 2020-07-17)"
165 /// },
166 /// {
167 /// "addresses": [
168 /// {
169 /// "address": "/ip4/174.80.182.60/tcp/52965/p2p/QmVTMd7SEXfxS5p4EEM5ykTe1DwWWVewEM3NwjLY242vr2",
170 /// "score": "0x1"
171 /// }
172 /// ],
173 /// "connected_duration": "0x95",
174 /// "is_outbound": true,
175 /// "last_ping_duration": "0x41",
176 /// "node_id": "QmSrkzhdBMmfCGx8tQGwgXxzBg8kLtX8qMcqECMuKWsxDV",
177 /// "protocols": [
178 /// {
179 /// "id": "0x0",
180 /// "version": "0.0.1"
181 /// },
182 /// {
183 /// "id": "0x2",
184 /// "version": "0.0.1"
185 /// },
186 /// {
187 /// "id": "0x6e",
188 /// "version": "1"
189 /// },
190 /// {
191 /// "id": "0x66",
192 /// "version": "1"
193 /// },
194 /// {
195 /// "id": "0x1",
196 /// "version": "0.0.1"
197 /// },
198 /// {
199 /// "id": "0x65",
200 /// "version": "1"
201 /// },
202 /// {
203 /// "id": "0x64",
204 /// "version": "1"
205 /// },
206 /// {
207 /// "id": "0x4",
208 /// "version": "0.0.1"
209 /// }
210 /// ],
211 /// "sync_state": {
212 /// "best_known_header_hash": "0x2157c72b3eddd41a7a14c361173cd22ef27d7e0a29eda2e511ee0b3598c0b895",
213 /// "best_known_header_number": "0xdb835",
214 /// "can_fetch_count": "0x80",
215 /// "inflight_count": "0xa",
216 /// "last_common_header_hash": "0xc63026bd881d880bb142c855dc8153187543245f0a94391c831c75df31f263c4",
217 /// "last_common_header_number": "0x4dc08",
218 /// "unknown_header_list_size": "0x1f"
219 /// },
220 /// "version": "0.30.1 (5cc1b75 2020-03-23)"
221 /// }
222 /// ]
223 /// }
224 /// ```
225 #[rpc(name = "get_peers")]
226 fn get_peers(&self) -> Result<Vec<RemoteNode>>;
227
228 /// Returns all banned IPs/Subnets.
229 ///
230 /// ## Examples
231 ///
232 /// Request
233 ///
234 /// ```json
235 /// {
236 /// "id": 42,
237 /// "jsonrpc": "2.0",
238 /// "method": "get_banned_addresses",
239 /// "params": []
240 /// }
241 /// ```
242 ///
243 /// Response
244 ///
245 /// ```json
246 /// {
247 /// "id": 42,
248 /// "jsonrpc": "2.0",
249 /// "result": [
250 /// {
251 /// "address": "192.168.0.2/32",
252 /// "ban_reason": "",
253 /// "ban_until": "0x1ac89236180",
254 /// "created_at": "0x16bde533338"
255 /// }
256 /// ]
257 /// }
258 /// ```
259 #[rpc(name = "get_banned_addresses")]
260 fn get_banned_addresses(&self) -> Result<Vec<BannedAddr>>;
261
262 /// Clears all banned IPs/Subnets.
263 ///
264 /// ## Examples
265 ///
266 /// Request
267 ///
268 /// ```json
269 /// {
270 /// "id": 42,
271 /// "jsonrpc": "2.0",
272 /// "method": "clear_banned_addresses",
273 /// "params": []
274 /// }
275 /// ```
276 ///
277 /// Response
278 ///
279 /// ```json
280 /// {
281 /// "id": 42,
282 /// "jsonrpc": "2.0",
283 /// "result": null
284 /// }
285 /// ```
286 #[rpc(name = "clear_banned_addresses")]
287 fn clear_banned_addresses(&self) -> Result<()>;
288
289 /// Inserts or deletes an IP/Subnet from the banned list
290 ///
291 /// ## Params
292 ///
293 /// * `address` - The IP/Subnet with an optional netmask (default is /32 = single IP). Examples:
294 /// * "192.168.0.2" bans a single IP
295 /// * "192.168.0.0/24" bans IP from "192.168.0.0" to "192.168.0.255".
296 /// * `command` - `insert` to insert an IP/Subnet to the list, `delete` to delete an IP/Subnet from the list.
297 /// * `ban_time` - Time in milliseconds how long (or until when if \[absolute\] is set) the IP is banned, optional parameter, null means using the default time of 24h
298 /// * `absolute` - If set, the `ban_time` must be an absolute timestamp in milliseconds since epoch, optional parameter.
299 /// * `reason` - Ban reason, optional parameter.
300 ///
301 /// ## Errors
302 ///
303 /// * [`InvalidParams (-32602)`](../enum.RPCError.html#variant.InvalidParams)
304 /// * Expected `address` to be a valid IP address with an optional netmask.
305 /// * Expected `command` to be in the list [insert, delete].
306 ///
307 /// ## Examples
308 ///
309 /// Request
310 ///
311 /// ```json
312 /// {
313 /// "id": 42,
314 /// "jsonrpc": "2.0",
315 /// "method": "set_ban",
316 /// "params": [
317 /// "192.168.0.2",
318 /// "insert",
319 /// "0x1ac89236180",
320 /// true,
321 /// "set_ban example"
322 /// ]
323 /// }
324 /// ```
325 ///
326 /// Response
327 ///
328 /// ```json
329 /// {
330 /// "id": 42,
331 /// "jsonrpc": "2.0",
332 /// "result": null
333 /// }
334 /// ```
335 #[rpc(name = "set_ban")]
336 fn set_ban(
337 &self,
338 address: String,
339 command: String,
340 ban_time: Option<Timestamp>,
341 absolute: Option<bool>,
342 reason: Option<String>,
343 ) -> Result<()>;
344
345 /// Returns chain synchronization state of this node.
346 ///
347 /// ## Examples
348 ///
349 /// Request
350 ///
351 /// ```json
352 /// {
353 /// "id": 42,
354 /// "jsonrpc": "2.0",
355 /// "method": "sync_state",
356 /// "params": []
357 /// }
358 /// ```
359 ///
360 /// Response
361 ///
362 /// ```json
363 /// {
364 /// "id": 42,
365 /// "jsonrpc": "2.0",
366 /// "result": {
367 /// "assume_valid_target": "0x0000000000000000000000000000000000000000000000000000000000000000",
368 /// "assume_valid_target_reached": true,
369 /// "best_known_block_number": "0x400",
370 /// "best_known_block_timestamp": "0x5cd2b117",
371 /// "fast_time": "0x3e8",
372 /// "ibd": true,
373 /// "inflight_blocks_count": "0x0",
374 /// "low_time": "0x5dc",
375 /// "min_chain_work": "0x0",
376 /// "min_chain_work_reached": true,
377 /// "normal_time": "0x4e2",
378 /// "orphan_blocks_count": "0x0",
379 /// "tip_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
380 /// "tip_number": "0x400",
381 /// "unverified_tip_hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
382 /// "unverified_tip_number": "0x400"
383 /// }
384 /// }
385 /// ```
386 #[rpc(name = "sync_state")]
387 fn sync_state(&self) -> Result<SyncState>;
388
389 /// Disable/enable all p2p network activity
390 ///
391 /// ## Params
392 ///
393 /// * `state` - true to enable networking, false to disable
394 ///
395 /// ## Examples
396 ///
397 /// Request
398 ///
399 /// ```json
400 /// {
401 /// "id": 42,
402 /// "jsonrpc": "2.0",
403 /// "method": "set_network_active",
404 /// "params": [
405 /// false
406 /// ]
407 /// }
408 /// ```
409 ///
410 /// Response
411 ///
412 /// ```json
413 /// {
414 /// "id": 42,
415 /// "jsonrpc": "2.0",
416 /// "result": null
417 /// }
418 /// ```
419 #[rpc(name = "set_network_active")]
420 fn set_network_active(&self, state: bool) -> Result<()>;
421
422 /// Attempts to add a node to the peers list and try connecting to it.
423 ///
424 /// ## Params
425 ///
426 /// * `peer_id` - The node id of the node.
427 /// * `address` - The address of the node.
428 ///
429 /// The full P2P address is usually displayed as `address/peer_id`, for example in the log
430 ///
431 /// ```text
432 /// 2020-09-16 15:31:35.191 +08:00 NetworkRuntime INFO ckb_network::network
433 /// Listen on address: /ip4/192.168.2.100/tcp/8114/QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS
434 /// ```
435 ///
436 /// And in RPC `local_node_info`:
437 ///
438 /// ```json
439 /// {
440 /// "addresses": [
441 /// {
442 /// "address": "/ip4/192.168.2.100/tcp/8114/QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS",
443 /// "score": "0xff"
444 /// }
445 /// ]
446 /// }
447 /// ```
448 ///
449 /// In both of these examples,
450 ///
451 /// * `peer_id` is `QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS`,
452 /// * and `address` is `/ip4/192.168.2.100/tcp/8114`
453 ///
454 /// ## Examples
455 ///
456 /// Request
457 ///
458 /// ```json
459 /// {
460 /// "id": 42,
461 /// "jsonrpc": "2.0",
462 /// "method": "add_node",
463 /// "params": [
464 /// "QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS",
465 /// "/ip4/192.168.2.100/tcp/8114"
466 /// ]
467 /// }
468 /// ```
469 ///
470 /// Response
471 ///
472 /// ```json
473 /// {
474 /// "id": 42,
475 /// "jsonrpc": "2.0",
476 /// "result": null
477 /// }
478 /// ```
479 #[rpc(name = "add_node")]
480 fn add_node(&self, peer_id: String, address: String) -> Result<()>;
481
482 /// Attempts to remove a node from the peers list and try disconnecting from it.
483 ///
484 /// ## Params
485 ///
486 /// * `peer_id` - The peer id of the node.
487 ///
488 /// This is the last part of a full P2P address. For example, in address
489 /// "/ip4/192.168.2.100/tcp/8114/QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS", the `peer_id`
490 /// is `QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS`.
491 ///
492 /// ## Examples
493 ///
494 /// Request
495 ///
496 /// ```json
497 /// {
498 /// "id": 42,
499 /// "jsonrpc": "2.0",
500 /// "method": "remove_node",
501 /// "params": [
502 /// "QmUsZHPbjjzU627UZFt4k8j6ycEcNvXRnVGxCPKqwbAfQS"
503 /// ]
504 /// }
505 /// ```
506 ///
507 /// Response
508 ///
509 /// ```json
510 /// {
511 /// "id": 42,
512 /// "jsonrpc": "2.0",
513 /// "result": null
514 /// }
515 /// ```
516 #[rpc(name = "remove_node")]
517 fn remove_node(&self, peer_id: String) -> Result<()>;
518
519 /// Requests that a ping is sent to all connected peers, to measure ping time.
520 ///
521 /// ## Examples
522 ///
523 /// Requests
524 ///
525 /// ```json
526 /// {
527 /// "id": 42,
528 /// "jsonrpc": "2.0",
529 /// "method": "ping_peers",
530 /// "params": []
531 /// }
532 /// ```
533 ///
534 /// Response
535 ///
536 /// ```json
537 /// {
538 /// "id": 42,
539 /// "jsonrpc": "2.0",
540 /// "result": null
541 /// }
542 /// ```
543 #[rpc(name = "ping_peers")]
544 fn ping_peers(&self) -> Result<()>;
545}
546
547#[derive(Clone)]
548pub(crate) struct NetRpcImpl {
549 pub network_controller: NetworkController,
550 pub sync_shared: Arc<SyncShared>,
551 pub chain_controller: Arc<ChainController>,
552}
553
554#[async_trait]
555impl NetRpc for NetRpcImpl {
556 fn local_node_info(&self) -> Result<LocalNode> {
557 Ok(LocalNode {
558 version: self.network_controller.version().to_owned(),
559 node_id: self.network_controller.node_id(),
560 active: self.network_controller.is_active(),
561 addresses: self
562 .network_controller
563 .public_urls(MAX_ADDRS)
564 .into_iter()
565 .map(|(address, score)| NodeAddress {
566 address,
567 score: u64::from(score).into(),
568 })
569 .collect(),
570 protocols: self
571 .network_controller
572 .protocols()
573 .into_iter()
574 .map(|(protocol_id, name, support_versions)| LocalNodeProtocol {
575 id: (protocol_id.value() as u64).into(),
576 name,
577 support_versions,
578 })
579 .collect::<Vec<_>>(),
580 connections: (self.network_controller.connected_peers().len() as u64).into(),
581 })
582 }
583
584 fn get_peers(&self) -> Result<Vec<RemoteNode>> {
585 let peers: Vec<RemoteNode> = self
586 .network_controller
587 .connected_peers()
588 .iter()
589 .map(|(peer_index, peer)| {
590 let addresses: HashSet<_> = std::iter::once(peer.connected_addr.clone())
591 .chain(peer.listened_addrs.iter().cloned())
592 .collect();
593
594 let node_addresses = addresses
595 .iter()
596 .map(|addr| {
597 let score = self
598 .network_controller
599 .addr_info(addr)
600 .map(|addr_info| addr_info.score)
601 .unwrap_or(1);
602 let non_negative_score = if score > 0 { score as u64 } else { 0 };
603 NodeAddress {
604 address: addr.to_string(),
605 score: non_negative_score.into(),
606 }
607 })
608 .collect();
609
610 let inflight_blocks = self.sync_shared.state().read_inflight_blocks();
611 RemoteNode {
612 is_outbound: peer.is_outbound(),
613 version: peer
614 .identify_info
615 .as_ref()
616 .map(|info| info.client_version.clone())
617 .unwrap_or_else(|| "unknown".to_string()),
618 node_id: extract_peer_id(&peer.connected_addr)
619 .map(|peer_id| peer_id.to_base58())
620 .unwrap_or_default(),
621 addresses: node_addresses,
622 connected_duration: (std::time::Instant::now()
623 .saturating_duration_since(peer.connected_time)
624 .as_millis() as u64)
625 .into(),
626 last_ping_duration: peer
627 .ping_rtt
628 .map(|duration| (duration.as_millis() as u64).into()),
629 sync_state: self.sync_shared.state().peers().state.get(peer_index).map(
630 |state| PeerSyncState {
631 best_known_header_hash: state
632 .best_known_header
633 .as_ref()
634 .map(|header| header.hash().into()),
635 best_known_header_number: state
636 .best_known_header
637 .as_ref()
638 .map(|header| header.number().into()),
639 last_common_header_hash: state
640 .last_common_header
641 .as_ref()
642 .map(|header| header.hash().into()),
643 last_common_header_number: state
644 .last_common_header
645 .as_ref()
646 .map(|header| header.number().into()),
647 unknown_header_list_size: (state.unknown_header_list.len() as u64)
648 .into(),
649 inflight_count: (inflight_blocks.peer_inflight_count(*peer_index)
650 as u64)
651 .into(),
652 can_fetch_count: (inflight_blocks.peer_can_fetch_count(*peer_index)
653 as u64)
654 .into(),
655 },
656 ),
657 protocols: peer
658 .protocols
659 .iter()
660 .map(|(protocol_id, protocol_version)| RemoteNodeProtocol {
661 id: (protocol_id.value() as u64).into(),
662 version: protocol_version.clone(),
663 })
664 .collect(),
665 }
666 })
667 .collect();
668
669 Ok(peers)
670 }
671
672 fn get_banned_addresses(&self) -> Result<Vec<BannedAddr>> {
673 Ok(self
674 .network_controller
675 .get_banned_addrs()
676 .into_iter()
677 .map(|banned| BannedAddr {
678 address: banned.address.to_string(),
679 ban_until: banned.ban_until.into(),
680 ban_reason: banned.ban_reason,
681 created_at: banned.created_at.into(),
682 })
683 .collect())
684 }
685
686 fn clear_banned_addresses(&self) -> Result<()> {
687 self.network_controller.clear_banned_addrs();
688 Ok(())
689 }
690
691 fn set_ban(
692 &self,
693 address: String,
694 command: String,
695 ban_time: Option<Timestamp>,
696 absolute: Option<bool>,
697 reason: Option<String>,
698 ) -> Result<()> {
699 let ip_network = address.parse().map_err(|_| {
700 RPCError::invalid_params(format!(
701 "Expected `params[0]` to be a valid IP address, got {address}"
702 ))
703 })?;
704
705 match command.as_ref() {
706 "insert" => {
707 let ban_until = if absolute.unwrap_or(false) {
708 ban_time.unwrap_or_default().into()
709 } else {
710 unix_time_as_millis()
711 + ban_time
712 .unwrap_or_else(|| DEFAULT_BAN_DURATION.into())
713 .value()
714 };
715 self.network_controller
716 .ban(ip_network, ban_until, reason.unwrap_or_default());
717 Ok(())
718 }
719 "delete" => {
720 self.network_controller.unban(&ip_network);
721 Ok(())
722 }
723 _ => Err(RPCError::invalid_params(format!(
724 "Expected `params[1]` to be in the list [insert, delete], got {address}"
725 ))),
726 }
727 }
728
729 fn sync_state(&self) -> Result<SyncState> {
730 let chain = self.sync_shared.active_chain();
731 let shared = chain.shared();
732 let state = chain.state();
733 let (fast_time, normal_time, low_time) = state.read_inflight_blocks().division_point();
734 let best_known = state.shared_best_header();
735 let min_chain_work = {
736 let mut min_chain_work_500k_u128: [u8; 16] = [0; 16];
737 min_chain_work_500k_u128
738 .copy_from_slice(&self.sync_shared.state().min_chain_work().to_le_bytes()[..16]);
739 u128::from_le_bytes(min_chain_work_500k_u128)
740 };
741 let unverified_tip = shared.get_unverified_tip();
742 let sync_state = SyncState {
743 ibd: chain.is_initial_block_download(),
744 assume_valid_target_reached: shared.assume_valid_targets().is_none(),
745 assume_valid_target: Into::<packed::Byte32>::into(
746 shared
747 .assume_valid_target_specified()
748 .as_ref()
749 .clone()
750 .unwrap_or_default(),
751 )
752 .into(),
753 min_chain_work: min_chain_work.into(),
754 min_chain_work_reached: state.min_chain_work_ready(),
755 best_known_block_number: best_known.number().into(),
756 best_known_block_timestamp: best_known.timestamp().into(),
757 orphan_blocks_count: (self.chain_controller.orphan_blocks_len() as u64).into(),
758 inflight_blocks_count: (state.read_inflight_blocks().total_inflight_count() as u64)
759 .into(),
760 unverified_tip_number: unverified_tip.number().into(),
761 unverified_tip_hash: unverified_tip.hash().into(),
762 tip_number: chain.tip_number().into(),
763 tip_hash: chain.tip_hash().into(),
764 fast_time: fast_time.into(),
765 normal_time: normal_time.into(),
766 low_time: low_time.into(),
767 };
768
769 Ok(sync_state)
770 }
771
772 fn set_network_active(&self, state: bool) -> Result<()> {
773 self.network_controller.set_active(state);
774 Ok(())
775 }
776
777 fn add_node(&self, peer_id: String, address: String) -> Result<()> {
778 let multiaddr = address.parse::<Multiaddr>().map_err(|e| {
779 RPCError::invalid_params(format!("Error parsing address '{}': {}", address, e))
780 })?;
781
782 // Check if the parsed multiaddr already contains a peer ID
783 let multiaddr_with_peer_id = if extract_peer_id(&multiaddr).is_some() {
784 multiaddr
785 } else {
786 // Add the peer ID to the address
787 format!("{}/p2p/{}", address, peer_id)
788 .parse()
789 .map_err(|e| {
790 RPCError::invalid_params(format!(
791 "Error adding peer ID to address: {}, err: {}",
792 address, e
793 ))
794 })?
795 };
796
797 self.network_controller.add_node(multiaddr_with_peer_id);
798 Ok(())
799 }
800
801 fn remove_node(&self, peer_id: String) -> Result<()> {
802 let id = peer_id.parse().map_err(|e| {
803 RPCError::invalid_params(format!("Error parsing peer_id: {}, err: {}", peer_id, e))
804 })?;
805 self.network_controller.remove_node(&id);
806 Ok(())
807 }
808
809 fn ping_peers(&self) -> Result<()> {
810 self.network_controller.ping_peers();
811 Ok(())
812 }
813}