Skip to main content

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}