468 lines
16 KiB
Rust
468 lines
16 KiB
Rust
// NAC_Lens 4.0 辖区路由层集成模块(Issue #77)
|
||
// 将 jurisdiction_router.rs 集成到 NAC_Lens 消息分发管道
|
||
// CBPP原则:约法即是治法 | 宪法即是规则 | 参与即是共识 | 节点产生区块交易决定区块大小
|
||
// NAC_Lens 是 NAC 原生协议(原 NRPC 已统一更名为 NAC_Lens)
|
||
// 跨辖区交易:源辖区节点和目标辖区节点各自独立出具CR(参与即是共识,非多签)
|
||
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// NAC_Lens 消息类型
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub enum NacLensMessageType {
|
||
/// 单辖区交易
|
||
IntraJurisdiction {
|
||
jurisdiction: String,
|
||
},
|
||
/// 跨辖区交易(源辖区 -> 目标辖区)
|
||
CrossJurisdiction {
|
||
source_jurisdiction: String,
|
||
target_jurisdiction: String,
|
||
},
|
||
/// 辖区规则更新广播(CA签名授权,约法即是治法)
|
||
JurisdictionRuleUpdate {
|
||
jurisdiction: String,
|
||
new_version: String,
|
||
ca_signature: Vec<u8>,
|
||
},
|
||
/// 宪法收据(CR)广播(参与即是共识)
|
||
ConstitutionalReceiptBroadcast {
|
||
jurisdiction: String,
|
||
tx_hash: String,
|
||
cr_hash: String,
|
||
},
|
||
/// 辖区节点注册(GIDS辖区证明)
|
||
NodeRegistration {
|
||
jurisdiction: String,
|
||
node_id: String,
|
||
gids_proof: Vec<u8>,
|
||
},
|
||
}
|
||
|
||
/// NAC_Lens 消息信封
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct NacLensEnvelope {
|
||
/// 协议版本(NAC_Lens 4.0)
|
||
pub protocol_version: String,
|
||
/// 消息ID
|
||
pub message_id: String,
|
||
/// 消息类型
|
||
pub message_type: NacLensMessageType,
|
||
/// 消息体(序列化的交易或规则数据)
|
||
pub payload: Vec<u8>,
|
||
/// 发送节点ID
|
||
pub sender_node_id: String,
|
||
/// 时间戳(UTC Unix 毫秒)
|
||
pub timestamp: u64,
|
||
}
|
||
|
||
impl NacLensEnvelope {
|
||
pub fn new(
|
||
message_id: &str,
|
||
message_type: NacLensMessageType,
|
||
payload: Vec<u8>,
|
||
sender_node_id: &str,
|
||
) -> Self {
|
||
Self {
|
||
protocol_version: "NAC_Lens/4.0".to_string(),
|
||
message_id: message_id.to_string(),
|
||
message_type,
|
||
payload,
|
||
sender_node_id: sender_node_id.to_string(),
|
||
timestamp: 0, // 实际使用时由运行时填充
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 辖区路由表条目
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct JurisdictionRouteEntry {
|
||
/// 辖区代码
|
||
pub jurisdiction_code: String,
|
||
/// 辖区节点列表(节点ID -> 节点地址)
|
||
pub nodes: HashMap<String, String>,
|
||
/// 当前活跃规则版本
|
||
pub active_rule_version: String,
|
||
/// 路由优先级(0=最高)
|
||
pub priority: u8,
|
||
/// 是否接受跨辖区消息
|
||
pub accepts_cross_jurisdiction: bool,
|
||
}
|
||
|
||
/// NAC_Lens 辖区路由器
|
||
/// 负责将消息路由到正确的辖区节点,支持跨辖区消息转发
|
||
pub struct NacLensJurisdictionRouter {
|
||
/// 路由表(jurisdiction_code -> RouteEntry)
|
||
routing_table: HashMap<String, JurisdictionRouteEntry>,
|
||
/// 本节点所属辖区
|
||
local_jurisdiction: String,
|
||
/// 本节点ID
|
||
local_node_id: String,
|
||
}
|
||
|
||
impl NacLensJurisdictionRouter {
|
||
pub fn new(local_jurisdiction: &str, local_node_id: &str) -> Self {
|
||
Self {
|
||
routing_table: HashMap::new(),
|
||
local_jurisdiction: local_jurisdiction.to_string(),
|
||
local_node_id: local_node_id.to_string(),
|
||
}
|
||
}
|
||
|
||
/// 注册辖区路由
|
||
pub fn register_jurisdiction(&mut self, entry: JurisdictionRouteEntry) {
|
||
self.routing_table.insert(entry.jurisdiction_code.clone(), entry);
|
||
}
|
||
|
||
/// 路由消息到目标辖区节点
|
||
/// 约法即是治法:路由决策基于宪法规则,不基于节点偏好
|
||
pub fn route(&self, envelope: &NacLensEnvelope) -> RoutingDecision {
|
||
match &envelope.message_type {
|
||
NacLensMessageType::IntraJurisdiction { jurisdiction } => {
|
||
self.route_intra_jurisdiction(jurisdiction, envelope)
|
||
}
|
||
NacLensMessageType::CrossJurisdiction {
|
||
source_jurisdiction,
|
||
target_jurisdiction,
|
||
} => self.route_cross_jurisdiction(source_jurisdiction, target_jurisdiction, envelope),
|
||
NacLensMessageType::JurisdictionRuleUpdate {
|
||
jurisdiction,
|
||
ca_signature,
|
||
..
|
||
} => {
|
||
// 规则更新:广播到所有辖区节点(CA签名验证通过后立即生效)
|
||
if ca_signature.is_empty() {
|
||
return RoutingDecision::Rejected {
|
||
reason: "Rule update rejected: missing CA signature. \
|
||
Per CBPP '约法即是治法', rule updates require CA authorization."
|
||
.to_string(),
|
||
};
|
||
}
|
||
self.route_rule_update_broadcast(jurisdiction)
|
||
}
|
||
NacLensMessageType::ConstitutionalReceiptBroadcast {
|
||
jurisdiction,
|
||
tx_hash,
|
||
cr_hash,
|
||
} => {
|
||
// CR广播:广播到所有节点(参与即是共识)
|
||
RoutingDecision::Broadcast {
|
||
target_jurisdictions: self.routing_table.keys().cloned().collect(),
|
||
message_summary: format!(
|
||
"CR broadcast: jurisdiction={}, tx={}, cr={}",
|
||
jurisdiction, tx_hash, cr_hash
|
||
),
|
||
}
|
||
}
|
||
NacLensMessageType::NodeRegistration {
|
||
jurisdiction,
|
||
node_id,
|
||
gids_proof,
|
||
} => {
|
||
if gids_proof.is_empty() {
|
||
return RoutingDecision::Rejected {
|
||
reason: format!(
|
||
"Node {} registration rejected: missing GIDS proof for jurisdiction {}",
|
||
node_id, jurisdiction
|
||
),
|
||
};
|
||
}
|
||
RoutingDecision::Forward {
|
||
target_jurisdiction: jurisdiction.clone(),
|
||
target_nodes: self
|
||
.routing_table
|
||
.get(jurisdiction)
|
||
.map(|e| e.nodes.keys().cloned().collect())
|
||
.unwrap_or_default(),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 单辖区内部路由
|
||
fn route_intra_jurisdiction(
|
||
&self,
|
||
jurisdiction: &str,
|
||
_envelope: &NacLensEnvelope,
|
||
) -> RoutingDecision {
|
||
if let Some(entry) = self.routing_table.get(jurisdiction) {
|
||
RoutingDecision::Forward {
|
||
target_jurisdiction: jurisdiction.to_string(),
|
||
target_nodes: entry.nodes.keys().cloned().collect(),
|
||
}
|
||
} else {
|
||
RoutingDecision::Rejected {
|
||
reason: format!(
|
||
"Jurisdiction {} not found in routing table. \
|
||
Node must register jurisdiction before routing.",
|
||
jurisdiction
|
||
),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 跨辖区路由
|
||
/// 参与即是共识:源辖区和目标辖区各自独立处理,各自出具CR
|
||
fn route_cross_jurisdiction(
|
||
&self,
|
||
source: &str,
|
||
target: &str,
|
||
_envelope: &NacLensEnvelope,
|
||
) -> RoutingDecision {
|
||
let source_entry = self.routing_table.get(source);
|
||
let target_entry = self.routing_table.get(target);
|
||
|
||
match (source_entry, target_entry) {
|
||
(Some(_src), Some(tgt)) => {
|
||
if !tgt.accepts_cross_jurisdiction {
|
||
return RoutingDecision::Rejected {
|
||
reason: format!(
|
||
"Jurisdiction {} does not accept cross-jurisdiction messages",
|
||
target
|
||
),
|
||
};
|
||
}
|
||
// 跨辖区:同时转发到源辖区和目标辖区
|
||
// 各辖区节点独立出具CR(参与即是共识,非多签)
|
||
RoutingDecision::CrossJurisdictionForward {
|
||
source_jurisdiction: source.to_string(),
|
||
target_jurisdiction: target.to_string(),
|
||
source_nodes: self
|
||
.routing_table
|
||
.get(source)
|
||
.map(|e| e.nodes.keys().cloned().collect())
|
||
.unwrap_or_default(),
|
||
target_nodes: tgt.nodes.keys().cloned().collect(),
|
||
}
|
||
}
|
||
(None, _) => RoutingDecision::Rejected {
|
||
reason: format!("Source jurisdiction {} not registered", source),
|
||
},
|
||
(_, None) => RoutingDecision::Rejected {
|
||
reason: format!("Target jurisdiction {} not registered", target),
|
||
},
|
||
}
|
||
}
|
||
|
||
/// 规则更新广播路由
|
||
fn route_rule_update_broadcast(&self, jurisdiction: &str) -> RoutingDecision {
|
||
RoutingDecision::Broadcast {
|
||
target_jurisdictions: vec![jurisdiction.to_string()],
|
||
message_summary: format!(
|
||
"Rule update broadcast to all {} nodes (CA-authorized, effective immediately)",
|
||
jurisdiction
|
||
),
|
||
}
|
||
}
|
||
|
||
/// 获取辖区路由表统计
|
||
pub fn routing_stats(&self) -> RoutingStats {
|
||
let total_nodes: usize = self.routing_table.values().map(|e| e.nodes.len()).sum();
|
||
RoutingStats {
|
||
registered_jurisdictions: self.routing_table.len(),
|
||
total_nodes,
|
||
local_jurisdiction: self.local_jurisdiction.clone(),
|
||
local_node_id: self.local_node_id.clone(),
|
||
}
|
||
}
|
||
|
||
/// 更新辖区规则版本(CA签名授权后调用)
|
||
pub fn update_jurisdiction_version(&mut self, jurisdiction: &str, new_version: &str) -> bool {
|
||
if let Some(entry) = self.routing_table.get_mut(jurisdiction) {
|
||
entry.active_rule_version = new_version.to_string();
|
||
true
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 路由决策
|
||
#[derive(Debug, Clone)]
|
||
pub enum RoutingDecision {
|
||
/// 转发到指定辖区节点
|
||
Forward {
|
||
target_jurisdiction: String,
|
||
target_nodes: Vec<String>,
|
||
},
|
||
/// 跨辖区转发(源辖区和目标辖区各自独立处理)
|
||
CrossJurisdictionForward {
|
||
source_jurisdiction: String,
|
||
target_jurisdiction: String,
|
||
source_nodes: Vec<String>,
|
||
target_nodes: Vec<String>,
|
||
},
|
||
/// 广播到多个辖区
|
||
Broadcast {
|
||
target_jurisdictions: Vec<String>,
|
||
message_summary: String,
|
||
},
|
||
/// 拒绝路由
|
||
Rejected {
|
||
reason: String,
|
||
},
|
||
}
|
||
|
||
/// 路由统计信息
|
||
#[derive(Debug, Clone)]
|
||
pub struct RoutingStats {
|
||
pub registered_jurisdictions: usize,
|
||
pub total_nodes: usize,
|
||
pub local_jurisdiction: String,
|
||
pub local_node_id: String,
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
fn make_router() -> NacLensJurisdictionRouter {
|
||
let mut router = NacLensJurisdictionRouter::new("CN", "node_cn_001");
|
||
|
||
// 注册 CN 辖区
|
||
let mut cn_nodes = HashMap::new();
|
||
cn_nodes.insert("node_cn_001".to_string(), "192.168.1.1:8080".to_string());
|
||
cn_nodes.insert("node_cn_002".to_string(), "192.168.1.2:8080".to_string());
|
||
router.register_jurisdiction(JurisdictionRouteEntry {
|
||
jurisdiction_code: "CN".to_string(),
|
||
nodes: cn_nodes,
|
||
active_rule_version: "1.0.0".to_string(),
|
||
priority: 0,
|
||
accepts_cross_jurisdiction: true,
|
||
});
|
||
|
||
// 注册 HK 辖区
|
||
let mut hk_nodes = HashMap::new();
|
||
hk_nodes.insert("node_hk_001".to_string(), "10.0.1.1:8080".to_string());
|
||
router.register_jurisdiction(JurisdictionRouteEntry {
|
||
jurisdiction_code: "HK".to_string(),
|
||
nodes: hk_nodes,
|
||
active_rule_version: "1.0.0".to_string(),
|
||
priority: 0,
|
||
accepts_cross_jurisdiction: true,
|
||
});
|
||
|
||
router
|
||
}
|
||
|
||
#[test]
|
||
fn test_intra_jurisdiction_routing() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_001",
|
||
NacLensMessageType::IntraJurisdiction {
|
||
jurisdiction: "CN".to_string(),
|
||
},
|
||
vec![],
|
||
"node_cn_001",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
assert!(matches!(decision, RoutingDecision::Forward { .. }));
|
||
if let RoutingDecision::Forward { target_jurisdiction, target_nodes } = decision {
|
||
assert_eq!(target_jurisdiction, "CN");
|
||
assert_eq!(target_nodes.len(), 2);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_cross_jurisdiction_routing() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_002",
|
||
NacLensMessageType::CrossJurisdiction {
|
||
source_jurisdiction: "CN".to_string(),
|
||
target_jurisdiction: "HK".to_string(),
|
||
},
|
||
vec![],
|
||
"node_cn_001",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
// 跨辖区:各辖区独立处理,参与即是共识
|
||
assert!(matches!(decision, RoutingDecision::CrossJurisdictionForward { .. }));
|
||
}
|
||
|
||
#[test]
|
||
fn test_rule_update_requires_ca_signature() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_003",
|
||
NacLensMessageType::JurisdictionRuleUpdate {
|
||
jurisdiction: "CN".to_string(),
|
||
new_version: "2.0.0".to_string(),
|
||
ca_signature: vec![], // 无签名
|
||
},
|
||
vec![],
|
||
"node_cn_001",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
assert!(matches!(decision, RoutingDecision::Rejected { .. }));
|
||
}
|
||
|
||
#[test]
|
||
fn test_rule_update_with_ca_signature() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_004",
|
||
NacLensMessageType::JurisdictionRuleUpdate {
|
||
jurisdiction: "CN".to_string(),
|
||
new_version: "2.0.0".to_string(),
|
||
ca_signature: vec![1u8; 64], // 有效CA签名
|
||
},
|
||
vec![],
|
||
"node_cn_001",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
assert!(matches!(decision, RoutingDecision::Broadcast { .. }));
|
||
}
|
||
|
||
#[test]
|
||
fn test_unknown_jurisdiction_rejected() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_005",
|
||
NacLensMessageType::IntraJurisdiction {
|
||
jurisdiction: "UNKNOWN".to_string(),
|
||
},
|
||
vec![],
|
||
"node_cn_001",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
assert!(matches!(decision, RoutingDecision::Rejected { .. }));
|
||
}
|
||
|
||
#[test]
|
||
fn test_routing_stats() {
|
||
let router = make_router();
|
||
let stats = router.routing_stats();
|
||
assert_eq!(stats.registered_jurisdictions, 2);
|
||
assert_eq!(stats.total_nodes, 3);
|
||
assert_eq!(stats.local_jurisdiction, "CN");
|
||
}
|
||
|
||
#[test]
|
||
fn test_node_registration_without_gids_rejected() {
|
||
let router = make_router();
|
||
let envelope = NacLensEnvelope::new(
|
||
"msg_006",
|
||
NacLensMessageType::NodeRegistration {
|
||
jurisdiction: "CN".to_string(),
|
||
node_id: "node_new".to_string(),
|
||
gids_proof: vec![], // 无GIDS证明
|
||
},
|
||
vec![],
|
||
"node_new",
|
||
);
|
||
let decision = router.route(&envelope);
|
||
assert!(matches!(decision, RoutingDecision::Rejected { .. }));
|
||
}
|
||
|
||
#[test]
|
||
fn test_update_jurisdiction_version() {
|
||
let mut router = make_router();
|
||
let updated = router.update_jurisdiction_version("CN", "2.0.0");
|
||
assert!(updated);
|
||
let stats = router.routing_stats();
|
||
assert_eq!(stats.registered_jurisdictions, 2);
|
||
}
|
||
}
|