// nac-daemon/src/main.rs // Issue #67: NAC公链本地守护进程 (nacd) // 提供本地 HTTP API,供开发者网站和 CLI 工具调用 use actix_web::{web, App, HttpServer, HttpResponse}; use serde::Serialize; use std::sync::Arc; use tokio::sync::RwLock; use tracing::info; mod config; mod node_status; mod wallet; mod contract; mod network; use config::DaemonConfig; use node_status::NodeStatus; /// 守护进程全局状态 pub struct DaemonState { pub config: DaemonConfig, pub node_status: NodeStatus, pub start_time: u64, } impl DaemonState { pub fn new(config: DaemonConfig) -> Self { DaemonState { config, node_status: NodeStatus::default(), start_time: chrono::Utc::now().timestamp() as u64, } } } type SharedState = Arc>; /// API 响应包装 #[derive(Serialize)] pub struct ApiResponse { pub success: bool, pub data: Option, pub error: Option, pub timestamp: u64, } impl ApiResponse { pub fn ok(data: T) -> Self { ApiResponse { success: true, data: Some(data), error: None, timestamp: chrono::Utc::now().timestamp() as u64, } } pub fn err(msg: &str) -> ApiResponse<()> { ApiResponse { success: false, data: None, error: Some(msg.to_string()), timestamp: chrono::Utc::now().timestamp() as u64, } } } // ============================================================ // API 端点 // ============================================================ /// GET /api/v1/status - 节点状态 async fn get_status(state: web::Data) -> HttpResponse { let state = state.read().await; let status = &state.node_status; HttpResponse::Ok().json(ApiResponse::ok(serde_json::json!({ "version": env!("CARGO_PKG_VERSION"), "chain": "NAC", "network": status.network_name, "block_height": status.block_height, "connected_peers": status.connected_peers, "syncing": status.is_syncing, "uptime_seconds": chrono::Utc::now().timestamp() as u64 - state.start_time, "nac_lens_endpoint": state.config.nac_lens_endpoint, "cbpp_epoch": status.current_epoch, }))) } /// GET /api/v1/node/info - 节点详细信息 async fn get_node_info(state: web::Data) -> HttpResponse { let state = state.read().await; HttpResponse::Ok().json(ApiResponse::ok(serde_json::json!({ "node_id": state.node_status.node_id, "node_type": state.config.node_type, "cbpp_role": state.node_status.cbpp_role, "csnp_address": state.config.csnp_listen_addr, "nac_lens_version": "4.0", "nvm_version": "1.0", "charter_version": "1.0", "cnnl_version": "1.0", "jurisdictions": state.node_status.active_jurisdictions, }))) } /// GET /api/v1/wallet/balance/:address - 查询余额 async fn get_balance( path: web::Path, state: web::Data, ) -> HttpResponse { let address = path.into_inner(); let state = state.read().await; // 通过 NAC_lens 查询余额 let endpoint = format!("{}/balance/{}", state.config.nac_lens_endpoint, address); match reqwest::get(&endpoint).await { Ok(resp) => { match resp.json::().await { Ok(data) => HttpResponse::Ok().json(ApiResponse::ok(data)), Err(e) => HttpResponse::InternalServerError() .json(ApiResponse::<()>::err(&e.to_string())), } } Err(_) => { // NAC_lens 不可用时返回模拟数据(开发模式) if state.config.dev_mode { HttpResponse::Ok().json(ApiResponse::ok(serde_json::json!({ "address": address, "xtzh_balance": "0", "xic_balance": "0", "nac_balance": "0", "note": "dev_mode: NAC_lens not connected" }))) } else { HttpResponse::ServiceUnavailable() .json(ApiResponse::<()>::err("NAC_lens endpoint unreachable")) } } } } /// POST /api/v1/contract/compile - 编译 Charter 合约 async fn compile_contract( body: web::Json, state: web::Data, ) -> HttpResponse { let state = state.read().await; let source = match body.get("source").and_then(|s| s.as_str()) { Some(s) => s.to_string(), None => return HttpResponse::BadRequest() .json(ApiResponse::<()>::err("缺少 source 字段")), }; // 调用 CNNL 编译服务 let cnnl_endpoint = format!("{}/api/v1/compile", state.config.cnnl_service_endpoint); let client = reqwest::Client::new(); match client.post(&cnnl_endpoint) .json(&serde_json::json!({ "source": source })) .send() .await { Ok(resp) => { match resp.json::().await { Ok(data) => HttpResponse::Ok().json(ApiResponse::ok(data)), Err(e) => HttpResponse::InternalServerError() .json(ApiResponse::<()>::err(&e.to_string())), } } Err(_) => HttpResponse::ServiceUnavailable() .json(ApiResponse::<()>::err("CNNL 编译服务不可用")), } } /// POST /api/v1/contract/validate - 验证 Charter 合约语法 async fn validate_contract( body: web::Json, state: web::Data, ) -> HttpResponse { let state = state.read().await; let source = match body.get("source").and_then(|s| s.as_str()) { Some(s) => s.to_string(), None => return HttpResponse::BadRequest() .json(ApiResponse::<()>::err("缺少 source 字段")), }; let cnnl_endpoint = format!("{}/api/v1/validate", state.config.cnnl_service_endpoint); let client = reqwest::Client::new(); match client.post(&cnnl_endpoint) .json(&serde_json::json!({ "source": source })) .send() .await { Ok(resp) => { match resp.json::().await { Ok(data) => HttpResponse::Ok().json(ApiResponse::ok(data)), Err(e) => HttpResponse::InternalServerError() .json(ApiResponse::<()>::err(&e.to_string())), } } Err(_) => HttpResponse::ServiceUnavailable() .json(ApiResponse::<()>::err("CNNL 验证服务不可用")), } } /// GET /api/v1/network/peers - 获取连接的节点列表 async fn get_peers(state: web::Data) -> HttpResponse { let state = state.read().await; HttpResponse::Ok().json(ApiResponse::ok(serde_json::json!({ "peers": state.node_status.peers, "total": state.node_status.connected_peers, }))) } /// GET /api/v1/constitution/clauses - 获取当前宪法条款 async fn get_constitution(state: web::Data) -> HttpResponse { let state = state.read().await; HttpResponse::Ok().json(ApiResponse::ok(serde_json::json!({ "version": state.node_status.constitution_version, "clause_count": 43, "last_updated_epoch": state.node_status.current_epoch, "state_hash": state.node_status.constitution_hash, }))) } /// GET /api/v1/health - 健康检查 async fn health_check() -> HttpResponse { HttpResponse::Ok().json(serde_json::json!({ "status": "ok", "service": "nac-daemon", "version": env!("CARGO_PKG_VERSION"), })) } // ============================================================ // 主函数 // ============================================================ #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); // 加载配置 let config = DaemonConfig::load()?; let listen_addr = config.listen_addr.clone(); let state = Arc::new(RwLock::new(DaemonState::new(config))); info!("NAC 守护进程启动中..."); info!("监听地址: {}", listen_addr); info!("NAC_lens 端点: {}", state.read().await.config.nac_lens_endpoint); HttpServer::new(move || { App::new() .app_data(web::Data::new(state.clone())) // 状态和节点信息 .route("/api/v1/status", web::get().to(get_status)) .route("/api/v1/node/info", web::get().to(get_node_info)) // 钱包 .route("/api/v1/wallet/balance/{address}", web::get().to(get_balance)) // 合约 .route("/api/v1/contract/compile", web::post().to(compile_contract)) .route("/api/v1/contract/validate", web::post().to(validate_contract)) // 网络 .route("/api/v1/network/peers", web::get().to(get_peers)) // 宪法 .route("/api/v1/constitution/clauses", web::get().to(get_constitution)) // 健康检查 .route("/api/v1/health", web::get().to(health_check)) }) .bind(&listen_addr)? .run() .await?; Ok(()) }