darkfid/rpc/management.rs
1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::collections::HashSet;
20
21use async_trait::async_trait;
22use smol::lock::MutexGuard;
23use tinyjson::JsonValue;
24use tracing::debug;
25
26use darkfi::{
27 net::P2pPtr,
28 rpc::{
29 jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
30 p2p_method::HandlerP2p,
31 server::RequestHandler,
32 },
33 system::StoppableTaskPtr,
34};
35
36use crate::DarkfiNode;
37
38/// JSON-RPC `RequestHandler` for node management
39pub struct ManagementRpcHandler;
40
41#[async_trait]
42#[rustfmt::skip]
43impl RequestHandler<ManagementRpcHandler> for DarkfiNode {
44 async fn handle_request(&self, req: JsonRequest) -> JsonResult {
45 debug!(target: "darkfid::rpc::management_rpc", "--> {}", req.stringify().unwrap());
46
47 match req.method.as_str() {
48 // =======================
49 // Node management methods
50 // =======================
51 "ping" => <DarkfiNode as RequestHandler<ManagementRpcHandler>>::pong(self, req.id, req.params).await,
52 "dnet.switch" => self.dnet_switch(req.id, req.params).await,
53 "dnet.subscribe_events" => self.dnet_subscribe_events(req.id, req.params).await,
54 "p2p.get_info" => self.p2p_get_info(req.id, req.params).await,
55 _ => JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
56 }
57 }
58
59 async fn connections_mut(&self) -> MutexGuard<'life0, HashSet<StoppableTaskPtr>> {
60 self.management_rpc_connections.lock().await
61 }
62}
63
64impl HandlerP2p for DarkfiNode {
65 fn p2p(&self) -> P2pPtr {
66 self.p2p_handler.p2p.clone()
67 }
68}
69
70impl DarkfiNode {
71 // RPCAPI:
72 // Activate or deactivate dnet in the P2P stack.
73 // By sending `true`, dnet will be activated, and by sending `false` dnet
74 // will be deactivated.
75 //
76 // Returns `true` on success.
77 //
78 // --> {"jsonrpc": "2.0", "method": "dnet.switch", "params": [true], "id": 1}
79 // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
80 pub async fn dnet_switch(&self, id: u16, params: JsonValue) -> JsonResult {
81 let Some(params) = params.get::<Vec<JsonValue>>() else {
82 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
83 };
84 if params.len() != 1 || !params[0].is_bool() {
85 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
86 }
87
88 let switch = params[0].get::<bool>().unwrap();
89
90 if *switch {
91 self.p2p_handler.p2p.dnet_enable();
92 } else {
93 self.p2p_handler.p2p.dnet_disable();
94 }
95
96 JsonResponse::new(JsonValue::Boolean(true), id).into()
97 }
98
99 // RPCAPI:
100 // Initializes a subscription to P2P dnet events.
101 // Once a subscription is established, `darkfid` will send JSON-RPC
102 // notifications of new network events to the subscriber.
103 //
104 // --> {
105 // "jsonrpc": "2.0",
106 // "method": "dnet.subscribe_events",
107 // "params": [],
108 // "id": 1
109 // }
110 // <-- {
111 // "jsonrpc": "2.0",
112 // "method": "dnet.subscribe_events",
113 // "params": [
114 // {
115 // "chan": {"Channel": "Info"},
116 // "cmd": "command",
117 // "time": 1767016282
118 // }
119 // ]
120 // }
121 pub async fn dnet_subscribe_events(&self, id: u16, params: JsonValue) -> JsonResult {
122 let Some(params) = params.get::<Vec<JsonValue>>() else {
123 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
124 };
125 if !params.is_empty() {
126 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
127 }
128
129 self.subscribers.get("dnet").unwrap().clone().into()
130 }
131}