127 lines
4.4 KiB
TypeScript
127 lines
4.4 KiB
TypeScript
|
|
import { logger } from '../../utils/logger';
|
|||
|
|
import { FederatedNode, FederatedNodeService } from './FederatedNodeService';
|
|||
|
|
import { RedisService } from '../../utils/RedisService';
|
|||
|
|
|
|||
|
|
export interface P2PMessage {
|
|||
|
|
from: string;
|
|||
|
|
to: string;
|
|||
|
|
type: string;
|
|||
|
|
payload: any;
|
|||
|
|
signature: string;
|
|||
|
|
timestamp: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* [SOV_NET_06] 基于 Libp2p/WebRTC 的节点直连通信 (P2P Connectivity)
|
|||
|
|
* @description 核心逻辑:建立并管理 Hub 节点间的双向加密流。
|
|||
|
|
* 在联邦网络中支持低延迟、高可靠的 P2P 直连通信,绕过中心化中继。
|
|||
|
|
* 遵循 Autocomplete-First (V31.5) 规范。
|
|||
|
|
*/
|
|||
|
|
export class P2PConnectionService {
|
|||
|
|
private static readonly CONNECTION_CACHE_PREFIX = 'sov_net:p2p:conn:';
|
|||
|
|
private static readonly PEER_STORE_KEY = 'sov_net:p2p:peers';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化 P2P 连接监听 (DHT Discovery & PeerStore)
|
|||
|
|
*/
|
|||
|
|
static async init() {
|
|||
|
|
logger.info(`[SovNet] Initializing P2P connection listener (Libp2p/DHT mode)...`);
|
|||
|
|
|
|||
|
|
// 1. 启动 DHT 发现循环 (模拟)
|
|||
|
|
setInterval(async () => {
|
|||
|
|
await this.discoverPeersViaDHT();
|
|||
|
|
}, 60000); // 每分钟发现一次
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 对接 DHT 发现节点并同步 PeerStore (DHT Discovery)
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
private static async discoverPeersViaDHT() {
|
|||
|
|
logger.info(`[SovNet] Scanning DHT for new Hub nodes...`);
|
|||
|
|
|
|||
|
|
// 模拟从分布式哈希表发现新节点
|
|||
|
|
const activeNodes = await FederatedNodeService.getActiveNodes();
|
|||
|
|
const peerIds = activeNodes.map(n => n.nodeId);
|
|||
|
|
|
|||
|
|
await RedisService.set(this.PEER_STORE_KEY, JSON.stringify(peerIds));
|
|||
|
|
logger.info(`[SovNet] PeerStore synchronized. ${peerIds.length} peers discovered.`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 建立与远程节点的 P2P 连接 (Noise/TLS 1.3 Handshake)
|
|||
|
|
* @param targetNodeId 目标节点 ID
|
|||
|
|
*/
|
|||
|
|
static async connectToNode(targetNodeId: string): Promise<boolean> {
|
|||
|
|
logger.info(`[SovNet] Attempting P2P connection to node: ${targetNodeId}`);
|
|||
|
|
|
|||
|
|
const nodes = await FederatedNodeService.getActiveNodes();
|
|||
|
|
const targetNode = nodes.find(n => n.nodeId === targetNodeId);
|
|||
|
|
|
|||
|
|
if (!targetNode) {
|
|||
|
|
logger.error(`[SovNet] Target node ${targetNodeId} not found or offline.`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 1. 模拟 Noise Protocol / TLS 1.3 双向加密握手 (Noise-IK)
|
|||
|
|
const handshakeSuccess = await this.performP2PHandshake(targetNode);
|
|||
|
|
if (!handshakeSuccess) {
|
|||
|
|
logger.warn(`[SovNet] Handshake failed with node ${targetNodeId}. Connection rejected.`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 建立多路复用流 (Stream Multiplexing)
|
|||
|
|
await this.establishMplexStream(targetNodeId);
|
|||
|
|
|
|||
|
|
// 3. 缓存连接状态
|
|||
|
|
await RedisService.set(`${this.CONNECTION_CACHE_PREFIX}${targetNodeId}`, 'ESTABLISHED', 300);
|
|||
|
|
|
|||
|
|
logger.info(`[SovNet] P2P (Noise/TLS 1.3) connection established with node ${targetNodeId}`);
|
|||
|
|
return true;
|
|||
|
|
} catch (err: any) {
|
|||
|
|
logger.error(`[SovNet] P2P connection failed: ${err.message}`);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 建立流复用 (Stream Multiplexing)
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
private static async establishMplexStream(nodeId: string) {
|
|||
|
|
// 模拟 mplex / yamux 流复用建立
|
|||
|
|
logger.info(`[SovNet] Mplex stream multiplexing initialized for ${nodeId}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 发送 P2P 消息 (Life-cycle Management)
|
|||
|
|
* @param message 消息对象
|
|||
|
|
*/
|
|||
|
|
static async sendMessage(message: P2PMessage): Promise<boolean> {
|
|||
|
|
logger.info(`[SovNet] Sending P2P message to ${message.to} (Type: ${message.type})`);
|
|||
|
|
|
|||
|
|
const connectionStatus = await RedisService.get(`${this.CONNECTION_CACHE_PREFIX}${message.to}`);
|
|||
|
|
if (connectionStatus !== 'ESTABLISHED') {
|
|||
|
|
const reconnected = await this.connectToNode(message.to);
|
|||
|
|
if (!reconnected) return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 实际生产中通过 libp2p.pubsub 或 stream 发送
|
|||
|
|
// 模拟发送成功并更新最后活动时间
|
|||
|
|
await RedisService.set(`${this.CONNECTION_CACHE_PREFIX}${message.to}`, 'ESTABLISHED', 300);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 模拟 Noise/TLS 1.3 握手逻辑 (Noise-IK)
|
|||
|
|
* @private
|
|||
|
|
*/
|
|||
|
|
private static async performP2PHandshake(node: FederatedNode): Promise<boolean> {
|
|||
|
|
logger.info(`[SovNet] Performing Noise-IK handshake with DID: ${node.did}`);
|
|||
|
|
// 模拟验证远程节点 DID 签名与证书链
|
|||
|
|
// 生产环境中会使用 node.publicKey 进行加密挑战响应
|
|||
|
|
return node.trustScore > 0.4; // 降低一点门槛以兼容更多节点
|
|||
|
|
}
|
|||
|
|
}
|