use ethrex_common::types::{BlockHash, ChainConfig};
use ethrex_storage::Store;
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
use tracing_subscriber::{EnvFilter, Registry, reload};
use crate::{
rpc::NodeData,
utils::{RpcErr, RpcRequest},
};
mod peers;
pub use peers::{add_peer, peer_scores, peers, sync_status};
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct NodeInfo {
enode: String,
enr: String,
id: String,
ip: String,
listen_addr: String,
name: String,
ports: Ports,
protocols: HashMap<String, Protocol>,
}
#[derive(Serialize, Debug)]
struct Ports {
discovery: u16,
listener: u16,
}
#[derive(Serialize, Debug)]
#[serde(untagged)]
enum Protocol {
Eth(EthProtocolInfo),
}
#[derive(Serialize, Debug)]
struct EthProtocolInfo {
network: u64,
genesis: BlockHash,
config: ChainConfig,
head: BlockHash,
}
pub async fn node_info(storage: Store, node_data: &NodeData) -> Result<Value, RpcErr> {
let enode_url = node_data.local_p2p_node.enode_url();
let enr_url = match node_data.local_node_record.enr_url() {
Ok(enr) => enr,
Err(_) => "".into(),
};
let chain_config = storage.get_chain_config();
let genesis_hash = storage
.get_block_header(0)
.map_err(|e| RpcErr::Internal(e.to_string()))?
.map(|h| h.hash())
.unwrap_or_default();
let head_hash = storage
.get_latest_canonical_block_hash()
.await
.map_err(|e| RpcErr::Internal(e.to_string()))?
.unwrap_or_default();
let eth_info = EthProtocolInfo {
network: chain_config.chain_id,
genesis: genesis_hash,
config: chain_config,
head: head_hash,
};
let mut protocols = HashMap::new();
protocols.insert("eth".to_string(), Protocol::Eth(eth_info));
let node_info = NodeInfo {
enode: enode_url,
enr: enr_url,
id: hex::encode(node_data.local_p2p_node.node_id()),
ip: node_data.local_p2p_node.ip.to_string(),
listen_addr: format!(
"{}:{}",
node_data.local_p2p_node.ip, node_data.local_p2p_node.tcp_port
),
name: node_data.client_version.to_string(),
ports: Ports {
discovery: node_data.local_p2p_node.udp_port,
listener: node_data.local_p2p_node.tcp_port,
},
protocols,
};
serde_json::to_value(node_info).map_err(|error| RpcErr::Internal(error.to_string()))
}
pub fn set_log_level(
req: &RpcRequest,
log_filter_handler: &Option<reload::Handle<EnvFilter, Registry>>,
) -> Result<Value, RpcErr> {
let params = req
.params
.clone()
.ok_or(RpcErr::MissingParam("log level".to_string()))?;
let log_level = params
.first()
.ok_or(RpcErr::MissingParam("log level".to_string()))?
.as_str()
.ok_or(RpcErr::WrongParam("Expected string".to_string()))?;
let filter = EnvFilter::try_new(log_level)
.map_err(|_| RpcErr::BadParams(format!("Cannot parse {log_level} as a log directive")))?;
if let Some(handle) = log_filter_handler {
handle
.reload(filter)
.map_err(|e| RpcErr::Internal(format!("Failed to reload log filter: {}", e)))?;
Ok(Value::Bool(true))
} else {
Err(RpcErr::Internal(
"Log filter handler not available".to_string(),
))
}
}