// acc20c_judicial.rs — ACC-20C 司法冻结与合规检查模块 // // 本模块实现 CNNL 宪法条款 A53(合规检查)和 A55(司法冻结)要求的功能。 // 作为独立文件新增,不修改现有 main.rs 的任何逻辑。 // // 新增路由: // POST /acc20c/freeze — 司法冻结包装资产(A55) // POST /acc20c/unfreeze — 解除司法冻结(A55) // POST /acc20c/compliance/check — 合规性检查(A53) // // 依赖:复用 main.rs 中已定义的 SharedState、Acc20cWrappedAsset 等类型 use actix_web::{web, HttpResponse}; use chrono::Utc; use serde::{Deserialize, Serialize}; use sha3::{Digest, Sha3_384}; use crate::{AccState, SharedState}; // ============================================================ // 请求/响应数据结构 // ============================================================ /// 司法冻结请求(CNNL 宪法条款 A55) #[derive(Debug, Deserialize)] pub struct FreezeReq { /// 要冻结的 Wrapper Token ID pub wrapper_token_id: String, /// 冻结发起方地址(必须是司法授权地址) pub authority_address: String, /// 冻结原因(司法命令编号或描述) pub reason: String, /// 司法辖区代码 pub jurisdiction: String, /// 宪法收据 ID(由 CEE 颁发,证明此冻结已通过宪法审查) pub constitutional_receipt_id: Option, } /// 解冻请求(CNNL 宪法条款 A55) #[derive(Debug, Deserialize)] pub struct UnfreezeReq { /// 要解冻的 Wrapper Token ID pub wrapper_token_id: String, /// 解冻发起方地址(必须是司法授权地址) pub authority_address: String, /// 解冻原因 pub reason: String, /// 宪法收据 ID(由 CEE 颁发) pub constitutional_receipt_id: Option, } /// 合规检查请求(CNNL 宪法条款 A53) #[derive(Debug, Deserialize)] pub struct ComplianceCheckReq { /// 要检查的 Wrapper Token ID pub wrapper_token_id: String, /// 检查类型:wrap / unwrap / transfer / valuation pub check_type: String, /// 操作发起方地址 pub initiator: String, /// 目标地址(转移时使用) pub target: Option, } /// 合规检查结果 #[derive(Debug, Serialize)] pub struct ComplianceCheckResult { pub wrapper_token_id: String, pub check_type: String, pub passed: bool, pub clauses_verified: Vec, pub failed_clause: Option, pub reason: String, pub checked_at: i64, pub constitutional_receipt_id: String, } /// 冻结/解冻操作结果 #[derive(Debug, Serialize)] pub struct JudicialActionResult { pub wrapper_token_id: String, pub action: String, pub success: bool, pub previous_status: String, pub new_status: String, pub authority_address: String, pub reason: String, pub tx_hash: String, pub jurisdiction: String, pub constitutional_receipt_id: String, pub timestamp: i64, } // ============================================================ // 内部辅助函数 // ============================================================ /// 生成 SHA3-384 哈希(NAC 原生 48 字节哈希) fn sha3_384_hex(input: &str) -> String { let mut hasher = Sha3_384::new(); hasher.update(input.as_bytes()); hex::encode(hasher.finalize()) } /// 验证地址格式(NAC 原生 32 字节地址 = 64 个十六进制字符) fn is_valid_nac_address(addr: &str) -> bool { addr.len() == 64 && addr.chars().all(|c| c.is_ascii_hexdigit()) } /// 生成宪法收据 ID(模拟 CEE 颁发) fn generate_cr_id(action: &str, token_id: &str, authority: &str) -> String { let input = format!( "CR:ACC20C:{}:{}:{}:{}", action, token_id, authority, Utc::now().timestamp_millis() ); format!("CR-{}", &sha3_384_hex(&input)[..32]) } // ============================================================ // 路由处理函数 // ============================================================ /// POST /acc20c/freeze — 司法冻结包装资产 /// 对应 CNNL 宪法条款 A55:司法冻结操作必须通过 CEE 宪法执行引擎审查 pub async fn acc20c_freeze( state: web::Data, req: web::Json, ) -> HttpResponse { // ── 1. 验证权限地址格式 ────────────────────────────────── if !is_valid_nac_address(&req.authority_address) { return HttpResponse::BadRequest().json(serde_json::json!({ "success": false, "error": "无效的权限地址格式(需要 64 位十六进制 NAC 原生地址)", "clause": "A55" })); } // ── 2. 验证冻结原因不为空 ──────────────────────────────── if req.reason.trim().is_empty() { return HttpResponse::BadRequest().json(serde_json::json!({ "success": false, "error": "冻结原因不能为空(宪法条款 A55 要求)", "clause": "A55" })); } let mut s: std::sync::MutexGuard = state.lock().unwrap(); // ── 3. 查找包装资产 ────────────────────────────────────── let asset = match s.acc20c_wrapped_assets.get(&req.wrapper_token_id) { Some(a) => a.clone(), None => { return HttpResponse::NotFound().json(serde_json::json!({ "success": false, "error": "包装资产不存在", "wrapper_token_id": req.wrapper_token_id })); } }; // ── 4. 检查资产是否已被冻结 ────────────────────────────── if asset.status == "frozen" { return HttpResponse::Conflict().json(serde_json::json!({ "success": false, "error": "资产已处于冻结状态", "wrapper_token_id": req.wrapper_token_id, "current_status": "frozen" })); } // ── 5. 检查资产是否已被销毁 ────────────────────────────── if asset.status == "burned" { return HttpResponse::Conflict().json(serde_json::json!({ "success": false, "error": "已销毁的资产不能被冻结", "wrapper_token_id": req.wrapper_token_id })); } // ── 6. 生成宪法收据 ID ─────────────────────────────────── let cr_id = req.constitutional_receipt_id.clone().unwrap_or_else(|| { generate_cr_id("FREEZE", &req.wrapper_token_id, &req.authority_address) }); // ── 7. 生成交易哈希 ────────────────────────────────────── let tx_input = format!( "ACC20C:FREEZE:{}:{}:{}:{}", req.wrapper_token_id, req.authority_address, req.reason, Utc::now().timestamp_millis() ); let tx_hash = format!("0x{}", &sha3_384_hex(&tx_input)[..64]); let previous_status = asset.status.clone(); // ── 8. 执行冻结 ────────────────────────────────────────── if let Some(a) = s.acc20c_wrapped_assets.get_mut(&req.wrapper_token_id) { a.status = "frozen".to_string(); a.constitutional_receipt_id = cr_id.clone(); } let result = JudicialActionResult { wrapper_token_id: req.wrapper_token_id.clone(), action: "freeze".to_string(), success: true, previous_status, new_status: "frozen".to_string(), authority_address: req.authority_address.clone(), reason: req.reason.clone(), tx_hash, jurisdiction: req.jurisdiction.clone(), constitutional_receipt_id: cr_id, timestamp: Utc::now().timestamp_millis(), }; HttpResponse::Ok().json(serde_json::json!({ "success": true, "result": result, "clause": "A55", "message": "资产已成功冻结,宪法条款 A55 已执行" })) } /// POST /acc20c/unfreeze — 解除司法冻结 /// 对应 CNNL 宪法条款 A55:解冻操作同样需要宪法授权 pub async fn acc20c_unfreeze( state: web::Data, req: web::Json, ) -> HttpResponse { // ── 1. 验证权限地址格式 ────────────────────────────────── if !is_valid_nac_address(&req.authority_address) { return HttpResponse::BadRequest().json(serde_json::json!({ "success": false, "error": "无效的权限地址格式(需要 64 位十六进制 NAC 原生地址)", "clause": "A55" })); } let mut s: std::sync::MutexGuard = state.lock().unwrap(); // ── 2. 查找包装资产 ────────────────────────────────────── let asset = match s.acc20c_wrapped_assets.get(&req.wrapper_token_id) { Some(a) => a.clone(), None => { return HttpResponse::NotFound().json(serde_json::json!({ "success": false, "error": "包装资产不存在", "wrapper_token_id": req.wrapper_token_id })); } }; // ── 3. 检查资产是否处于冻结状态 ────────────────────────── if asset.status != "frozen" { return HttpResponse::Conflict().json(serde_json::json!({ "success": false, "error": "资产当前不处于冻结状态,无需解冻", "wrapper_token_id": req.wrapper_token_id, "current_status": asset.status })); } // ── 4. 生成宪法收据 ID ─────────────────────────────────── let cr_id = req.constitutional_receipt_id.clone().unwrap_or_else(|| { generate_cr_id("UNFREEZE", &req.wrapper_token_id, &req.authority_address) }); // ── 5. 生成交易哈希 ────────────────────────────────────── let tx_input = format!( "ACC20C:UNFREEZE:{}:{}:{}:{}", req.wrapper_token_id, req.authority_address, req.reason, Utc::now().timestamp_millis() ); let tx_hash = format!("0x{}", &sha3_384_hex(&tx_input)[..64]); // ── 6. 执行解冻 ────────────────────────────────────────── if let Some(a) = s.acc20c_wrapped_assets.get_mut(&req.wrapper_token_id) { a.status = "active".to_string(); a.constitutional_receipt_id = cr_id.clone(); } let result = JudicialActionResult { wrapper_token_id: req.wrapper_token_id.clone(), action: "unfreeze".to_string(), success: true, previous_status: "frozen".to_string(), new_status: "active".to_string(), authority_address: req.authority_address.clone(), reason: req.reason.clone(), tx_hash, jurisdiction: asset.jurisdiction.clone(), constitutional_receipt_id: cr_id, timestamp: Utc::now().timestamp_millis(), }; HttpResponse::Ok().json(serde_json::json!({ "success": true, "result": result, "clause": "A55", "message": "资产已成功解冻,宪法条款 A55 已执行" })) } /// POST /acc20c/compliance/check — 合规性检查 /// 对应 CNNL 宪法条款 A53:所有 ACC-20C 操作前必须通过合规检查 pub async fn acc20c_compliance_check( state: web::Data, req: web::Json, ) -> HttpResponse { let s: std::sync::MutexGuard = state.lock().unwrap(); // ── 1. 查找包装资产 ────────────────────────────────────── let asset = match s.acc20c_wrapped_assets.get(&req.wrapper_token_id) { Some(a) => a.clone(), None => { return HttpResponse::NotFound().json(serde_json::json!({ "success": false, "error": "包装资产不存在", "wrapper_token_id": req.wrapper_token_id })); } }; let now = Utc::now().timestamp_millis(); let mut clauses_verified: Vec = Vec::new(); let mut passed = true; let mut failed_clause: Option = None; let mut reason = "所有宪法条款验证通过".to_string(); // ── 2. 验证 A53:资产状态检查 ──────────────────────────── if asset.status == "frozen" { passed = false; failed_clause = Some("A55".to_string()); reason = format!( "资产处于司法冻结状态,{}操作被拒绝(宪法条款 A55)", req.check_type ); } else if asset.status == "burned" { passed = false; failed_clause = Some("A53".to_string()); reason = "资产已销毁,无法执行任何操作(宪法条款 A53)".to_string(); } else { clauses_verified.push("A53".to_string()); } // ── 3. 验证 A54:解包冷却期检查(仅 unwrap 操作)──────── if passed && req.check_type == "unwrap" { if asset.unlock_timestamp > now { let remaining_secs = (asset.unlock_timestamp - now) / 1000; passed = false; failed_clause = Some("A54".to_string()); reason = format!( "解包冷却期未结束,还需等待 {} 秒(宪法条款 A54)", remaining_secs ); } else { clauses_verified.push("A54".to_string()); } } // ── 4. 验证 A56:转移目标地址格式(仅 transfer 操作)──── if passed && req.check_type == "transfer" { match &req.target { None => { passed = false; failed_clause = Some("A56".to_string()); reason = "转移操作必须提供目标地址(宪法条款 A56)".to_string(); } Some(target) => { if !is_valid_nac_address(target) { passed = false; failed_clause = Some("A56".to_string()); reason = "目标地址格式无效(需要 64 位十六进制 NAC 原生地址,宪法条款 A56)" .to_string(); } else { clauses_verified.push("A56".to_string()); } } } } // ── 5. 生成合规检查收据 ────────────────────────────────── let cr_input = format!( "CR:COMPLIANCE:{}:{}:{}:{}", req.check_type, req.wrapper_token_id, req.initiator, now ); let cr_id = format!("CR-COMP-{}", &sha3_384_hex(&cr_input)[..28]); let result = ComplianceCheckResult { wrapper_token_id: req.wrapper_token_id.clone(), check_type: req.check_type.clone(), passed, clauses_verified, failed_clause, reason, checked_at: now, constitutional_receipt_id: cr_id, }; let status_code = if passed { 200u16 } else { 403u16 }; if status_code == 200 { HttpResponse::Ok().json(serde_json::json!({ "success": true, "result": result })) } else { HttpResponse::Forbidden().json(serde_json::json!({ "success": false, "result": result })) } }