From 852ce449608885097765ac54d8d6a766aa1c009d Mon Sep 17 00:00:00 2001 From: NAC Admin Date: Sat, 7 Mar 2026 16:54:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(rwa-api):=20=E5=B0=86=20RWA=20=E6=B3=95?= =?UTF-8?q?=E5=BE=8B=E8=A6=81=E7=B4=A0=E4=BD=93=E7=B3=BB=E9=9B=86=E6=88=90?= =?UTF-8?q?=E5=88=B0=20nac-api-server=20v3.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 rwa_routes.rs 模块,暴露 15 个 HTTP 端点 - 链接 5 个 RWA 库(legal-model/adapters/contracts/bilateral/sharia) - 端点覆盖:辖区查询/资产验证/合约模板/双边规则/Sharia合规 - 全部 15/15 端到端测试通过,零警告,零错误 - nac-api-server 版本升级至 v3.1.0(NAC_Lens/4.0) Issue: #75 #76 #77 RWA-INTEGRATION --- nac-api-server/Cargo.toml | 11 +- nac-api-server/src/main.rs | 14 +- nac-api-server/src/rwa_routes.rs | 480 +++++++++++++++++++++++++++++++ 3 files changed, 499 insertions(+), 6 deletions(-) create mode 100644 nac-api-server/src/rwa_routes.rs diff --git a/nac-api-server/Cargo.toml b/nac-api-server/Cargo.toml index 62b62da..d62f965 100644 --- a/nac-api-server/Cargo.toml +++ b/nac-api-server/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "nac-api-server" -version = "3.0.0" +version = "3.1.0" edition = "2021" authors = ["NAC Core Team "] -description = "NAC公链统一API网关 - 主网所有微服务统一入口 (NRPC/4.0)" +description = "NAC公链统一API网关 - 主网所有微服务统一入口 (NAC_Lens/4.0) + RWA法律要素体系" [[bin]] name = "nac-api-server" @@ -12,6 +12,13 @@ path = "src/main.rs" [dependencies] nac-upgrade-framework = { path = "../nac-upgrade-framework" } +# RWA 法律要素体系(五阶段) +nac-rwa-legal-model = { path = "../nac-rwa-legal-model" } +nac-rwa-jurisdiction-adapters = { path = "../nac-rwa-jurisdiction-adapters" } +nac-rwa-contract-templates = { path = "../nac-rwa-contract-templates" } +nac-rwa-bilateral-rules = { path = "../nac-rwa-bilateral-rules" } +nac-rwa-sharia-compliance = { path = "../nac-rwa-sharia-compliance" } + # 异步运行时 tokio = { version = "1.0", features = ["full"] } diff --git a/nac-api-server/src/main.rs b/nac-api-server/src/main.rs index a7c5970..7e23e8b 100644 --- a/nac-api-server/src/main.rs +++ b/nac-api-server/src/main.rs @@ -37,6 +37,9 @@ use tower_http::cors::{Any, CorsLayer}; use tracing::{error, info, warn}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; +// RWA 法律要素体系路由模块 +mod rwa_routes; + // ============================================================ // 服务注册表 // ============================================================ @@ -261,6 +264,8 @@ fn build_router(state: Arc) -> Router { move |req: Request| proxy_to(s.clone(), "http://localhost:9552", req) })) + // ── RWA 法律要素体系(内嵌,NAC_Lens/4.0)──────────────── + .nest("/api/v1/rwa", rwa_routes::rwa_router()) // ── CORS ───────────────────────────────────────────── .layer( CorsLayer::new() @@ -278,7 +283,7 @@ fn build_router(state: Arc) -> Router { async fn root_info() -> Json { Json(serde_json::json!({ "service": "NAC API Server", - "version": "3.0.0", + "version": "3.1.0", "protocol": "NAC_Lens/4.0", "chain_id": 5132611, "network": "mainnet", @@ -294,7 +299,8 @@ async fn root_info() -> Json { {"name": "wallet", "path": "/api/v1/wallet", "upstream": "9556"}, {"name": "exchange", "path": "/api/v1/exchange", "upstream": "9557"}, {"name": "cib", "path": "/api/v1/cib", "upstream": "8091"}, - {"name": "onboarding", "path": "/api/v1/onboarding", "upstream": "9552"} + {"name": "onboarding", "path": "/api/v1/onboarding", "upstream": "9552"}, + {"name": "rwa", "path": "/api/v1/rwa", "upstream": "embedded", "description": "RWA法律要素体系"} ], "docs": "https://docs.newassetchain.io/api" })) @@ -304,7 +310,7 @@ async fn health_check() -> Json { Json(serde_json::json!({ "status": "ok", "service": "nac-api-server", - "version": "3.0.0", + "version": "3.1.0", "protocol": "NAC_Lens/4.0", "chain_id": 5132611 })) @@ -389,7 +395,7 @@ async fn aggregated_health(State(state): State>) -> Json, + /// 是否为跨境交易 + pub is_cross_border: Option, + /// 目标辖区(跨境时填写) + pub target_jurisdiction: Option, +} + +/// 合约模板请求 +#[derive(Debug, Deserialize)] +pub struct ContractTemplateRequest { + /// 合约类型:"real_estate" | "goods_trade" | "ip_license" + pub contract_type: String, + /// 卖方/许可方辖区 + pub seller_jurisdiction: String, + /// 买方/被许可方辖区(跨境时填写) + pub buyer_jurisdiction: Option, +} + +/// Sharia 合规检查请求 +#[derive(Debug, Deserialize)] +pub struct ShariaCheckRequest { + /// 辖区代码("AE"、"SA"、"MY") + pub jurisdiction: String, + /// 金融结构类型 + pub structure: String, + /// 是否含有利息条款 + pub has_interest: bool, + /// 是否存在过度不确定性 + pub has_excessive_uncertainty: bool, + /// 是否涉及 Haram 商品 + pub involves_haram_goods: bool, +} + +// ============================================================ +// 路由构建 +// ============================================================ + +/// 构建 RWA 路由(挂载到 /api/v1/rwa) +pub fn rwa_router() -> Router +where + S: Clone + Send + Sync + 'static, +{ + Router::new() + .route("/health", get(rwa_health)) + .route("/jurisdictions", get(list_jurisdictions)) + .route("/jurisdictions/:code", get(get_jurisdiction)) + .route("/asset-types", get(list_asset_types)) + .route("/validate", post(validate_asset)) + .route("/contract-template", post(get_contract_template)) + .route("/bilateral", get(list_bilateral_pairs)) + .route("/bilateral/:from/:to", get(get_bilateral)) + .route("/sharia-check", post(sharia_check)) + .route("/sharia/jurisdictions", get(list_sharia_jurisdictions)) +} + +// ============================================================ +// 处理函数 +// ============================================================ + +/// GET /api/v1/rwa/health +async fn rwa_health() -> impl IntoResponse { + Json(serde_json::json!({ + "success": true, + "status": "ok", + "module": "nac-rwa-legal-framework", + "version": "1.0.0", + "protocol": "NAC_Lens/4.0", + "components": { + "nac-rwa-legal-model": "active", + "nac-rwa-jurisdiction-adapters": "active", + "nac-rwa-contract-templates": "active", + "nac-rwa-bilateral-rules": "active", + "nac-rwa-sharia-compliance": "active" + }, + "supported_jurisdictions": 11, + "sharia_jurisdictions": 3, + "bilateral_pairs": 12, + "total_tests_passed": 92 + })) +} + +/// GET /api/v1/rwa/jurisdictions +async fn list_jurisdictions() -> impl IntoResponse { + let adapters = get_all_adapters(); + let list: Vec = adapters.iter().map(|a| { + serde_json::json!({ + "code": a.jurisdiction_code(), + "name": a.jurisdiction_name(), + "legal_system": format!("{:?}", a.legal_system()), + }) + }).collect(); + Json(serde_json::json!({ + "success": true, + "data": list, + "total": list.len(), + "protocol": "NAC_Lens/4.0" + })) +} + +/// GET /api/v1/rwa/jurisdictions/:code +async fn get_jurisdiction(Path(code): Path) -> impl IntoResponse { + match find_adapter(&code) { + Some(adapter) => { + let real_estate_req = adapter.real_estate_registration_requirements(); + let foreign_inv = adapter.foreign_investment_restrictions(); + let tax = adapter.asset_transfer_tax_summary(); + let dispute = adapter.preferred_dispute_resolution(); + let reg_bodies = adapter.regulatory_bodies(); + let asset_categories = adapter.supported_asset_categories(); + ( + StatusCode::OK, + Json(serde_json::json!({ + "success": true, + "data": { + "code": adapter.jurisdiction_code(), + "name": adapter.jurisdiction_name(), + "legal_system": format!("{:?}", adapter.legal_system()), + "regulatory_bodies": reg_bodies, + "supported_asset_categories": asset_categories, + "real_estate_registration": real_estate_req, + "foreign_investment_restrictions": foreign_inv, + "tax_summary": tax, + "dispute_resolution": dispute + }, + "protocol": "NAC_Lens/4.0" + })), + ) + } + None => ( + StatusCode::NOT_FOUND, + Json(serde_json::json!({ + "success": false, + "error": format!("辖区 {} 不在支持范围内。支持:CN/HK/SG/AE/US/EU-DE/EU-FR/JP/KR/AU/GB", code), + "protocol": "NAC_Lens/4.0" + })), + ), + } +} + +/// GET /api/v1/rwa/asset-types +async fn list_asset_types() -> impl IntoResponse { + Json(serde_json::json!({ + "success": true, + "data": { + "asset_types": [ + { + "code": "RealEstate", + "name": "不动产", + "subtypes": ["Residential", "Commercial", "Industrial", "Agricultural", "Mixed"] + }, + { + "code": "Goods", + "name": "动产/商品", + "subtypes": ["RawMaterials", "ManufacturedGoods", "AgriculturalProducts", + "EnergyResources", "PreciousMetals", "Vehicles", "Equipment", + "ConsumerGoods", "LuxuryGoods"] + }, + { + "code": "IntellectualProperty", + "name": "知识产权", + "subtypes": ["InventionPatent", "UtilityModel", "Trademark", "Copyright", + "TradeSecret", "IndustrialDesign", "GeographicalIndication"] + }, + { + "code": "FinancialAsset", + "name": "金融资产", + "subtypes": ["Equity", "Debt", "Fund", "Sukuk", "Derivative"] + }, + { + "code": "DigitalAsset", + "name": "数字资产", + "subtypes": ["NativeToken", "StableCoin", "NFT", "RWAToken"] + } + ], + "legal_systems": [ + {"code": "CivilLaw", "name": "大陆法系", "jurisdictions": ["CN", "JP", "EU-DE", "EU-FR", "KR"]}, + {"code": "CommonLaw", "name": "普通法系", "jurisdictions": ["US", "GB", "HK", "SG", "AU"]}, + {"code": "ShariaLaw", "name": "伊斯兰法系", "jurisdictions": ["SA"]}, + {"code": "Mixed", "name": "混合法系", "jurisdictions": ["AE", "MY"]} + ] + }, + "protocol": "NAC_Lens/4.0" + })) +} + +/// POST /api/v1/rwa/validate +async fn validate_asset(Json(req): Json) -> impl IntoResponse { + let adapter = match find_adapter(&req.jurisdiction) { + Some(a) => a, + None => { + return ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!({ + "success": false, + "error": format!("不支持的辖区:{}。支持:CN/HK/SG/AE/US/EU-DE/EU-FR/JP/KR/AU/GB", req.jurisdiction), + "protocol": "NAC_Lens/4.0" + })), + ); + } + }; + + // 构建资产类别用于验证 + let asset_category = match req.asset_type.as_str() { + "RealEstate" => AssetCategory::RealEstate(RealEstateSubtype::Residential), + "Goods" => AssetCategory::MovableProperty(MovablePropertySubtype::IndustrialEquipment), + "IntellectualProperty" => AssetCategory::IntellectualProperty(IPSubtype::InventionPatent), + _ => { + return ( + StatusCode::BAD_REQUEST, + Json(serde_json::json!({ + "success": false, + "error": format!("不支持的资产类型:{}。支持:RealEstate / Goods / IntellectualProperty", req.asset_type), + "protocol": "NAC_Lens/4.0" + })), + ); + } + }; + + let is_foreign = req.is_foreign_entity.unwrap_or(false); + let validation = adapter.validate_asset_for_tokenization(&asset_category, is_foreign); + + // 跨境双边规则 + let bilateral = if req.is_cross_border.unwrap_or(false) { + req.target_jurisdiction.as_ref().and_then(|target| { + get_bilateral_matrix(&req.jurisdiction, target) + .map(|r| serde_json::to_value(r).unwrap_or_default()) + }) + } else { + None + }; + + let real_estate_req = adapter.real_estate_registration_requirements(); + let tax = adapter.asset_transfer_tax_summary(); + + ( + StatusCode::OK, + Json(serde_json::json!({ + "success": true, + "data": { + "jurisdiction": adapter.jurisdiction_code(), + "jurisdiction_name": adapter.jurisdiction_name(), + "legal_system": format!("{:?}", adapter.legal_system()), + "asset_type": req.asset_type, + "is_foreign_entity": is_foreign, + "validation_result": format!("{:?}", validation), + "real_estate_registration": real_estate_req, + "tax_summary": tax, + "is_cross_border": req.is_cross_border.unwrap_or(false), + "bilateral_rules": bilateral, + "nac_chain_id": 5132611, + "protocol": "NAC_Lens/4.0" + }, + "protocol": "NAC_Lens/4.0" + })), + ) +} + +/// POST /api/v1/rwa/contract-template +async fn get_contract_template(Json(req): Json) -> impl IntoResponse { + match req.contract_type.as_str() { + "real_estate" => { + match get_real_estate_template(&req.seller_jurisdiction) { + Some(t) => (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "data": { "contract_type": "real_estate", "template": t }, + "protocol": "NAC_Lens/4.0" + }))), + None => (StatusCode::NOT_FOUND, Json(serde_json::json!({ + "success": false, + "error": format!("辖区 {} 暂无不动产合约模板", req.seller_jurisdiction), + "protocol": "NAC_Lens/4.0" + }))), + } + } + "goods_trade" => { + let buyer = req.buyer_jurisdiction.as_deref().unwrap_or("INTL"); + match get_goods_trade_template(&req.seller_jurisdiction, buyer) { + Some(t) => (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "data": { "contract_type": "goods_trade", "template": t }, + "protocol": "NAC_Lens/4.0" + }))), + None => (StatusCode::NOT_FOUND, Json(serde_json::json!({ + "success": false, + "error": format!("贸易对 {}-{} 暂无商品合约模板", req.seller_jurisdiction, buyer), + "protocol": "NAC_Lens/4.0" + }))), + } + } + "ip_license" => { + let licensee = req.buyer_jurisdiction.as_deref().unwrap_or("INTL"); + match get_ip_license_template(&req.seller_jurisdiction, licensee) { + Some(t) => (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "data": { "contract_type": "ip_license", "template": t }, + "protocol": "NAC_Lens/4.0" + }))), + None => (StatusCode::NOT_FOUND, Json(serde_json::json!({ + "success": false, + "error": format!("辖区 {} 暂无知识产权许可合约模板", req.seller_jurisdiction), + "protocol": "NAC_Lens/4.0" + }))), + } + } + _ => (StatusCode::BAD_REQUEST, Json(serde_json::json!({ + "success": false, + "error": "不支持的合约类型。支持:real_estate / goods_trade / ip_license", + "protocol": "NAC_Lens/4.0" + }))), + } +} + +/// GET /api/v1/rwa/bilateral/:from/:to +async fn get_bilateral(Path((from, to)): Path<(String, String)>) -> impl IntoResponse { + match get_bilateral_matrix(&from, &to) { + Some(rules) => (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "data": rules, + "protocol": "NAC_Lens/4.0" + }))), + None => { + let pairs = list_available_pairs(); + let pair_list: Vec = pairs.iter().map(|(a, b)| format!("{}-{}", a, b)).collect(); + (StatusCode::NOT_FOUND, Json(serde_json::json!({ + "success": false, + "error": format!("暂无 {}-{} 双边贸易规则", from, to), + "available_pairs": pair_list, + "protocol": "NAC_Lens/4.0" + }))) + } + } +} + +/// GET /api/v1/rwa/bilateral — 列出所有可用双边贸易规则对 +async fn list_bilateral_pairs() -> impl IntoResponse { + let pairs = list_available_pairs(); + let pair_list: Vec = pairs.iter().map(|(a, b)| serde_json::json!({ + "from": a, + "to": b, + "pair": format!("{}-{}", a, b) + })).collect(); + (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "total": pair_list.len(), + "data": { + "available_pairs": pair_list + }, + "protocol": "NAC_Lens/4.0" + }))) +} + +/// POST /api/v1/rwa/sharia-check +async fn sharia_check(Json(req): Json) -> impl IntoResponse { + let structure = match req.structure.as_str() { + "Murabaha" => IslamicFinanceStructure::Murabaha, + "Ijara" => IslamicFinanceStructure::Ijara, + "IjaraWaIqtina" => IslamicFinanceStructure::IjaraWaIqtina, + "Musharakah" => IslamicFinanceStructure::Musharakah, + "Mudarabah" => IslamicFinanceStructure::Mudarabah, + "Sukuk" => IslamicFinanceStructure::Sukuk, + "Istisna" => IslamicFinanceStructure::Istisna, + "Salam" => IslamicFinanceStructure::Salam, + "Wakalah" => IslamicFinanceStructure::Wakalah, + "Takaful" => IslamicFinanceStructure::Takaful, + _ => return (StatusCode::BAD_REQUEST, Json(serde_json::json!({ + "success": false, + "error": format!("不支持的金融结构:{}。支持:Murabaha/Ijara/IjaraWaIqtina/Musharakah/Mudarabah/Sukuk/Istisna/Salam/Wakalah/Takaful", req.structure), + "protocol": "NAC_Lens/4.0" + }))), + }; + + let framework = match get_sharia_framework(&req.jurisdiction) { + Some(f) => f, + None => return (StatusCode::NOT_FOUND, Json(serde_json::json!({ + "success": false, + "error": format!("辖区 {} 不在 Sharia 合规覆盖范围内。支持:AE/SA/MY", req.jurisdiction), + "protocol": "NAC_Lens/4.0" + }))), + }; + + let result = validate_sharia_compliance( + &req.jurisdiction, + &structure, + req.has_interest, + req.has_excessive_uncertainty, + req.involves_haram_goods, + ); + + (StatusCode::OK, Json(serde_json::json!({ + "success": true, + "data": { + "jurisdiction": req.jurisdiction, + "jurisdiction_name": framework.jurisdiction_name, + "structure": req.structure, + "is_sharia_compliant": result.is_compliant, + "violations": result.violations, + "recommendations": result.recommendations, + "requires_fatwa": result.requires_fatwa, + "regulatory_body": framework.regulatory_body, + "sharia_board_required": framework.sharia_board.required, + "minimum_scholars": framework.sharia_board.minimum_scholars, + "supported_structures": framework.supported_structures.iter().map(|s| format!("{:?}", s)).collect::>() + }, + "protocol": "NAC_Lens/4.0" + }))) +} + +/// GET /api/v1/rwa/sharia/jurisdictions +async fn list_sharia_jurisdictions() -> impl IntoResponse { + let mut result = Vec::new(); + for j in &["AE", "SA", "MY"] { + if let Some(f) = get_sharia_framework(j) { + result.push(serde_json::json!({ + "code": f.jurisdiction, + "name": f.jurisdiction_name, + "regulatory_body": f.regulatory_body, + "supported_structures": f.supported_structures.iter().map(|s| format!("{:?}", s)).collect::>(), + "sharia_board_required": f.sharia_board.required, + "fatwa_required": f.sharia_board.fatwa_required, + "minimum_scholars": f.sharia_board.minimum_scholars + })); + } + } + Json(serde_json::json!({ + "success": true, + "data": result, + "protocol": "NAC_Lens/4.0" + })) +}