525 lines
14 KiB
Rust
525 lines
14 KiB
Rust
|
|
//! # 金元Ɉ Экономика Времени
|
|||
|
|
//!
|
|||
|
|
//! Сердце Montana. Объединяет ВСЕ модули в единую систему.
|
|||
|
|
//!
|
|||
|
|
//! ## Русские комментарии
|
|||
|
|
//! Все комментарии на русском — это эксклюзивная русская технология.
|
|||
|
|
//!
|
|||
|
|
//! ## Зависимости
|
|||
|
|
//! Этот модуль требует ВСЕ остальные модули для компиляции:
|
|||
|
|
//! - ZH: crypto, acp
|
|||
|
|
//! - EN: philosophy, cognitive
|
|||
|
|
//! - RU: network
|
|||
|
|
|
|||
|
|
// Импорт всех модулей — без любого из них код не скомпилируется
|
|||
|
|
use montana_crypto::{sha3_256, Keypair, merkle_root};
|
|||
|
|
use montana_acp::{PresenceProof, Slice, tau};
|
|||
|
|
use montana_philosophy::{Trust, Value, Identity, TemporalPrecision};
|
|||
|
|
use montana_cognitive::{CognitiveSignature, CognitiveIdentity, CouncilRole};
|
|||
|
|
use montana_network::{NetworkHealth, SignatureGossip, AddrManager};
|
|||
|
|
|
|||
|
|
/// Символ Ɉ
|
|||
|
|
pub const SYMBOL: char = 'Ɉ';
|
|||
|
|
|
|||
|
|
/// Unicode код символа
|
|||
|
|
pub const UNICODE: &str = "U+0248";
|
|||
|
|
|
|||
|
|
/// Золотой цвет символа
|
|||
|
|
pub const COLOR_GOLD: &str = "#D4A84B";
|
|||
|
|
|
|||
|
|
/// Чёрный фон
|
|||
|
|
pub const COLOR_BLACK: &str = "#000000";
|
|||
|
|
|
|||
|
|
/// Минимальная единица Ɉ (как сатоши для биткоина)
|
|||
|
|
pub const UNITS_PER_JIN: u64 = 100_000_000; // 10^8
|
|||
|
|
|
|||
|
|
/// Баланс кошелька
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct Balance {
|
|||
|
|
/// Баланс в минимальных единицах
|
|||
|
|
units: u64,
|
|||
|
|
|
|||
|
|
/// История присутствия (для доказательства)
|
|||
|
|
presence_history: Vec<PresenceCheckpoint>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Balance {
|
|||
|
|
/// Создать пустой баланс
|
|||
|
|
pub fn zero() -> Self {
|
|||
|
|
Self {
|
|||
|
|
units: 0,
|
|||
|
|
presence_history: Vec::new(),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Создать баланс из единиц
|
|||
|
|
pub fn from_units(units: u64) -> Self {
|
|||
|
|
Self {
|
|||
|
|
units,
|
|||
|
|
presence_history: Vec::new(),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить баланс в Ɉ (с дробной частью)
|
|||
|
|
pub fn as_jin(&self) -> f64 {
|
|||
|
|
self.units as f64 / UNITS_PER_JIN as f64
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить баланс в минимальных единицах
|
|||
|
|
pub fn units(&self) -> u64 {
|
|||
|
|
self.units
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Добавить присутствие
|
|||
|
|
pub fn add_presence(&mut self, checkpoint: PresenceCheckpoint) {
|
|||
|
|
self.presence_history.push(checkpoint);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Доказанное время владельца
|
|||
|
|
pub fn proven_time_seconds(&self) -> u64 {
|
|||
|
|
self.presence_history
|
|||
|
|
.iter()
|
|||
|
|
.map(|cp| cp.duration_seconds)
|
|||
|
|
.sum()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить доверие на основе истории
|
|||
|
|
pub fn trust(&self) -> Trust {
|
|||
|
|
Trust::from_evidence(self.presence_history.len() as u64)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить точность времени
|
|||
|
|
pub fn precision(&self) -> TemporalPrecision {
|
|||
|
|
TemporalPrecision::from_evidence(self.presence_history.len() as u64)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Чекпоинт присутствия
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct PresenceCheckpoint {
|
|||
|
|
/// Индекс τ₃
|
|||
|
|
pub tau3_index: u64,
|
|||
|
|
|
|||
|
|
/// Merkle root подписей
|
|||
|
|
pub presence_root: [u8; 32],
|
|||
|
|
|
|||
|
|
/// Количество подписей
|
|||
|
|
pub signature_count: u64,
|
|||
|
|
|
|||
|
|
/// Длительность в секундах
|
|||
|
|
pub duration_seconds: u64,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Расчёт эмиссии
|
|||
|
|
pub struct Emission;
|
|||
|
|
|
|||
|
|
impl Emission {
|
|||
|
|
/// Базовая эмиссия: 1 Ɉ за τ₁ присутствия
|
|||
|
|
pub const BASE_RATE: u64 = UNITS_PER_JIN; // 1 Ɉ
|
|||
|
|
|
|||
|
|
/// Рассчитать эмиссию за период
|
|||
|
|
pub fn calculate(presence_count: u64, tau3_index: u64) -> u64 {
|
|||
|
|
let base = presence_count * Self::BASE_RATE;
|
|||
|
|
let coefficient = Self::epoch_coefficient(tau3_index);
|
|||
|
|
|
|||
|
|
(base as f64 * coefficient) as u64
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Коэффициент эпохи
|
|||
|
|
/// Асимптотически стремится к 1.0
|
|||
|
|
fn epoch_coefficient(tau3_index: u64) -> f64 {
|
|||
|
|
let years = tau3_index / 26; // 26 τ₃ в году
|
|||
|
|
|
|||
|
|
match years {
|
|||
|
|
0 => 2.0, // Первый год: x2 (стимулирование)
|
|||
|
|
1 => 1.5, // Второй год: x1.5
|
|||
|
|
2 => 1.25, // Третий год: x1.25
|
|||
|
|
_ => 1.0 + 1.0 / (years as f64), // Далее: асимптотика к 1.0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Распределение эмиссии (80/20)
|
|||
|
|
#[derive(Clone, Copy, Debug)]
|
|||
|
|
pub struct Distribution {
|
|||
|
|
/// Доля Full Nodes (80%)
|
|||
|
|
pub full_node_share: f64,
|
|||
|
|
|
|||
|
|
/// Доля Verified Users (20%)
|
|||
|
|
pub verified_user_share: f64,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Default for Distribution {
|
|||
|
|
fn default() -> Self {
|
|||
|
|
Self {
|
|||
|
|
full_node_share: 0.80,
|
|||
|
|
verified_user_share: 0.20,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Distribution {
|
|||
|
|
/// Рассчитать распределение (80/20)
|
|||
|
|
pub fn calculate(&self, total_emission: u64) -> (u64, u64) {
|
|||
|
|
let full_nodes = (total_emission as f64 * self.full_node_share) as u64;
|
|||
|
|
let verified_users = total_emission - full_nodes; // Остаток
|
|||
|
|
|
|||
|
|
(full_nodes, verified_users)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// UTXO (Unspent Transaction Output)
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct Utxo {
|
|||
|
|
/// Хеш транзакции
|
|||
|
|
pub tx_hash: [u8; 32],
|
|||
|
|
|
|||
|
|
/// Индекс выхода
|
|||
|
|
pub output_index: u32,
|
|||
|
|
|
|||
|
|
/// Сумма в минимальных единицах
|
|||
|
|
pub amount: u64,
|
|||
|
|
|
|||
|
|
/// Публичный ключ владельца
|
|||
|
|
pub owner_pubkey: [u8; 32],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Транзакция
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct Transaction {
|
|||
|
|
/// Входы (ссылки на UTXO)
|
|||
|
|
pub inputs: Vec<TxInput>,
|
|||
|
|
|
|||
|
|
/// Выходы (новые UTXO)
|
|||
|
|
pub outputs: Vec<TxOutput>,
|
|||
|
|
|
|||
|
|
/// Подпись
|
|||
|
|
pub signature: [u8; 64],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Вход транзакции
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct TxInput {
|
|||
|
|
/// Хеш предыдущей транзакции
|
|||
|
|
pub prev_tx_hash: [u8; 32],
|
|||
|
|
|
|||
|
|
/// Индекс выхода
|
|||
|
|
pub output_index: u32,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Выход транзакции
|
|||
|
|
#[derive(Clone, Debug)]
|
|||
|
|
pub struct TxOutput {
|
|||
|
|
/// Сумма
|
|||
|
|
pub amount: u64,
|
|||
|
|
|
|||
|
|
/// Публичный ключ получателя
|
|||
|
|
pub recipient_pubkey: [u8; 32],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Transaction {
|
|||
|
|
/// Создать транзакцию
|
|||
|
|
pub fn create(
|
|||
|
|
inputs: Vec<TxInput>,
|
|||
|
|
outputs: Vec<TxOutput>,
|
|||
|
|
keypair: &Keypair,
|
|||
|
|
) -> Self {
|
|||
|
|
let mut tx = Self {
|
|||
|
|
inputs,
|
|||
|
|
outputs,
|
|||
|
|
signature: [0u8; 64],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Подписать транзакцию
|
|||
|
|
let message = tx.signing_message();
|
|||
|
|
tx.signature = keypair.sign(&message);
|
|||
|
|
|
|||
|
|
tx
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить сообщение для подписи
|
|||
|
|
fn signing_message(&self) -> Vec<u8> {
|
|||
|
|
let mut msg = Vec::new();
|
|||
|
|
|
|||
|
|
for input in &self.inputs {
|
|||
|
|
msg.extend_from_slice(&input.prev_tx_hash);
|
|||
|
|
msg.extend_from_slice(&input.output_index.to_le_bytes());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for output in &self.outputs {
|
|||
|
|
msg.extend_from_slice(&output.amount.to_le_bytes());
|
|||
|
|
msg.extend_from_slice(&output.recipient_pubkey);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
msg
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Вычислить хеш транзакции
|
|||
|
|
pub fn hash(&self) -> [u8; 32] {
|
|||
|
|
let mut data = self.signing_message();
|
|||
|
|
data.extend_from_slice(&self.signature);
|
|||
|
|
sha3_256(&data)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Общая сумма выходов
|
|||
|
|
pub fn total_output(&self) -> u64 {
|
|||
|
|
self.outputs.iter().map(|o| o.amount).sum()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Кошелёк Montana
|
|||
|
|
pub struct Wallet {
|
|||
|
|
/// Пара ключей
|
|||
|
|
keypair: Keypair,
|
|||
|
|
|
|||
|
|
/// Баланс
|
|||
|
|
balance: Balance,
|
|||
|
|
|
|||
|
|
/// Непотраченные выходы
|
|||
|
|
utxos: Vec<Utxo>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Wallet {
|
|||
|
|
/// Создать новый кошелёк
|
|||
|
|
pub fn new() -> Self {
|
|||
|
|
Self {
|
|||
|
|
keypair: Keypair::generate(),
|
|||
|
|
balance: Balance::zero(),
|
|||
|
|
utxos: Vec::new(),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить публичный ключ
|
|||
|
|
pub fn public_key(&self) -> [u8; 32] {
|
|||
|
|
self.keypair.public_key()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить баланс
|
|||
|
|
pub fn balance(&self) -> &Balance {
|
|||
|
|
&self.balance
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Добавить UTXO
|
|||
|
|
pub fn add_utxo(&mut self, utxo: Utxo) {
|
|||
|
|
self.balance.units += utxo.amount;
|
|||
|
|
self.utxos.push(utxo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Создать подпись присутствия
|
|||
|
|
pub fn create_presence(&self, tau1: u64, tau2_index: u64) -> PresenceProof {
|
|||
|
|
PresenceProof::create(&self.keypair, tau1, tau2_index)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Создать транзакцию
|
|||
|
|
pub fn create_transaction(
|
|||
|
|
&mut self,
|
|||
|
|
recipient: [u8; 32],
|
|||
|
|
amount: u64,
|
|||
|
|
) -> Option<Transaction> {
|
|||
|
|
// Собрать достаточно UTXO
|
|||
|
|
let mut selected_utxos = Vec::new();
|
|||
|
|
let mut total = 0u64;
|
|||
|
|
|
|||
|
|
for utxo in &self.utxos {
|
|||
|
|
if total >= amount {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
selected_utxos.push(utxo.clone());
|
|||
|
|
total += utxo.amount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if total < amount {
|
|||
|
|
return None; // Недостаточно средств
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создать входы
|
|||
|
|
let inputs: Vec<TxInput> = selected_utxos
|
|||
|
|
.iter()
|
|||
|
|
.map(|u| TxInput {
|
|||
|
|
prev_tx_hash: u.tx_hash,
|
|||
|
|
output_index: u.output_index,
|
|||
|
|
})
|
|||
|
|
.collect();
|
|||
|
|
|
|||
|
|
// Создать выходы
|
|||
|
|
let mut outputs = vec![TxOutput {
|
|||
|
|
amount,
|
|||
|
|
recipient_pubkey: recipient,
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
// Сдача
|
|||
|
|
let change = total - amount;
|
|||
|
|
if change > 0 {
|
|||
|
|
outputs.push(TxOutput {
|
|||
|
|
amount: change,
|
|||
|
|
recipient_pubkey: self.public_key(),
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Удалить использованные UTXO
|
|||
|
|
for selected in &selected_utxos {
|
|||
|
|
self.utxos.retain(|u| u.tx_hash != selected.tx_hash || u.output_index != selected.output_index);
|
|||
|
|
self.balance.units -= selected.amount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Some(Transaction::create(inputs, outputs, &self.keypair))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Default for Wallet {
|
|||
|
|
fn default() -> Self {
|
|||
|
|
Self::new()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Полная система Montana
|
|||
|
|
/// Объединяет ВСЕ компоненты
|
|||
|
|
pub struct Montana {
|
|||
|
|
/// Сеть (RU)
|
|||
|
|
pub network: MontanaNetwork,
|
|||
|
|
|
|||
|
|
/// Консенсус (ZH)
|
|||
|
|
pub consensus: MontanaConsensus,
|
|||
|
|
|
|||
|
|
/// Когнитив (EN)
|
|||
|
|
pub cognitive: MontanaCognitive,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Сетевой компонент
|
|||
|
|
pub struct MontanaNetwork {
|
|||
|
|
/// Менеджер адресов
|
|||
|
|
pub addr_manager: AddrManager,
|
|||
|
|
|
|||
|
|
/// Gossip протокол
|
|||
|
|
pub gossip: SignatureGossip,
|
|||
|
|
|
|||
|
|
/// Здоровье сети
|
|||
|
|
pub health: NetworkHealth,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Консенсус компонент
|
|||
|
|
pub struct MontanaConsensus {
|
|||
|
|
/// Текущий τ₂ индекс
|
|||
|
|
pub current_tau2: u64,
|
|||
|
|
|
|||
|
|
/// Последний слайс
|
|||
|
|
pub last_slice: Option<Slice>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Когнитивный компонент
|
|||
|
|
pub struct MontanaCognitive {
|
|||
|
|
/// Совет
|
|||
|
|
pub council: Vec<CognitiveIdentity>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Montana {
|
|||
|
|
/// Создать систему
|
|||
|
|
pub fn new() -> Self {
|
|||
|
|
Self {
|
|||
|
|
network: MontanaNetwork {
|
|||
|
|
addr_manager: AddrManager::new(),
|
|||
|
|
gossip: SignatureGossip::new(),
|
|||
|
|
health: NetworkHealth {
|
|||
|
|
соединений: 0,
|
|||
|
|
подсетей: 0,
|
|||
|
|
средняя_задержка_мс: 0,
|
|||
|
|
подписей_за_τ2: 0,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
consensus: MontanaConsensus {
|
|||
|
|
current_tau2: 0,
|
|||
|
|
last_slice: None,
|
|||
|
|
},
|
|||
|
|
cognitive: MontanaCognitive {
|
|||
|
|
council: Vec::new(),
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Проверить что система полная
|
|||
|
|
/// Все три языка/домена должны быть активны
|
|||
|
|
pub fn is_complete(&self) -> bool {
|
|||
|
|
// RU: сеть работает
|
|||
|
|
let ru_active = self.network.health.connections() > 0
|
|||
|
|
|| self.network.addr_manager.count().0 > 0;
|
|||
|
|
|
|||
|
|
// ZH: консенсус работает
|
|||
|
|
let zh_active = self.consensus.current_tau2 > 0
|
|||
|
|
|| self.consensus.last_slice.is_some();
|
|||
|
|
|
|||
|
|
// EN: когнитив работает
|
|||
|
|
let en_active = !self.cognitive.council.is_empty();
|
|||
|
|
|
|||
|
|
// Все три идентичности должны быть активны
|
|||
|
|
ru_active || zh_active || en_active
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Получить ценность системы
|
|||
|
|
pub fn value(&self) -> Value {
|
|||
|
|
let evidence = self.network.health.signatures_per_tau2() as u64;
|
|||
|
|
let age_days = self.consensus.current_tau2 / 144;
|
|||
|
|
let participants = self.network.health.connections() as u64;
|
|||
|
|
|
|||
|
|
Value::calculate(evidence, age_days, participants)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl Default for Montana {
|
|||
|
|
fn default() -> Self {
|
|||
|
|
Self::new()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Формат отображения Ɉ
|
|||
|
|
pub fn format_jin(units: u64) -> String {
|
|||
|
|
let jin = units as f64 / UNITS_PER_JIN as f64;
|
|||
|
|
format!("{:.8} {}", jin, SYMBOL)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[cfg(test)]
|
|||
|
|
mod tests {
|
|||
|
|
use super::*;
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_balance() {
|
|||
|
|
let balance = Balance::from_units(100_000_000);
|
|||
|
|
assert_eq!(balance.as_jin(), 1.0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_emission() {
|
|||
|
|
// 1000 подписей в первый год
|
|||
|
|
let emission = Emission::calculate(1000, 0);
|
|||
|
|
// Базовая эмиссия x2 = 2000 Ɉ
|
|||
|
|
assert_eq!(emission, 2000 * UNITS_PER_JIN);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_distribution() {
|
|||
|
|
let dist = Distribution::default();
|
|||
|
|
let (full_nodes, verified_users) = dist.calculate(1000);
|
|||
|
|
|
|||
|
|
assert_eq!(full_nodes, 800); // 80%
|
|||
|
|
assert_eq!(verified_users, 200); // 20%
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_wallet() {
|
|||
|
|
let wallet = Wallet::new();
|
|||
|
|
assert_eq!(wallet.balance().units(), 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_format_jin() {
|
|||
|
|
assert_eq!(format_jin(100_000_000), "1.00000000 Ɉ");
|
|||
|
|
assert_eq!(format_jin(50_000_000), "0.50000000 Ɉ");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_montana_system() {
|
|||
|
|
let montana = Montana::new();
|
|||
|
|
// Новая система ещё не полная
|
|||
|
|
assert!(!montana.is_complete());
|
|||
|
|
}
|
|||
|
|
}
|