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}