kaspa_cli_lib/modules/
rpc.rs

1use crate::imports::*;
2use convert_case::{Case, Casing};
3use kaspa_rpc_core::api::ops::RpcApiOps;
4
5#[derive(Default, Handler)]
6#[help("Execute RPC commands against the connected Kaspa node")]
7pub struct Rpc;
8
9impl Rpc {
10    fn println<T>(&self, ctx: &Arc<KaspaCli>, v: T)
11    where
12        T: core::fmt::Debug,
13    {
14        ctx.term().writeln(format!("{v:#?}").crlf());
15    }
16
17    async fn main(self: Arc<Self>, ctx: &Arc<dyn Context>, mut argv: Vec<String>, cmd: &str) -> Result<()> {
18        let ctx = ctx.clone().downcast_arc::<KaspaCli>()?;
19        let rpc = ctx.wallet().rpc_api().clone();
20        // tprintln!(ctx, "{response}");
21
22        if argv.is_empty() {
23            return self.display_help(ctx, argv).await;
24        }
25
26        let op_str = argv.remove(0);
27
28        let sanitize = regex::Regex::new(r"\s*rpc\s+\S+\s+").unwrap();
29        let _args = sanitize.replace(cmd, "").trim().to_string();
30        let op_str_uc = op_str.to_case(Case::UpperCamel).to_string();
31        // tprintln!(ctx, "uc: '{op_str_uc}'");
32
33        let op = RpcApiOps::from_str(op_str_uc.as_str()).ok_or(Error::custom(format!("No such rpc method: '{op_str}'")))?;
34
35        match op {
36            RpcApiOps::Ping => {
37                rpc.ping().await?;
38                tprintln!(ctx, "ok");
39            }
40            RpcApiOps::GetMetrics => {
41                let result = rpc.get_metrics(true, true, true, true, true, true).await?;
42                self.println(&ctx, result);
43            }
44            RpcApiOps::GetSystemInfo => {
45                let result = rpc.get_system_info().await?;
46                self.println(&ctx, result);
47            }
48            RpcApiOps::GetConnections => {
49                let result = rpc.get_connections(true).await?;
50                self.println(&ctx, result);
51            }
52            RpcApiOps::GetServerInfo => {
53                let result = rpc.get_server_info_call(None, GetServerInfoRequest {}).await?;
54                self.println(&ctx, result);
55            }
56            RpcApiOps::GetSyncStatus => {
57                let result = rpc.get_sync_status_call(None, GetSyncStatusRequest {}).await?;
58                self.println(&ctx, result);
59            }
60            RpcApiOps::GetCurrentNetwork => {
61                let result = rpc.get_current_network_call(None, GetCurrentNetworkRequest {}).await?;
62                self.println(&ctx, result);
63            }
64            // RpcApiOps::SubmitBlock => {
65            //     let result = rpc.submit_block_call(SubmitBlockRequest {  }).await?;
66            //     self.println(&ctx, result);
67            // }
68            // RpcApiOps::GetBlockTemplate => {
69            //     let result = rpc.get_block_template_call(GetBlockTemplateRequest {  }).await?;
70            //     self.println(&ctx, result);
71            // }
72            RpcApiOps::GetPeerAddresses => {
73                let result = rpc.get_peer_addresses_call(None, GetPeerAddressesRequest {}).await?;
74                self.println(&ctx, result);
75            }
76            RpcApiOps::GetSink => {
77                let result = rpc.get_sink_call(None, GetSinkRequest {}).await?;
78                self.println(&ctx, result);
79            }
80            // RpcApiOps::GetMempoolEntry => {
81            //     let result = rpc.get_mempool_entry_call(GetMempoolEntryRequest {  }).await?;
82            //     self.println(&ctx, result);
83            // }
84            RpcApiOps::GetMempoolEntries => {
85                // TODO
86                let result = rpc
87                    .get_mempool_entries_call(
88                        None,
89                        GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: true },
90                    )
91                    .await?;
92                self.println(&ctx, result);
93            }
94            RpcApiOps::GetConnectedPeerInfo => {
95                let result = rpc.get_connected_peer_info_call(None, GetConnectedPeerInfoRequest {}).await?;
96                self.println(&ctx, result);
97            }
98            RpcApiOps::AddPeer => {
99                if argv.is_empty() {
100                    return Err(Error::custom("Usage: rpc addpeer <ip:port> [true|false for 'is_permanent']"));
101                }
102                let peer_address = argv.remove(0).parse::<RpcContextualPeerAddress>()?;
103                let is_permanent = argv.remove(0).parse::<bool>().unwrap_or(false);
104                let result = rpc.add_peer_call(None, AddPeerRequest { peer_address, is_permanent }).await?;
105                self.println(&ctx, result);
106            }
107            // RpcApiOps::SubmitTransaction => {
108            //     let result = rpc.submit_transaction_call(SubmitTransactionRequest {  }).await?;
109            //     self.println(&ctx, result);
110            // }
111            RpcApiOps::GetBlock => {
112                if argv.is_empty() {
113                    return Err(Error::custom("Missing block hash argument"));
114                }
115                let hash = argv.remove(0);
116                let hash = RpcHash::from_hex(hash.as_str())?;
117                let include_transactions = argv.first().and_then(|x| x.parse::<bool>().ok()).unwrap_or(true);
118                let result = rpc.get_block_call(None, GetBlockRequest { hash, include_transactions }).await?;
119                self.println(&ctx, result);
120            }
121            // RpcApiOps::GetSubnetwork => {
122            //     let result = rpc.get_subnetwork_call(GetSubnetworkRequest {  }).await?;
123            //     self.println(&ctx, result);
124            // }
125            RpcApiOps::GetVirtualChainFromBlock => {
126                if argv.is_empty() {
127                    return Err(Error::custom("Missing startHash argument"));
128                };
129                let start_hash = RpcHash::from_hex(argv.remove(0).as_str())?;
130                let include_accepted_transaction_ids = argv.first().and_then(|x| x.parse::<bool>().ok()).unwrap_or_default();
131                let result = rpc
132                    .get_virtual_chain_from_block_call(
133                        None,
134                        GetVirtualChainFromBlockRequest { start_hash, include_accepted_transaction_ids },
135                    )
136                    .await?;
137                self.println(&ctx, result);
138            }
139            // RpcApiOps::GetBlocks => {
140            //     let result = rpc.get_blocks_call(GetBlocksRequest {  }).await?;
141            //     self.println(&ctx, result);
142            // }
143            RpcApiOps::GetBlockCount => {
144                let result = rpc.get_block_count_call(None, GetBlockCountRequest {}).await?;
145                self.println(&ctx, result);
146            }
147            RpcApiOps::GetBlockDagInfo => {
148                let result = rpc.get_block_dag_info_call(None, GetBlockDagInfoRequest {}).await?;
149                self.println(&ctx, result);
150            }
151            // RpcApiOps::ResolveFinalityConflict => {
152            //     let result = rpc.resolve_finality_conflict_call(ResolveFinalityConflictRequest {  }).await?;
153            //     self.println(&ctx, result);
154            // }
155            RpcApiOps::Shutdown => {
156                let result = rpc.shutdown_call(None, ShutdownRequest {}).await?;
157                self.println(&ctx, result);
158            }
159            // RpcApiOps::GetHeaders => {
160            //     let result = rpc.get_headers_call(GetHeadersRequest {  }).await?;
161            //     self.println(&ctx, result);
162            // }
163            RpcApiOps::GetUtxosByAddresses => {
164                if argv.is_empty() {
165                    return Err(Error::custom("Please specify at least one address"));
166                }
167                let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
168                let result = rpc.get_utxos_by_addresses_call(None, GetUtxosByAddressesRequest { addresses }).await?;
169                self.println(&ctx, result);
170            }
171            RpcApiOps::GetBalanceByAddress => {
172                if argv.is_empty() {
173                    return Err(Error::custom("Please specify at least one address"));
174                }
175                let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
176                for address in addresses {
177                    let result = rpc.get_balance_by_address_call(None, GetBalanceByAddressRequest { address }).await?;
178                    self.println(&ctx, sompi_to_kaspa(result.balance));
179                }
180            }
181            RpcApiOps::GetBalancesByAddresses => {
182                if argv.is_empty() {
183                    return Err(Error::custom("Please specify at least one address"));
184                }
185                let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
186                let result = rpc.get_balances_by_addresses_call(None, GetBalancesByAddressesRequest { addresses }).await?;
187                self.println(&ctx, result);
188            }
189            RpcApiOps::GetSinkBlueScore => {
190                let result = rpc.get_sink_blue_score_call(None, GetSinkBlueScoreRequest {}).await?;
191                self.println(&ctx, result);
192            }
193            RpcApiOps::Ban => {
194                if argv.is_empty() {
195                    return Err(Error::custom("Please specify peer IP address"));
196                }
197                let ip: RpcIpAddress = argv.remove(0).parse()?;
198                let result = rpc.ban_call(None, BanRequest { ip }).await?;
199                self.println(&ctx, result);
200            }
201            RpcApiOps::Unban => {
202                if argv.is_empty() {
203                    return Err(Error::custom("Please specify peer IP address"));
204                }
205                let ip: RpcIpAddress = argv.remove(0).parse()?;
206                let result = rpc.unban_call(None, UnbanRequest { ip }).await?;
207                self.println(&ctx, result);
208            }
209            RpcApiOps::GetInfo => {
210                let result = rpc.get_info_call(None, GetInfoRequest {}).await?;
211                self.println(&ctx, result);
212            }
213            // RpcApiOps::EstimateNetworkHashesPerSecond => {
214            //     let result = rpc.estimate_network_hashes_per_second_call(EstimateNetworkHashesPerSecondRequest {  }).await?;
215            //     self.println(&ctx, result);
216            // }
217            RpcApiOps::GetMempoolEntriesByAddresses => {
218                if argv.is_empty() {
219                    return Err(Error::custom("Please specify at least one address"));
220                }
221                let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
222                let include_orphan_pool = true;
223                let filter_transaction_pool = true;
224                let result = rpc
225                    .get_mempool_entries_by_addresses_call(
226                        None,
227                        GetMempoolEntriesByAddressesRequest { addresses, include_orphan_pool, filter_transaction_pool },
228                    )
229                    .await?;
230                self.println(&ctx, result);
231            }
232            RpcApiOps::GetCoinSupply => {
233                let result = rpc.get_coin_supply_call(None, GetCoinSupplyRequest {}).await?;
234                self.println(&ctx, result);
235            }
236            RpcApiOps::GetDaaScoreTimestampEstimate => {
237                if argv.is_empty() {
238                    return Err(Error::custom("Please specify a daa_score"));
239                }
240                let daa_score_result = argv.iter().map(|s| s.parse::<u64>()).collect::<std::result::Result<Vec<_>, _>>();
241
242                match daa_score_result {
243                    Ok(daa_scores) => {
244                        let result = rpc
245                            .get_daa_score_timestamp_estimate_call(None, GetDaaScoreTimestampEstimateRequest { daa_scores })
246                            .await?;
247                        self.println(&ctx, result);
248                    }
249                    Err(_err) => {
250                        return Err(Error::custom("Could not parse daa_scores to u64"));
251                    }
252                }
253            }
254            RpcApiOps::GetFeeEstimate => {
255                let result = rpc.get_fee_estimate_call(None, GetFeeEstimateRequest {}).await?;
256                self.println(&ctx, result);
257            }
258            RpcApiOps::GetFeeEstimateExperimental => {
259                let verbose = if argv.is_empty() { false } else { argv.remove(0).parse().unwrap_or(false) };
260                let result = rpc.get_fee_estimate_experimental_call(None, GetFeeEstimateExperimentalRequest { verbose }).await?;
261                self.println(&ctx, result);
262            }
263            RpcApiOps::GetCurrentBlockColor => {
264                if argv.is_empty() {
265                    return Err(Error::custom("Missing block hash argument"));
266                }
267                let hash = argv.remove(0);
268                let hash = RpcHash::from_hex(hash.as_str())?;
269                let result = rpc.get_current_block_color_call(None, GetCurrentBlockColorRequest { hash }).await?;
270                self.println(&ctx, result);
271            }
272            _ => {
273                tprintln!(ctx, "rpc method exists but is not supported by the cli: '{op_str}'\r\n");
274                return Ok(());
275            }
276        }
277
278        let prefix = Regex::new(r"(?i)^\s*rpc\s+\S+\s+").unwrap();
279        let _req = prefix.replace(cmd, "").trim().to_string();
280
281        Ok(())
282    }
283
284    async fn display_help(self: Arc<Self>, ctx: Arc<KaspaCli>, _argv: Vec<String>) -> Result<()> {
285        // RpcApiOps that do not contain docs are not displayed
286        let help = RpcApiOps::into_iter()
287            .filter_map(|op| op.rustdoc().is_not_empty().then_some((op.as_str().to_case(Case::Kebab).to_string(), op.rustdoc())))
288            .collect::<Vec<(_, _)>>();
289
290        ctx.term().help(&help, None)?;
291
292        tprintln!(ctx);
293        tprintln!(ctx, "Please note that not all listed RPC methods are currently implemented");
294        tprintln!(ctx);
295
296        Ok(())
297    }
298}