NAC_Blockchain/cnnl-service/src/main.rs

657 lines
21 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 CNNL HTTP 服务
//!
//! 将 CNNL 编译器封装为 RESTful HTTP API供立法 IDE、宪法沙箱等工具调用。
//!
//! # 接口列表
//! - POST /api/v1/compile - 编译 CNNL 源代码
//! - POST /api/v1/parse - 解析 CNNL 源代码(仅返回 AST
//! - POST /api/v1/validate - 验证 CNNL 语法(不生成字节码)
//! - GET /api/v1/health - 健康检查
//! - GET /api/v1/version - 版本信息
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use std::time::Instant;
use chrono::Utc;
use log::{info, warn};
// ============================================================
// 请求/响应数据结构
// ============================================================
/// 编译请求
#[derive(Debug, Deserialize)]
pub struct CompileRequest {
/// CNNL 源代码
pub source: String,
/// 是否启用形式化验证
#[serde(default)]
pub enable_verification: bool,
/// 是否生成调试信息
#[serde(default)]
pub debug_info: bool,
/// 是否生成宪法状态文件
#[serde(default = "default_true")]
pub generate_state: bool,
}
fn default_true() -> bool {
true
}
/// 解析请求
#[derive(Debug, Deserialize)]
pub struct ParseRequest {
/// CNNL 源代码
pub source: String,
}
/// 验证请求
#[derive(Debug, Deserialize)]
pub struct ValidateRequest {
/// CNNL 源代码
pub source: String,
}
/// 通用 API 响应
#[derive(Debug, Serialize)]
pub struct ApiResponse<T: Serialize> {
pub success: bool,
pub data: Option<T>,
pub error: Option<String>,
pub timestamp: String,
pub duration_ms: u64,
}
impl<T: Serialize> ApiResponse<T> {
pub fn ok(data: T, duration_ms: u64) -> Self {
Self {
success: true,
data: Some(data),
error: None,
timestamp: Utc::now().to_rfc3339(),
duration_ms,
}
}
pub fn err(msg: String, duration_ms: u64) -> ApiResponse<serde_json::Value> {
ApiResponse {
success: false,
data: None,
error: Some(msg),
timestamp: Utc::now().to_rfc3339(),
duration_ms,
}
}
}
/// 编译结果
#[derive(Debug, Serialize)]
pub struct CompileResult {
/// 字节码hex 编码)
pub bytecode_hex: String,
/// 字节码长度(字节)
pub bytecode_size: usize,
/// 宪法状态 JSON可选
pub state_json: Option<serde_json::Value>,
/// 条款数量
pub clause_count: usize,
/// 条款摘要
pub clauses: Vec<ClauseSummary>,
}
/// 解析结果
#[derive(Debug, Serialize)]
pub struct ParseResult {
/// 条款数量
pub clause_count: usize,
/// 测试块数量
pub test_count: usize,
/// 条款详情
pub clauses: Vec<ClauseDetail>,
}
/// 验证结果
#[derive(Debug, Serialize)]
pub struct ValidateResult {
/// 是否有效
pub valid: bool,
/// 错误列表
pub errors: Vec<String>,
/// 警告列表
pub warnings: Vec<String>,
/// 条款数量
pub clause_count: usize,
}
/// 条款摘要
#[derive(Debug, Serialize)]
pub struct ClauseSummary {
pub id: String,
pub level: String,
pub title: String,
pub parameter_count: usize,
pub predicate_count: usize,
pub obligation_count: usize,
}
/// 条款详情
#[derive(Debug, Serialize)]
pub struct ClauseDetail {
pub id: String,
pub level: String,
pub title: String,
pub name: Option<String>,
pub version: Option<String>,
pub description: Option<String>,
pub depends_on: Vec<String>,
pub parameters: Vec<ParameterDetail>,
pub predicates: Vec<PredicateDetail>,
pub obligations: Vec<ObligationDetail>,
}
/// 参数详情
#[derive(Debug, Serialize)]
pub struct ParameterDetail {
pub name: String,
pub type_name: String,
pub value: String,
pub description: Option<String>,
}
/// 谓词详情
#[derive(Debug, Serialize)]
pub struct PredicateDetail {
pub name: String,
pub params: Vec<(String, String)>,
pub return_type: String,
}
/// 义务详情
#[derive(Debug, Serialize)]
pub struct ObligationDetail {
pub name: String,
pub frequency: String,
pub enforcer: String,
pub penalty: String,
pub description: Option<String>,
}
/// 版本信息
#[derive(Debug, Serialize)]
pub struct VersionInfo {
pub service: String,
pub version: String,
pub compiler_version: String,
pub build_time: String,
}
/// 健康状态
#[derive(Debug, Serialize)]
pub struct HealthStatus {
pub status: String,
pub uptime_seconds: u64,
}
// ============================================================
// 全局状态
// ============================================================
pub struct AppState {
pub start_time: Instant,
}
// ============================================================
// API 处理函数
// ============================================================
/// POST /api/v1/compile - 编译 CNNL 源代码
async fn handle_compile(
_state: web::Data<AppState>,
req: web::Json<CompileRequest>,
) -> impl Responder {
let start = Instant::now();
info!("收到编译请求,源代码长度: {} 字节", req.source.len());
let options = cnnl_compiler::CompilerOptions {
enable_verification: req.enable_verification,
debug_info: req.debug_info,
output_dir: None,
generate_state_file: req.generate_state,
};
match cnnl_compiler::compile(&req.source, options) {
Ok(result) => {
let bytecode_hex = hex::encode(&result.bytecode);
let state_json = result.state_json.as_ref().and_then(|s| {
serde_json::from_str(s).ok()
});
let clauses: Vec<ClauseSummary> = result.ast.clauses.iter().map(|c| ClauseSummary {
id: c.id.clone(),
level: c.level.to_string(),
title: c.title.clone(),
parameter_count: c.parameters.len(),
predicate_count: c.predicates.len(),
obligation_count: c.obligations.len(),
}).collect();
let clause_count = clauses.len();
let compile_result = CompileResult {
bytecode_hex,
bytecode_size: result.bytecode.len(),
state_json,
clause_count,
clauses,
};
let duration_ms = start.elapsed().as_millis() as u64;
info!("编译成功,字节码大小: {} 字节,耗时: {}ms", compile_result.bytecode_size, duration_ms);
HttpResponse::Ok().json(ApiResponse::ok(compile_result, duration_ms))
}
Err(e) => {
let duration_ms = start.elapsed().as_millis() as u64;
warn!("编译失败: {}", e);
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
format!("{}", e),
duration_ms,
))
}
}
}
/// POST /api/v1/parse - 解析 CNNL 源代码
async fn handle_parse(
_state: web::Data<AppState>,
req: web::Json<ParseRequest>,
) -> impl Responder {
let start = Instant::now();
info!("收到解析请求,源代码长度: {} 字节", req.source.len());
match cnnl_compiler::Parser::new(&req.source) {
Ok(mut parser) => {
match parser.parse() {
Ok(ast) => {
let clauses: Vec<ClauseDetail> = ast.clauses.iter().map(|c| ClauseDetail {
id: c.id.clone(),
level: c.level.to_string(),
title: c.title.clone(),
name: c.name.clone(),
version: c.version.clone(),
description: c.description.clone(),
depends_on: c.depends_on.clone(),
parameters: c.parameters.iter().map(|p| ParameterDetail {
name: p.name.clone(),
type_name: p.ty.to_string(),
value: format!("{}", p.value),
description: p.description.clone(),
}).collect(),
predicates: c.predicates.iter().map(|pred| PredicateDetail {
name: pred.name.clone(),
params: pred.params.iter().map(|(n, t)| (n.clone(), t.to_string())).collect(),
return_type: pred.return_type.to_string(),
}).collect(),
obligations: c.obligations.iter().map(|o| ObligationDetail {
name: o.name.clone(),
frequency: format!("{:?}", o.frequency),
enforcer: o.enforcer.clone(),
penalty: o.penalty.clone(),
description: o.description.clone(),
}).collect(),
}).collect();
let test_count = ast.tests.len();
let clause_count = clauses.len();
let parse_result = ParseResult {
clause_count,
test_count,
clauses,
};
let duration_ms = start.elapsed().as_millis() as u64;
info!("解析成功,条款数: {},测试块数: {},耗时: {}ms", clause_count, test_count, duration_ms);
HttpResponse::Ok().json(ApiResponse::ok(parse_result, duration_ms))
}
Err(e) => {
let duration_ms = start.elapsed().as_millis() as u64;
warn!("解析失败: {}", e);
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
format!("{}", e),
duration_ms,
))
}
}
}
Err(e) => {
let duration_ms = start.elapsed().as_millis() as u64;
warn!("词法分析失败: {}", e);
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
format!("{}", e),
duration_ms,
))
}
}
}
/// POST /api/v1/validate - 验证 CNNL 语法
async fn handle_validate(
_state: web::Data<AppState>,
req: web::Json<ValidateRequest>,
) -> impl Responder {
let start = Instant::now();
info!("收到验证请求,源代码长度: {} 字节", req.source.len());
let mut errors = Vec::new();
let warnings = Vec::new();
let mut clause_count = 0;
match cnnl_compiler::Parser::new(&req.source) {
Ok(mut parser) => {
match parser.parse() {
Ok(ast) => {
clause_count = ast.clauses.len();
// 语义分析
let mut analyzer = cnnl_compiler::semantic::SemanticAnalyzer::new();
if let Err(e) = analyzer.analyze(&ast) {
errors.push(format!("语义错误: {}", e));
}
}
Err(e) => {
errors.push(format!("语法错误: {}", e));
}
}
}
Err(e) => {
errors.push(format!("词法错误: {}", e));
}
}
let valid = errors.is_empty();
let validate_result = ValidateResult {
valid,
errors,
warnings,
clause_count,
};
let duration_ms = start.elapsed().as_millis() as u64;
info!("验证完成,有效: {},耗时: {}ms", valid, duration_ms);
HttpResponse::Ok().json(ApiResponse::ok(validate_result, duration_ms))
}
/// GET /api/v1/health - 健康检查
async fn handle_health(state: web::Data<AppState>) -> impl Responder {
let uptime_seconds = state.start_time.elapsed().as_secs();
let health = HealthStatus {
status: "ok".to_string(),
uptime_seconds,
};
HttpResponse::Ok().json(ApiResponse::ok(health, 0))
}
/// GET /api/v1/version - 版本信息
async fn handle_version(_state: web::Data<AppState>) -> impl Responder {
let version = VersionInfo {
service: "nac-cnnl-service".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
compiler_version: "0.1.0".to_string(),
build_time: env!("CARGO_PKG_VERSION").to_string(),
};
HttpResponse::Ok().json(ApiResponse::ok(version, 0))
}
// ============================================================
// 根路径 - API 文档页面
// ============================================================
async fn handle_root() -> impl Responder {
let html = r#"<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NAC CNNL 编译服务</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: #0a0e1a;
color: #c9d1d9;
min-height: 100vh;
padding: 40px 20px;
}
.container { max-width: 900px; margin: 0 auto; }
.header {
text-align: center;
padding: 60px 0 40px;
border-bottom: 1px solid #21262d;
margin-bottom: 40px;
}
.logo {
font-size: 48px;
font-weight: 700;
background: linear-gradient(135deg, #58a6ff, #3fb950, #f78166);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -1px;
}
.subtitle {
color: #8b949e;
font-size: 16px;
margin-top: 12px;
}
.badge {
display: inline-block;
background: #1f6feb;
color: #fff;
font-size: 12px;
padding: 3px 10px;
border-radius: 20px;
margin-top: 10px;
font-weight: 500;
}
.section { margin-bottom: 40px; }
h2 {
font-size: 20px;
color: #e6edf3;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #21262d;
}
.endpoint-grid { display: grid; gap: 12px; }
.endpoint {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px 20px;
display: flex;
align-items: flex-start;
gap: 16px;
}
.method {
font-size: 12px;
font-weight: 700;
padding: 4px 10px;
border-radius: 4px;
min-width: 52px;
text-align: center;
flex-shrink: 0;
margin-top: 2px;
}
.method.get { background: #1a7f37; color: #3fb950; border: 1px solid #238636; }
.method.post { background: #1a4a7f; color: #58a6ff; border: 1px solid #1f6feb; }
.endpoint-info { flex: 1; }
.path {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 14px;
color: #e6edf3;
font-weight: 600;
}
.desc { color: #8b949e; font-size: 13px; margin-top: 4px; }
.code-block {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 20px;
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 13px;
overflow-x: auto;
line-height: 1.6;
}
.code-block .key { color: #79c0ff; }
.code-block .str { color: #a5d6ff; }
.code-block .num { color: #f2cc60; }
.code-block .bool { color: #ff7b72; }
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.info-card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px;
}
.info-label { color: #8b949e; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
.info-value { color: #e6edf3; font-size: 15px; font-weight: 600; margin-top: 4px; }
.footer {
text-align: center;
color: #484f58;
font-size: 13px;
padding-top: 40px;
border-top: 1px solid #21262d;
margin-top: 40px;
}
a { color: #58a6ff; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="logo">NAC CNNL</div>
<div class="subtitle">宪政神经网络语言编译服务</div>
<div class="badge">v0.1.0 · 运行中</div>
</div>
<div class="section">
<h2>API 端点</h2>
<div class="endpoint-grid">
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/compile</div>
<div class="desc">编译 CNNL 源代码,生成 NVM 字节码和宪法状态文件</div>
</div>
</div>
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/parse</div>
<div class="desc">解析 CNNL 源代码返回抽象语法树AST</div>
</div>
</div>
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/validate</div>
<div class="desc">验证 CNNL 语法正确性,不生成字节码</div>
</div>
</div>
<div class="endpoint">
<span class="method get">GET</span>
<div class="endpoint-info">
<div class="path">/api/v1/health</div>
<div class="desc">服务健康检查,返回运行状态和运行时长</div>
</div>
</div>
<div class="endpoint">
<span class="method get">GET</span>
<div class="endpoint-info">
<div class="path">/api/v1/version</div>
<div class="desc">服务版本信息</div>
</div>
</div>
</div>
</div>
<div class="section">
<h2>快速示例</h2>
<div class="code-block">
<span style="color:#8b949e"># 编译一个 CNNL 条款</span>
curl -X POST https://cnnl.newassetchain.io/api/v1/compile \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{
"source": "clause XTZH_GOLD_COVERAGE {\n level: eternal\n title: \"黄金储备覆盖率底线\"\n parameter XTZH_GOLD_COVERAGE_MIN: f64 = 1.25\n}",
"generate_state": true
}'</span>
</div>
</div>
<div class="section">
<h2>服务信息</h2>
<div class="info-grid">
<div class="info-card">
<div class="info-label">服务名称</div>
<div class="info-value">nac-cnnl-service</div>
</div>
<div class="info-card">
<div class="info-label">编译器版本</div>
<div class="info-value">CNNL v0.1.0</div>
</div>
<div class="info-card">
<div class="info-label">目标虚拟机</div>
<div class="info-value">NVM (NAC VM)</div>
</div>
<div class="info-card">
<div class="info-label">协议</div>
<div class="info-value">HTTPS / TLS 1.3</div>
</div>
</div>
</div>
<div class="footer">
<p>NAC NewAssetChain · CNNL 宪政神经网络语言 · <a href="https://git.newassetchain.io/nacadmin/NAC_Blockchain">源代码仓库</a></p>
</div>
</div>
</body>
</html>"#;
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html)
}
// ============================================================
// 主函数
// ============================================================
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
let host = std::env::var("CNNL_HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
let port: u16 = std::env::var("CNNL_PORT")
.unwrap_or_else(|_| "8080".to_string())
.parse()
.unwrap_or(8080);
info!("NAC CNNL HTTP 服务启动中...");
info!("监听地址: {}:{}", host, port);
let app_state = web::Data::new(AppState {
start_time: Instant::now(),
});
HttpServer::new(move || {
App::new()
.app_data(app_state.clone())
.app_data(web::JsonConfig::default().limit(1024 * 1024)) // 1MB 请求体限制
// API 路由
.route("/", web::get().to(handle_root))
.route("/api/v1/compile", web::post().to(handle_compile))
.route("/api/v1/parse", web::post().to(handle_parse))
.route("/api/v1/validate", web::post().to(handle_validate))
.route("/api/v1/health", web::get().to(handle_health))
.route("/api/v1/version", web::get().to(handle_version))
})
.bind(format!("{}:{}", host, port))?
.run()
.await
}