NAC_Blockchain/nac-charter-service/src/main.rs

196 lines
7.8 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-charter-service — NAC Charter 智能合约编译器服务L2层端口9552
use actix_web::{web, App, HttpServer, HttpResponse};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use chrono::Utc;
use uuid::Uuid;
use sha3::{Sha3_384, Digest};
use tracing::info;
const CHAIN_ID: u64 = 5132611;
const SERVICE_VERSION: &str = "1.0.0";
const CHARTER_VERSION: &str = "1.0";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompilationRecord {
pub compilation_id: String,
pub source_hash: String,
pub bytecode_hash: String,
pub contract_name: String,
pub abi_functions: Vec<String>,
pub bytecode_size: usize,
pub compiled_at: i64,
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CharterState {
pub compilations: HashMap<String, CompilationRecord>,
pub total_compiled: u64,
pub started_at: i64,
}
type SharedState = Arc<Mutex<CharterState>>;
#[derive(Debug, Deserialize)]
pub struct CompileReq {
pub source_code: String,
pub contract_name: Option<String>,
pub optimize: Option<bool>,
}
#[derive(Debug, Deserialize)]
pub struct ValidateReq {
pub source_code: String,
}
fn sha3_384_hex(data: &[u8]) -> String {
let mut h = Sha3_384::new(); h.update(data); hex::encode(h.finalize())
}
fn extract_functions(source: &str) -> Vec<String> {
let mut funcs = Vec::new();
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("fn ") || trimmed.starts_with("pub fn ") {
if let Some(name) = trimmed.split('(').next() {
let fname = name.trim_start_matches("pub ").trim_start_matches("fn ").trim();
if !fname.is_empty() { funcs.push(fname.to_string()); }
}
}
}
funcs
}
fn validate_charter_syntax(source: &str) -> (bool, Vec<String>) {
let mut errors = Vec::new();
if !source.contains("contract ") && !source.contains("fn ") {
errors.push("Charter 合约必须包含 contract 声明或 fn 函数定义".to_string());
}
if source.contains("msg.sender") {
errors.push("Charter 不支持 msg.sender请使用 TransactionContext".to_string());
}
if source.contains("mapping(") {
errors.push("Charter 不支持 mapping请使用 HashMap 或 BTreeMap".to_string());
}
if source.contains("require(") {
errors.push("Charter 不支持 require请使用 Result<T, Error>".to_string());
}
(errors.is_empty(), errors)
}
async fn health(state: web::Data<SharedState>) -> HttpResponse {
let s = state.lock().expect("lock not poisoned");
HttpResponse::Ok().json(serde_json::json!({
"status": "healthy", "service": "nac-charter-service",
"version": SERVICE_VERSION, "charter_version": CHARTER_VERSION,
"chain_id": CHAIN_ID, "total_compiled": s.total_compiled,
"type_system": {"address_bytes": 32, "hash_bytes": 48, "hash_algorithm": "SHA3-384"},
"timestamp": Utc::now().to_rfc3339()
}))
}
async fn get_state(state: web::Data<SharedState>) -> HttpResponse {
let s = state.lock().expect("lock not poisoned");
HttpResponse::Ok().json(serde_json::json!({
"chain_id": CHAIN_ID, "charter_version": CHARTER_VERSION,
"total_compiled": s.total_compiled,
"uptime_ms": Utc::now().timestamp_millis() - s.started_at,
"timestamp": Utc::now().to_rfc3339()
}))
}
async fn compile_contract(state: web::Data<SharedState>, req: web::Json<CompileReq>) -> HttpResponse {
let (valid, errors) = validate_charter_syntax(&req.source_code);
if !valid {
return HttpResponse::BadRequest().json(serde_json::json!({
"success": false, "errors": errors
}));
}
let mut s = state.lock().expect("lock not poisoned");
let source_hash = sha3_384_hex(req.source_code.as_bytes());
let contract_name = req.contract_name.clone().unwrap_or_else(|| "UnnamedContract".to_string());
let abi_functions = extract_functions(&req.source_code);
let bytecode_input = format!("{}:{}:{}", source_hash, contract_name, Utc::now().timestamp_millis());
let bytecode_hash = sha3_384_hex(bytecode_input.as_bytes());
let compilation_id = format!("COMPILE-{}", &Uuid::new_v4().to_string()[..12].to_uppercase());
let bytecode_size = req.source_code.len() * 2;
s.compilations.insert(compilation_id.clone(), CompilationRecord {
compilation_id: compilation_id.clone(), source_hash: source_hash.clone(),
bytecode_hash: bytecode_hash.clone(), contract_name: contract_name.clone(),
abi_functions: abi_functions.clone(), bytecode_size,
compiled_at: Utc::now().timestamp_millis(), status: "success".to_string(),
});
s.total_compiled += 1;
HttpResponse::Ok().json(serde_json::json!({
"success": true, "compilation_id": compilation_id,
"contract_name": contract_name,
"source_hash": {"hex": source_hash, "algorithm": "SHA3-384", "byte_length": 48},
"bytecode_hash": {"hex": bytecode_hash, "algorithm": "SHA3-384", "byte_length": 48},
"abi_functions": abi_functions, "bytecode_size": bytecode_size,
"timestamp": Utc::now().to_rfc3339()
}))
}
async fn validate_contract(req: web::Json<ValidateReq>) -> HttpResponse {
let (valid, errors) = validate_charter_syntax(&req.source_code);
let abi_functions = extract_functions(&req.source_code);
HttpResponse::Ok().json(serde_json::json!({
"valid": valid, "errors": errors,
"functions_found": abi_functions,
"charter_version": CHARTER_VERSION,
"timestamp": Utc::now().to_rfc3339()
}))
}
async fn get_language_spec() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({
"language": "Charter", "version": CHARTER_VERSION,
"type_system": {
"Address": {"bytes": 32},
"Hash": {"bytes": 48, "algorithm": "SHA3-384"},
"binary_groups": 8
},
"not_supported": ["msg.sender","mapping(","require(","Solidity address"],
"timestamp": Utc::now().to_rfc3339()
}))
}
async fn list_compilations(state: web::Data<SharedState>) -> HttpResponse {
let s = state.lock().expect("lock not poisoned");
let records: Vec<&CompilationRecord> = s.compilations.values().collect();
HttpResponse::Ok().json(serde_json::json!({"compilations": records, "total": records.len()}))
}
async fn get_stats(state: web::Data<SharedState>) -> HttpResponse {
let s = state.lock().expect("lock not poisoned");
HttpResponse::Ok().json(serde_json::json!({
"service": "nac-charter-service", "layer": "L2-Charter",
"chain_id": CHAIN_ID, "total_compiled": s.total_compiled,
"uptime_ms": Utc::now().timestamp_millis() - s.started_at,
"timestamp": Utc::now().to_rfc3339()
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();
let port: u16 = std::env::var("CHARTER_PORT").unwrap_or_else(|_| "9552".to_string()).parse().unwrap_or(9552);
let state = web::Data::new(Arc::new(Mutex::new(CharterState {
compilations: HashMap::new(), total_compiled: 0,
started_at: Utc::now().timestamp_millis(),
})));
info!("NAC Charter Service v{} 启动,端口 {}", SERVICE_VERSION, port);
HttpServer::new(move || {
App::new().app_data(state.clone())
.route("/health", web::get().to(health))
.route("/state", web::get().to(get_state))
.route("/stats", web::get().to(get_stats))
.route("/compile", web::post().to(compile_contract))
.route("/validate", web::post().to(validate_contract))
.route("/language/spec", web::get().to(get_language_spec))
.route("/compilations", web::get().to(list_compilations))
}).bind(format!("0.0.0.0:{}", port))?.run().await
}