use crate::imports::*;
use convert_case::{Case, Casing};
use kaspa_rpc_core::{api::ops::RpcApiOps, *};
#[derive(Default, Handler)]
#[help("Execute RPC commands against the connected Kaspa node")]
pub struct Rpc;
impl Rpc {
fn println<T>(&self, ctx: &Arc<KaspaCli>, v: T)
where
T: core::fmt::Debug,
{
ctx.term().writeln(format!("{v:#?}").crlf());
}
async fn main(self: Arc<Self>, ctx: &Arc<dyn Context>, mut argv: Vec<String>, cmd: &str) -> Result<()> {
let ctx = ctx.clone().downcast_arc::<KaspaCli>()?;
let rpc = ctx.wallet().rpc_api().clone();
if argv.is_empty() {
return self.display_help(ctx, argv).await;
}
let op_str = argv.remove(0);
let sanitize = regex::Regex::new(r"\s*rpc\s+\S+\s+").unwrap();
let _args = sanitize.replace(cmd, "").trim().to_string();
let op_str_uc = op_str.to_case(Case::UpperCamel).to_string();
let op = RpcApiOps::from_str(op_str_uc.as_str()).ok_or(Error::custom(format!("No such rpc method: '{op_str}'")))?;
match op {
RpcApiOps::Ping => {
rpc.ping().await?;
tprintln!(ctx, "ok");
}
RpcApiOps::GetMetrics => {
let result = rpc.get_metrics(true, true, true, true).await?;
self.println(&ctx, result);
}
RpcApiOps::GetServerInfo => {
let result = rpc.get_server_info_call(GetServerInfoRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetSyncStatus => {
let result = rpc.get_sync_status_call(GetSyncStatusRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetCurrentNetwork => {
let result = rpc.get_current_network_call(GetCurrentNetworkRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetPeerAddresses => {
let result = rpc.get_peer_addresses_call(GetPeerAddressesRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetSink => {
let result = rpc.get_sink_call(GetSinkRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetMempoolEntries => {
let result = rpc
.get_mempool_entries_call(GetMempoolEntriesRequest { include_orphan_pool: true, filter_transaction_pool: true })
.await?;
self.println(&ctx, result);
}
RpcApiOps::GetConnectedPeerInfo => {
let result = rpc.get_connected_peer_info_call(GetConnectedPeerInfoRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::AddPeer => {
if argv.is_empty() {
return Err(Error::custom("Usage: rpc addpeer <ip:port> [true|false for 'is_permanent']"));
}
let peer_address = argv.remove(0).parse::<RpcContextualPeerAddress>()?;
let is_permanent = argv.remove(0).parse::<bool>().unwrap_or(false);
let result = rpc.add_peer_call(AddPeerRequest { peer_address, is_permanent }).await?;
self.println(&ctx, result);
}
RpcApiOps::GetBlock => {
if argv.is_empty() {
return Err(Error::custom("Missing block hash argument"));
}
let hash = argv.remove(0);
let hash = RpcHash::from_hex(hash.as_str())?;
let result = rpc.get_block_call(GetBlockRequest { hash, include_transactions: true }).await?;
self.println(&ctx, result);
}
RpcApiOps::GetBlockCount => {
let result = rpc.get_block_count_call(GetBlockCountRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetBlockDagInfo => {
let result = rpc.get_block_dag_info_call(GetBlockDagInfoRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::Shutdown => {
let result = rpc.shutdown_call(ShutdownRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetUtxosByAddresses => {
if argv.is_empty() {
return Err(Error::custom("Please specify at least one address"));
}
let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
let result = rpc.get_utxos_by_addresses_call(GetUtxosByAddressesRequest { addresses }).await?;
self.println(&ctx, result);
}
RpcApiOps::GetBalanceByAddress => {
if argv.is_empty() {
return Err(Error::custom("Please specify at least one address"));
}
let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
for address in addresses {
let result = rpc.get_balance_by_address_call(GetBalanceByAddressRequest { address }).await?;
self.println(&ctx, sompi_to_kaspa(result.balance));
}
}
RpcApiOps::GetBalancesByAddresses => {
if argv.is_empty() {
return Err(Error::custom("Please specify at least one address"));
}
let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
let result = rpc.get_balances_by_addresses_call(GetBalancesByAddressesRequest { addresses }).await?;
self.println(&ctx, result);
}
RpcApiOps::GetSinkBlueScore => {
let result = rpc.get_sink_blue_score_call(GetSinkBlueScoreRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::Ban => {
if argv.is_empty() {
return Err(Error::custom("Please specify peer IP address"));
}
let ip: RpcIpAddress = argv.remove(0).parse()?;
let result = rpc.ban_call(BanRequest { ip }).await?;
self.println(&ctx, result);
}
RpcApiOps::Unban => {
if argv.is_empty() {
return Err(Error::custom("Please specify peer IP address"));
}
let ip: RpcIpAddress = argv.remove(0).parse()?;
let result = rpc.unban_call(UnbanRequest { ip }).await?;
self.println(&ctx, result);
}
RpcApiOps::GetInfo => {
let result = rpc.get_info_call(GetInfoRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetMempoolEntriesByAddresses => {
if argv.is_empty() {
return Err(Error::custom("Please specify at least one address"));
}
let addresses = argv.iter().map(|s| Address::try_from(s.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
let include_orphan_pool = true;
let filter_transaction_pool = true;
let result = rpc
.get_mempool_entries_by_addresses_call(GetMempoolEntriesByAddressesRequest {
addresses,
include_orphan_pool,
filter_transaction_pool,
})
.await?;
self.println(&ctx, result);
}
RpcApiOps::GetCoinSupply => {
let result = rpc.get_coin_supply_call(GetCoinSupplyRequest {}).await?;
self.println(&ctx, result);
}
RpcApiOps::GetDaaScoreTimestampEstimate => {
if argv.is_empty() {
return Err(Error::custom("Please specify a daa_score"));
}
let daa_score_result = argv.iter().map(|s| s.parse::<u64>()).collect::<std::result::Result<Vec<_>, _>>();
match daa_score_result {
Ok(daa_scores) => {
let result =
rpc.get_daa_score_timestamp_estimate_call(GetDaaScoreTimestampEstimateRequest { daa_scores }).await?;
self.println(&ctx, result);
}
Err(_err) => {
return Err(Error::custom("Could not parse daa_scores to u64"));
}
}
}
_ => {
tprintln!(ctx, "rpc method exists but is not supported by the cli: '{op_str}'\r\n");
return Ok(());
}
}
let prefix = Regex::new(r"(?i)^\s*rpc\s+\S+\s+").unwrap();
let _req = prefix.replace(cmd, "").trim().to_string();
Ok(())
}
async fn display_help(self: Arc<Self>, ctx: Arc<KaspaCli>, _argv: Vec<String>) -> Result<()> {
let help = RpcApiOps::list()
.iter()
.filter_map(|op| op.doc().is_not_empty().then_some((op.as_str().to_case(Case::Kebab).to_string(), op.doc())))
.collect::<Vec<(_, _)>>();
ctx.term().help(&help, None)?;
tprintln!(ctx);
tprintln!(ctx, "Please note that not all listed RPC methods are currently implemented");
tprintln!(ctx);
Ok(())
}
}