montana/Русский/Сеть/исходники/lib.rs

558 lines
24 KiB
Rust
Raw Normal View History

//! # P2P Сетевой модуль
//!
//! Распространение подписей присутствия между узлами.
//!
//! ## Русский код
//! Все идентификаторы на русском, как будто писал дух русского языка.
use montana_crypto::{sha3_256 as хеш256, secure_random_bytes as случайные_байты};
use montana_acp::PresenceProof as ДоказательствоПрисутствия;
use std::collections::{HashMap, HashSet, VecDeque};
use std::net::IpAddr;
// ═══════════════════════════════════════════════════════════════════════════════
// ЛИМИТЫ СОЕДИНЕНИЙ
// ═══════════════════════════════════════════════════════════════════════════════
/// Лимиты соединений
/// Защита от перегрузки и Eclipse-атак
#[derive(Clone, Copy, Debug)]
pub struct ЛимитыСоединений {
/// Максимум входящих соединений
pub максимумходящих: usize,
/// Максимум исходящих соединений
pub максимумсходящих: usize,
/// Максимум соединений на подсеть /16
pub максимума_подсеть: usize,
/// Минимум различных подсетей
pub минимум_подсетей: usize,
}
impl Default for ЛимитыСоединений {
fn default() -> Self {
Self {
максимумходящих: 117,
максимумсходящих: 11,
максимума_подсеть: 2,
минимум_подсетей: 4,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// СЕТЕВОЙ АДРЕС
// ═══════════════════════════════════════════════════════════════════════════════
/// Сетевой адрес узла
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct СетевойАдрес {
/// IP адрес
pub адрес: IpAddr,
/// Порт
pub порт: u16,
/// Временная метка последнего контакта
pub меткаремени: u64,
/// Сервисы узла
pub сервисы: u64,
}
impl СетевойАдрес {
/// Создать новый адрес
pub fn новый(адрес: IpAddr, порт: u16) -> Self {
let меткаремени = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
Self {
адрес,
порт,
меткаремени,
сервисы: 0,
}
}
/// Проверить роутабельность адреса
pub fn маршрутизируемый(&self) -> bool {
match self.адрес {
IpAddr::V4(ip) => {
!ip.is_private()
&& !ip.is_loopback()
&& !ip.is_link_local()
&& !ip.is_broadcast()
&& !ip.is_documentation()
&& !ip.is_unspecified()
}
IpAddr::V6(ip) => {
!ip.is_loopback() && !ip.is_unspecified()
}
}
}
/// Получить ключ группы (для бакетирования)
pub fn ключ_группы(&self) -> [u8; 4] {
match self.адрес {
IpAddr::V4(ip) => {
let октеты = ip.octets();
[октеты[0], октеты[1], 0, 0]
}
IpAddr::V6(ip) => {
let сегменты = ip.segments();
let байты1 = сегменты[0].to_be_bytes();
let байты2 = сегменты[1].to_be_bytes();
[байты1[0], байты1[1], байты2[0], байты2[1]]
}
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// МЕНЕДЖЕР АДРЕСОВ
// ═══════════════════════════════════════════════════════════════════════════════
/// Менеджер адресов
/// Защита от Eclipse через криптографическое бакетирование
pub struct МенеджерАдресов {
/// Новые адреса (непроверенные)
новые: HashMap<usize, Vec<СетевойАдрес>>,
/// Проверенные адреса
проверенные: HashMap<usize, Vec<СетевойАдрес>>,
/// Секретный ключ для бакетирования
секретный_ключ: [u8; 32],
/// Количество новых бакетов
количествоовых_бакетов: usize,
/// Количество проверенных бакетов
количество_проверенных_бакетов: usize,
/// Размер бакета
размер_бакета: usize,
}
impl МенеджерАдресов {
/// Создать новый менеджер
pub fn новый() -> Self {
Self {
новые: HashMap::new(),
проверенные: HashMap::new(),
секретный_ключ: случайные_байты(),
количествоовых_бакетов: 1024,
количество_проверенных_бакетов: 256,
размер_бакета: 64,
}
}
/// Вычислить бакет для адреса
fn вычислить_бакет(&self, адрес: &СетевойАдрес, источник: &СетевойАдрес, новый: bool) -> usize {
let mut данные = Vec::new();
данные.extend_from_slice(&self.секретный_ключ);
данные.extend_from_slice(&адрес.ключ_группы());
данные.extend_from_slice(&источник.ключ_группы());
let хеш = хеш256(&данные);
let количество = if новый { self.количествоовых_бакетов } else { self.количество_проверенных_бакетов };
(u64::from_le_bytes(хеш[0..8].try_into().unwrap()) as usize) % количество
}
/// Добавить адрес в таблицу новых
pub fn добавитьовый(&mut self, адрес: СетевойАдрес, источник: &СетевойАдрес) -> bool {
if !адрес.маршрутизируемый() {
return false;
}
let бакет = self.вычислить_бакет(&адрес, источник, true);
let адреса_бакета = self.новые.entry(бакет).or_insert_with(Vec::new);
if адреса_бакета.len() >= self.размер_бакета {
if let Some(индекс_старого) = адреса_бакета
.iter()
.enumerate()
.min_by_key(|(_, а)| а.меткаремени)
.map(|(и, _)| и)
{
адреса_бакета.remove(индекс_старого);
}
}
if !адреса_бакета.contains(&адрес) {
адреса_бакета.push(адрес);
true
} else {
false
}
}
/// Отметить адрес как хороший
pub fn отметить_хорошим(&mut self, адрес: &СетевойАдрес) {
for адреса_бакета in self.новые.values_mut() {
if let Some(позиция) = адреса_бакета.iter().position(|а| а == адрес) {
адреса_бакета.remove(позиция);
break;
}
}
let пустойсточник = СетевойАдрес::новый(адрес.адрес, 0);
let бакет = self.вычислить_бакет(адрес, &пустойсточник, false);
let адреса_бакета = self.проверенные.entry(бакет).or_insert_with(Vec::new);
if адреса_бакета.len() < self.размер_бакета && !адреса_бакета.contains(адрес) {
адреса_бакета.push(адрес.clone());
}
}
/// Выбрать адрес для подключения
pub fn выбрать_для_подключения(&self) -> Option<СетевойАдрес> {
let использовать_проверенные = rand::random::<bool>();
if использовать_проверенные {
self.выбрать_из_проверенных().or_else(|| self.выбрать_из_новых())
} else {
self.выбрать_из_новых().or_else(|| self.выбрать_из_проверенных())
}
}
/// Выбрать из проверенных
fn выбрать_из_проверенных(&self) -> Option<СетевойАдрес> {
if self.проверенные.is_empty() {
return None;
}
let индекс_бакета = rand::random::<usize>() % self.количество_проверенных_бакетов;
self.проверенные.get(&индекс_бакета).and_then(|адреса| {
if адреса.is_empty() {
None
} else {
let индекс = rand::random::<usize>() % адреса.len();
Some(адреса[индекс].clone())
}
})
}
/// Выбрать из новых
fn выбрать_из_новых(&self) -> Option<СетевойАдрес> {
if self.новые.is_empty() {
return None;
}
let индекс_бакета = rand::random::<usize>() % self.количествоовых_бакетов;
self.новые.get(&индекс_бакета).and_then(|адреса| {
if адреса.is_empty() {
None
} else {
let индекс = rand::random::<usize>() % адреса.len();
Some(адреса[индекс].clone())
}
})
}
/// Получить количество адресов
pub fn количество(&self) -> (usize, usize) {
let новых: usize = self.новые.values().map(|v| v.len()).sum();
let проверенных: usize = self.проверенные.values().map(|v| v.len()).sum();
(новых, проверенных)
}
}
impl Default for МенеджерАдресов {
fn default() -> Self {
Self::новый()
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// СТАТИСТИКА ПИРА
// ═══════════════════════════════════════════════════════════════════════════════
/// Идентификатор пира
pub type ИдПира = [u8; 32];
/// Статистика пира
#[derive(Clone, Debug, Default)]
pub struct СтатистикаПира {
/// Количество валидных сообщений
pub валидных: u64,
/// Количество невалидных сообщений
pub невалидных: u64,
/// Время последнего сообщения
pub последнее_сообщение: u64,
/// Средняя задержка (мс)
pub средняяадержкас: u64,
}
// ═══════════════════════════════════════════════════════════════════════════════
// РАСПРОСТРАНЕНИЕ ПОДПИСЕЙ
// ═══════════════════════════════════════════════════════════════════════════════
/// Протокол распространения подписей
pub struct РаспространениеПодписей {
/// Очередь подписей для отправки
очередьсходящих: VecDeque<ДоказательствоПрисутствия>,
/// Фильтр виденных подписей
виденные: HashSet<[u8; 32]>,
/// Статистика по пирам
статистика_пиров: HashMap<ИдПира, СтатистикаПира>,
/// Локальный пул подписей
локальный_пул: Vec<ДоказательствоПрисутствия>,
/// Максимум подписей в пуле
максимум_пула: usize,
}
impl РаспространениеПодписей {
/// Создать новый протокол
pub fn новый() -> Self {
Self {
очередьсходящих: VecDeque::new(),
виденные: HashSet::new(),
статистика_пиров: HashMap::new(),
локальный_пул: Vec::new(),
максимум_пула: 10_000,
}
}
/// Обработать входящую подпись
pub fn при_подписи(&mut self, подпись: ДоказательствоПрисутствия, от: ИдПира, текущий_τ2: u64) -> bool {
let хеш_подписи = подпись.hash();
if self.виденные.contains(&хеш_подписи) {
return false;
}
self.виденные.insert(хеш_подписи);
if !подпись.verify(текущий_τ2) {
if let Some(статистика) = self.статистика_пиров.get_mut(&от) {
статистика.невалидных += 1;
}
return false;
}
if let Some(статистика) = self.статистика_пиров.get_mut(&от) {
статистика.валидных += 1;
статистика.последнее_сообщение = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
}
if self.локальный_пул.len() < self.максимум_пула {
self.локальный_пул.push(подпись.clone());
}
self.очередьсходящих.push_back(подпись);
true
}
/// Получить следующую подпись для отправки
pub fn следующаясходящая(&mut self) -> Option<ДоказательствоПрисутствия> {
self.очередьсходящих.pop_front()
}
/// Зарегистрировать пира
pub fn зарегистрировать_пира(&mut self, ид_пира: ИдПира) {
self.статистика_пиров.insert(ид_пира, СтатистикаПира::default());
}
/// Удалить пира
pub fn удалить_пира(&mut self, ид_пира: &ИдПира) {
self.статистика_пиров.remove(ид_пира);
}
/// Получить пул
pub fn пул(&self) -> &[ДоказательствоПрисутствия] {
&self.локальный_пул
}
/// Очистить пул
pub fn очистить_пул(&mut self) {
self.локальный_пул.clear();
self.виденные.clear();
}
/// Получить статистику пира
pub fn статистика_пира(&self, ид_пира: &ИдПира) -> Option<&СтатистикаПира> {
self.статистика_пиров.get(ид_пира)
}
}
impl Default for РаспространениеПодписей {
fn default() -> Self {
Self::новый()
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// ЗДОРОВЬЕ СЕТИ
// ═══════════════════════════════════════════════════════════════════════════════
/// Метрики здоровья сети
#[derive(Clone, Debug)]
pub struct ЗдоровьеСети {
/// Количество активных соединений
pub соединений: usize,
/// Количество различных подсетей
pub подсетей: usize,
/// Среднее время отклика (мс)
pub средняяадержкас: u64,
/// Количество подписей за последний τ₂
pub подписейа_τ2: usize,
}
impl ЗдоровьеСети {
/// Оценка здоровья сети (0.0 - 1.0)
pub fn оценка(&self) -> f64 {
let лимиты = ЛимитыСоединений::default();
let оценка_соединений = (self.соединений as f64 / 10.0).min(1.0);
let оценка_подсетей = (self.подсетей as f64 / лимиты.минимум_подсетей as f64).min(1.0);
let оценкаадержки = if self.средняяадержкас == 0 {
0.5
} else {
(500.0 / self.средняяадержкас as f64).min(1.0)
};
(оценка_соединений + оценка_подсетей + оценкаадержки) / 3.0
}
/// Сеть здорова?
pub fn здорова(&self) -> bool {
self.оценка() > 0.6
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// P2P СООБЩЕНИЯ
// ═══════════════════════════════════════════════════════════════════════════════
/// Типы P2P сообщений
#[derive(Clone, Debug)]
pub enum P2PСообщение {
/// Рукопожатие
Версия {
версия: u32,
сервисы: u64,
меткаремени: u64,
одноразовыйомер: u64,
},
/// Подпись присутствия
Присутствие(ДоказательствоПрисутствия),
/// Адреса узлов
Адреса(Vec<СетевойАдрес>),
/// Запрос адресов
ЗапросАдресов,
/// Пинг
Пинг(u64),
/// Понг
Понг(u64),
}
impl P2PСообщение {
/// Получить тип сообщения
pub fn тип(&self) -> &'static str {
match self {
Self::Версия { .. } => "ВЕРСИЯ",
Self::Присутствие(_) => "ПРИСУТСТВИЕ",
Self::Адреса(_) => "АДРЕСА",
Self::ЗапросАдресов => "ЗАПРОС_АДРЕСОВ",
Self::Пинг(_) => "ПИНГ",
Self::Понг(_) => "ПОНГ",
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// СОВМЕСТИМЫЕ ПСЕВДОНИМЫ
// ═══════════════════════════════════════════════════════════════════════════════
pub use СетевойАдрес as NetAddr;
pub use МенеджерАдресов as AddrManager;
pub use РаспространениеПодписей as SignatureGossip;
pub use ЗдоровьеСети as NetworkHealth;
// Совместимость: английские методы для экономического модуля
impl МенеджерАдресов {
pub fn new() -> Self { Self::новый() }
pub fn count(&self) -> (usize, usize) { self.количество() }
}
impl РаспространениеПодписей {
pub fn new() -> Self { Self::новый() }
}
impl ЗдоровьеСети {
pub fn connections(&self) -> usize { self.соединений }
pub fn netgroups(&self) -> usize { self.подсетей }
pub fn avg_latency_ms(&self) -> u64 { self.средняяадержкас }
pub fn signatures_per_tau2(&self) -> usize { self.подписейа_τ2 }
}
// ═══════════════════════════════════════════════════════════════════════════════
// ТЕСТЫ
// ═══════════════════════════════════════════════════════════════════════════════
#[cfg(test)]
mod тесты {
use super::*;
use std::net::Ipv4Addr;
#[test]
fn тестаршрутизируемости_адреса() {
let публичный = СетевойАдрес::новый(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 8333);
let приватный = СетевойАдрес::новый(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8333);
let локальный = СетевойАдрес::новый(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8333);
assert!(публичный.маршрутизируемый());
assert!(!приватный.маршрутизируемый());
assert!(!локальный.маршрутизируемый());
}
#[test]
fn тестенеджера_адресов() {
let mut менеджер = МенеджерАдресов::новый();
let источник = СетевойАдрес::новый(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 8333);
let адрес = СетевойАдрес::новый(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 8333);
assert!(менеджер.добавитьовый(адрес.clone(), &источник));
let (новых, _) = менеджер.количество();
assert_eq!(новых, 1);
}
#[test]
fn тест_здоровья_сети() {
let здоровье = ЗдоровьеСети {
соединений: 10,
подсетей: 5,
средняяадержкас: 100,
подписейа_τ2: 1000,
};
assert!(здоровье.здорова());
assert!(здоровье.оценка() > 0.6);
}
}