NAC_Blockchain/nac-daemon/src/main.rs

274 lines
9.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<RwLock<DaemonState>>;
/// API 响应包装
#[derive(Serialize)]
pub struct ApiResponse<T: Serialize> {
pub success: bool,
pub data: Option<T>,
pub error: Option<String>,
pub timestamp: u64,
}
impl<T: Serialize> ApiResponse<T> {
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<SharedState>) -> 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<SharedState>) -> 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<String>,
state: web::Data<SharedState>,
) -> 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::<serde_json::Value>().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<serde_json::Value>,
state: web::Data<SharedState>,
) -> 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::<serde_json::Value>().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<serde_json::Value>,
state: web::Data<SharedState>,
) -> 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::<serde_json::Value>().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<SharedState>) -> 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<SharedState>) -> 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(())
}