ethrex-rpc 17.0.0

JSON-RPC and Engine API server for the ethrex Ethereum execution client
Documentation
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(),
        ))
    }
}