montana/Монтана-Протокол/Архив/Montana v16.1.0.md

96 KiB
Raw Blame History

Montana — Спецификация протокола

Версия: 16.1.0 (2026-04-03)

Определение

Montana — цифровой стандарт времени. Сеть независимых VDF-осцилляторов, поддерживающих единую верифицируемую временную шкалу с криптографическим доказательством каждого момента. Каждая зарегистрированная секунда = 1 TimeCoin (Ɉ).

NTP говорит «сейчас 14:32» — и ты доверяешь серверу. Montana говорит «сейчас 14:32, вот криптографическое доказательство, и вот что произошло в этот момент» — и ты проверяешь сам.

Основная функция — хронометраж. Вторичная — передача ценности.

Консенсус: Proof of Time (PoT) — три цепочки. TimeChain: глобальные часы (D последовательных SHA-256 = одно окно). NodeChain: персональная цепочка узла (доказательство присутствия при каждом тике). Account: состояние счёта. Влияние узла = длина его NodeChain. Протокол не использует время для консенсуса — протокол и есть ход времени, оцифрованный и криптографически верифицируемый.

Генезис: 09.01.2026 00:00:00 MSK.


Три решённые проблемы

1. Децентрализованный хронометраж

Проблема. Существующие системы измерения времени (NTP, GPS, PTP) зависят от доверенной инфраструктуры. Компрометация сервера NTP или отключение спутника GPS нарушает временную шкалу для всех зависимых систем.

Решение. Децентрализованные часы — сеть независимых VDF-осцилляторов, в которой каждый узел вычисляет ход времени автономно через последовательное SHA-256 хэширование. Результат детерминирован и верифицируем любым участником без доверия к третьей стороне.

Свойства. Montana Time обладает четырьмя свойствами одновременно:

Свойство Определение NTP GPS PTP Montana
Монотонность Время не идёт назад нет да да да
Консистентность Все честные узлы согласны на одну шкалу слабая да да да
Верифицируемость Любой может доказать прохождение интервала нет нет нет да
Независимость Отсутствие серверов, спутников, доверенной инфраструктуры нет нет нет да

Ни одна существующая система измерения времени не обеспечивает все четыре свойства.

2. Неплутократический консенсус

Проблема. В Proof of Work влияние пропорционально вычислительному бюджету. В Proof of Stake — капиталу. В обоих случаях безопасность сети является функцией концентрации ресурсов, приобретаемых на рынке.

Решение. Proof of Time — механизм консенсуса, в котором влияние узла определяется исключительно длительностью его непрерывного присутствия в сети, измеренной в подписанных временных окнах. Вес узла = длина его NodeChain (количество окон, в которых узел криптографически доказал своё присутствие).

Свойства.

  • Время — единственный ресурс, который нельзя приобрести, передать, делегировать или сконцентрировать
  • Два участника, запустившие узлы одновременно, имеют равный вес независимо от капитала
  • Стоимость атаки на консенсус выражается не в валюте, а во времени, и растёт линейно с возрастом сети

3. Хронометрическая эмиссия

Проблема. Денежная политика фиатных валют определяется решениями комитетов и непредсказуема. Денежная политика Bitcoin предсказуема, но дефляционна — фиксированный потолок supply создаёт ожидание роста цены и подавляет использование как средства обмена.

Решение. Хронометрическая эмиссия — денежная политика, в которой скорость создания новых единиц привязана к физической константе — секунде — и неизменна на всём горизонте существования протокола. Одна секунда протокольного времени порождает одну монету.

Свойства.

  • Supply в момент T = количество секунд, прошедших с генезиса
  • Годовая инфляция монотонно убывает и асимптотически стремится к нулю как следствие арифметики, не изменения правил
  • Эмиссия не контролируется ни одним участником, комитетом или голосованием
  • Денежная политика полностью определена единственной константой и не может быть изменена после генезиса

Следствие: цифровой стандарт времени

Три решённые проблемы порождают уникальную возможность. Любой документ, событие, состояние может быть записано в Montana с математически доказуемой привязкой к моменту верифицируемого времени. AnchorBlock — 32 байта, одна комиссия, навсегда. Ни одна существующая система не предоставляет timestamp, который одновременно децентрализован, неплутократичен и привязан к детерминированной денежной политике. Montana — не блокчейн с функцией timestamping. Montana — цифровой стандарт времени с функцией передачи ценности.


Montana Time

VDF — цифровой аналог физического осциллятора. 9 192 631 770 колебаний цезия-133 = одна секунда SI. D последовательных SHA-256 = одно окно τ₁ Montana. Калибровка D каждые τ₂ — синхронизация цифрового осциллятора с физическим временем.

TimeChain — глобальные цифровые часы, поддерживаемые сетью узлов. Каждый узел тикает независимо через последовательное хэширование. Результат детерминирован — одни входные данные дают одну временную шкалу.

Токен — не награда за работу. Токен — тик часов, записанный в цепочку. Протокол не генерирует монеты — протокол регистрирует прошедшие секунды. Запись называется монетой.

Четыре свойства

Свойство NTP GPS PTP Montana
Монотонность нет да да да
Консистентность слабая да да да
Верифицируемость нет нет нет да
Независимость нет нет нет да

Монотонность. Время никогда не идёт назад. VDF последователен — каждый хэш зависит от предыдущего.

Консистентность. Все честные узлы согласны на одну временную шкалу. TimeChain детерминирован.

Верифицируемость. Любой может пересчитать VDF и доказать что заявленное время прошло.

Независимость. Каждый узел тикает сам. Нет серверов, спутников, доверенной инфраструктуры.

Montana — не эталон точности. Montana — эталон независимости.

Точность

Гранулярность осциллятора: одно SHA-256 хэширование (зависит от аппаратуры: наносекунды — десятки наносекунд). Дрифт между калибровками (τ₂): единицы секунд за 14 дней. Калибровка D возвращает окно к целевым 60 секундам. Протокол самокорректируется.

Time Oracle

TimeChain value в каждом proposal — верифицируемая временная метка. Внешние системы используют Montana Time:

  • Timestamping. H(document) привязанный к TimeChain value = криптографическое доказательство существования в момент T.
  • Ordering. Два события привязанные к разным TimeChain values имеют доказуемый порядок.
  • Anchoring. Внешний протокол якорится в Montana Time для независимой верификации порядка событий.

Протокольная дата

protocol_date(height) = genesis_timestamp + height × 60

Genesis: 09.01.2026 00:00:00 MSK (Unix: 1736373600). Proposal height 525 600 = ровно один год после генезиса. Калибровка D корректирует дрифт — протокольная дата отклоняется от UTC на единицы секунд за τ₂. Формула точна для любого height.

TimeChain хранится навсегда. Временные метки верифицируемы любым узлом в любой момент.


Криптография

Четыре примитива с разделёнными ролями:

  • SHA-256 — консенсус (TimeChain, NodeChain), адреса, Merkle-деревья, хэширование
  • FN-DSA-512 (selected NIST candidate, forthcoming FIPS 206) — подписи транзакционных блоков
  • Pedersen commitments (Ristretto group) — скрытие балансов и сумм
  • Bulletproofs (Bünz et al. 2018) — range proofs и balance proofs без trusted setup

SHA-256 обеспечивает квантовую устойчивость консенсуса: алгоритм Гровера сокращает безопасность с 256 до 128 бит. FN-DSA-512 обеспечивает математическую постквантовую устойчивость подписей на основе NTRU-решёток. Pedersen commitments и Bulletproofs обеспечивают приватность транзакций — балансы, суммы и получатели скрыты, корректность верифицируема без раскрытия значений.

Подписи — FN-DSA-512

Подпись на NTRU-решётках (Falcon-512). Stateless, многоразовая. Публичный ключ закрепляется за аккаунтом при создании и используется для всех последующих блоков.

Компонент Размер
Приватный ключ 1 281B
Публичный ключ 897B
Подпись (padded) 666B

Поле suite_id в формате блока обеспечивает миграцию подписи без изменения модели состояния. Активация новой схемы требует protocol upgrade. Активная схема на момент запуска: FN-DSA-512.

Адреса

Формат: mt + Base58(account_id + checksum).

Account_id = SHA-256("mt-account" || suite_id || pubkey). Стабильный идентификатор аккаунта. Смена ключа или схемы подписи выполняется через ChangeKey блок без изменения account_id — для этого account_id привязан к первому pubkey, а текущий ключ хранится в состоянии аккаунта.


Account Chain (Block Lattice)

Каждый аккаунт имеет собственную цепочку блоков. Перевод — один блок в цепочке отправителя. Зачисление получателю — детерминированно после финализации proposal. Цепочки аккаунтов полностью независимы.

Типы блоков

OpenAccount — создание аккаунта (один раз):

type           1B
suite_id       2B
account_id    32B
pubkey       897B     <- FN-DSA-512, публикуется единожды
signature    666B
Итого:     ~1 598B

Аккаунт создаётся автоматически при первом входящем переводе. Отправитель указывает pubkey получателя в поле link, при финализации в Account Table создаётся запись с balance = link_amount. Приглашений не требуется. Как Bitcoin: адрес существует математически, в блокчейне появляется при первом зачислении.

StateBlock — приватный перевод:

prev_hash            32B     <- хэш предыдущего блока в цепочке аккаунта
sender_commitment    33B     <- Pedersen commitment на account_id отправителя
link_commitment      33B     <- Pedersen commitment на account_id получателя
amount_commitment    33B     <- Pedersen commitment на сумму перевода
balance_commitment   33B     <- Pedersen commitment на баланс после операции
fee                   8B     <- комиссия (открытая, аудируемая)
ephemeral_pubkey     33B     <- одноразовый ключ для stealth address получателя
encrypted_data       96B     <- зашифровано для получателя: сумма, sender, blinding factors
flags                 1B
signature           666B     <- FN-DSA-512
range_proof         672B     <- Bulletproof: amount >= 0, balance >= 0
balance_proof        64B     <- proof: prev_commitment == balance + amount + fee
Итого:           ~1 704B

Скрыто: отправитель, получатель, сумма, баланс. Открыто: комиссия. Комиссия — часть публичного слоя, аудируемая как эмиссия. Победитель получает сумму открытых комиссий — верифицируемо каждым узлом. Balance proof доказывает что prev_commitment == balance_commitment + amount_commitment + fee (открытая сумма) без раскрытия скрытых значений.

ChangeKey — смена ключа или схемы подписи:

prev_hash     32B
account_id    32B
new_suite_id   2B
new_pubkey   897B     <- новый публичный ключ
signature    666B     <- подписано старым ключом
Итого:     ~1 629B

AnchorBlock — криптографический якорь (привязка данных ко времени):

prev_hash              32B     <- хэш предыдущего блока в цепочке аккаунта
account_id             32B
app_id                 32B     <- SHA-256("mt-app" || app_name), пространство имён приложения
data_hash              32B     <- хэш произвольных данных (Merkle root, H(document), ...)
balance_commitment     33B     <- Pedersen commitment на баланс после комиссии
signature             666B     <- FN-DSA-512
range_proof           336B     <- Bulletproof: balance >= 0, fee >= min_fee
Итого:             ~1 163B

AnchorBlock не перемещает средства. Единственная операция — запись data_hash в цепочку аккаунта с привязкой к timechain_value окна финализации. Комиссия верифицируется через balance_proof: разница между предыдущим и новым balance_commitment >= min_fee.

Верификация баланса

Балансы и суммы скрыты через Pedersen commitments. Верификация без знания значений:

balance_proof:  prev_balance_commitment == new_balance_commitment + amount_commitment + fee_commitment
range_proof:    new_balance >= 0, amount > 0, fee >= min_fee

Каждый узел проверяет Bulletproofs: commitments балансируются, все значения неотрицательны, комиссия не ниже min_fee. Значения не раскрываются. Без trusted setup.

Anti-inflation

Чеканка монет из воздуха невозможна. Два механизма:

1. Commitment arithmetic. Pedersen commitments гомоморфны: C(a) + C(b) = C(a+b). Сумма всех balance_commitments в системе = commitment на общий supply. Каждый узел верифицирует: сумма всех commitments == commitment на supply(height). Если кто-то создал монеты — сумма не сходится.

2. Supply audit. supply(height) = 60_000_000_000 × (height + 1) nɈ. TimeCoin — единственный источник новых монет. TimeCoin публичен (в proposal header). Supply детерминирован по height. Расхождение = невалидный state.

Комиссия

base_fee_nɈ(W) = base_fee_nɈ(W-1) + base_fee_nɈ(W-1) × (utilization_bps - 5000) / 40000
utilization_bps = user_set_size × 10000 / max_block_size

Адаптивная формула на основе заполненности окна. Целевая заполненность: 50%. Входные данные каноничны — из proposals. Детерминирована, одинакова у всех узлов.

Генезис: base_fee(0) = 1 000 nɈ (1 μɈ). Пол: 1 nɈ. Потолок: 1 000 000 000 nɈ (1 Ɉ).

При пустой сети: base_fee падает до пола (1 nɈ). При нагрузке > 50%: base_fee растёт экспоненциально (~удвоение каждые 20 окон при 100% заполненности). Спам становится экспоненциально дороже с каждым окном.

Победитель окна τ₁ получает сумму всех комиссий блоков в окне. Сумма комиссий верифицируема через commitment arithmetic.

Перевод

Перевод на несуществующий account_id создаёт аккаунт получателя автоматически при финализации. Запись в Account Table: account_id, balance = link_amount, pubkey из OpenAccount (если есть) или из первого входящего перевода.

TimeCoin (публичный слой)

Единственный публичный объект в системе переводов. Победитель τ₁ записывает прошедшее время: 60 Ɉ (60 зарегистрированных секунд) + сумма комиссий всех блоков окна.

TimeCoin открыт: сумма эмиссии, winner_node_id, height — публичные поля proposal header. Это чеканка новых монет — она должна быть верифицируема каждым узлом без криптографических proofs.

Публичное (верифицируемо всеми):
  TimeCoin:     60 Ɉ за окно (константа)
  Комиссии:     fee в каждом блоке (открытое поле, 8B)
  Supply audit: supply(height) = 60_000_000_000 × (height + 1) nɈ
  Winner:       winner_node_id в proposal header
  VDF:          TimeChain values, NodeChain endpoints, подписи

Приватное (только участники):
  Кто отправил перевод       (sender_commitment)
  Кому отправлен             (link_commitment)
  Сколько                    (amount_commitment)
  Какой баланс               (balance_commitment)

Supply audit: сумма всех TimeCoin от генезиса = supply(height). Чеканка открыта — нельзя напечатать из воздуха. Комиссии открыты — сумма верифицируема простым сложением. Переводы закрыты — нельзя узнать кто кому сколько. Два слоя: публичная эмиссия и комиссии + приватные транзакции.

Двойная трата

Каждый аккаунт имеет одну цепочку. Два блока с одним prev_hash = форк. Форк обнаруживается мгновенно. Разрешается победителем τ₁: побеждает блок полученный раньше (меньшая позиция j в NodeChain победителя). Второй блок отбрасывается. Позиция верифицируема через VDF — гриндинг невозможен.


Состояние сети

Глобальное состояние = Account Table + Node Table.

Account Table (запись на аккаунт):
  account_id              32B
  balance_commitment      33B     <- Pedersen commitment на баланс (значение скрыто)
  frontier_hash           32B     <- хэш последнего блока в цепочке
  block_height             4B
  suite_id                 2B
  current_pubkey         897B
  phone_hash              32B     <- SHA-256("mt-phone" || phone), 0x00 если не привязан

Node Table (запись на узел):
  node_id                          32B     <- SHA-256("mt-node" || node_pubkey), верифицируемо
  node_pubkey                     897B
  suite_id                          2B
  chain_length                      4B     <- суммарное количество подписанных τ₁, weight = chain_length
  pending_invite                   32B     <- node_id приглашённого узла (0x00..00 если нет)
  invite_window                     4B     <- окно финализации NodeInvitation (0 если нет)
  invite_expires                    4B     <- invite_window + 21 160 (0 если нет)
  status                            1B     <- active | suspended

State Root

Merkle-дерево глобального состояния. Два подкорня, каждый — Merkle root своей таблицы с явным порядком листьев:

State Root = SHA-256("mt-merkle-node" || account_root || node_root)

Account Table Root:          листья по account_id (лексикографически)
Node Table Root:             листья по node_id (лексикографически)

active_set_root = Merkle root подмножества Node Table где status = active
                  и chain_length > 0.
                  Детерминировано из Global State. Узлы с weight = 0 не входят в active set.

Все sort keys фиксированной длины. Побайтовое лексикографическое сравнение. Две реализации с одинаковыми данными строят одинаковое дерево и получают одинаковый State Root.

State Root коммитится в заголовке каждого финализированного proposal τ₁.


Двигатели

Три цепочки с односторонним потоком зависимостей: TimeChain → NodeChain → Account.

TimeChain VDF — осциллятор

Первичный продукт протокола. Непрерывная последовательная SHA-256 цепочка — цифровой осциллятор Montana Time:

T_r = SHA-256^D(T_{r-1})

D — количество последовательных хэшей за одно окно τ₁. Каждый хэш — один тик осциллятора. D хэшей — одно колебание. TimeChain продвигается по расписанию окон. Для фиксированного индекса r значение T_r совпадает у всех честных узлов. Каждый узел вычисляет TimeChain независимо — результат детерминирован.

TimeChain не зависит от состояния, транзакций и поведения отдельных узлов. Даже при отказе всего Account слоя часы продолжают тикать.

NodeChain — персональная цепочка узла

Криптографическое доказательство присутствия конкретного node_id при каждом тике часов. Якорится в TimeChain каждое окно:

S_{i,s,0}   = SHA-256(S_{i,s-1,m} || T_s || node_id_i)
S_{i,s,j+1} = SHA-256(S_{i,s,j})    для j = 0..m-1

Три компонента seed: предыдущий endpoint (непрерывность цепочки), значение TimeChain (протокольное время), node_id (идентичность). m последовательных хэшей за окно — одно звено NodeChain.

Инициализация: для первого окна нового узла предыдущий endpoint отсутствует. NodeChain init привязан к каноническим данным proposal в котором NodeInvitation финализирован:

S_{i,0,0} = SHA-256("mt-nodechain-init" || control_root || timechain_value || node_id_i)

control_root и timechain_value из proposal header окна финализации Invitation. Оба канонические (не зависят от субъективного user_set). Предвычисление VDF невозможно — timechain_value неизвестен до закрытия окна. Grinding surface = ноль. Верифицируем любым узлом.

NodeChain зависит от TimeChain. TimeChain не зависит от NodeChain.

VDF Reveal (публичный слой)

После закрытия окна τ₁ каждый узел публикует подписанное reveal-сообщение (VDF endpoint становится известен только после завершения VDF за окно). VDF Reveal — публичный: node_id, endpoint, подпись открыты. Это консенсус — должен быть верифицируем всеми:

VDF_Reveal:
  node_id          32B
  window_index      4B     <- индекс τ₁
  endpoint         32B     <- S_{i,s,m}
  signature       666B     <- FN-DSA-512, подписано node_pubkey
Итого:       ~734B

Reveal распространяется по P2P. Валидация при получении:

  1. Подпись FN-DSA-512 соответствует node_pubkey из Node Table
  2. window_index = только что закрытый τ₁
  3. node_id существует в Node Table, status = active
  4. endpoint верифицируем: пересчёт NodeChain VDF от предыдущего endpoint (или от nodechain_init для первого окна узла)

Два reveal с одинаковыми (node_id, window_index) и одинаковым endpoint = дедупликация. Два reveal с одинаковыми (node_id, window_index) и разным endpoint = reveal_conflict (equivocation).

active_vdf_list = все валидные VDF_Reveal для текущего window_index, полученные до reveal_cutoff. Порядок: node_id лексикографически. Список каноничен. Свобода победителя над списком: ноль. Пропуск валидного reveal = невалидный proposal = fallback.

late_vdf_list = все валидные VDF_Reveal для предыдущего window_index (N-1), не вошедшие в active_vdf_list окна N-1, полученные до reveal_cutoff текущего окна. Порядок: node_id лексикографически. Каноничен. Одно окно grace — reveal для окна N-2 и старше отвергается.

chain_length += 1 за reveal в любом из двух списков. Время доказано — время учтено. Лотерея использует только active_vdf_list. TimeCoin начисляется только за active_vdf_list. Стимул раскрывать вовремя: опоздавший узел сохраняет chain_length, но теряет TimeCoin за это окно.

Account — содержимое блока

Приём, верификация объектов и формирование набора. Два класса объектов:

UserObjects — пользовательские операции:

Тип Описание Валидация
StateBlock Приватный перевод FN-DSA-512 подпись, Bulletproofs валидны, prev_hash. Если получатель не существует — создаётся автоматически
OpenAccount Публикация ключа FN-DSA-512 подпись, account_id существует в Account Table (создан входящим переводом)
ChangeKey Смена ключа FN-DSA-512 подпись старым ключом, new_pubkey
AnchorBlock Якорь данных ко времени FN-DSA-512 подпись, Bulletproofs валидны, prev_hash, app_id = 32B, data_hash = 32B
PhoneLink Привязка телефона FN-DSA-512 подпись, phone_hash = 32B, Bulletproofs валидны

ControlObjects — объекты управляющие составом сети:

Тип Описание Валидация
NodeInvitation Приглашение нового узла FN-DSA-512 подпись пригласившего, pending_invite = 0, status = active
NodeRegistration Регистрация узла FN-DSA-512 подпись, node_id уникален, proof_endpoint верифицируем через VDF, приглашение существует
EquivocationProof Доказательство equivocation Два конфликтующих подписанных сообщения от одного node_id (proposal_conflict или reveal_conflict)

Каждый узел валидирует объекты обоих классов локально при получении. Валидные объекты ретранслируются по P2P. ControlObjects дополнительно ретранслируются каждое окно до финализации или expiry.

Два набора в proposal

Proposal содержит два набора с разными правилами:

user_set = все валидные UserObjects из мемпула победителя до block_cutoff. Определяется мемпулом победителя. UserObjects не вошедшие в proposal переносятся в следующее окно. Пропуск UserObject не является основанием для отклонения proposal.

control_set = все валидные ControlObjects полученные по P2P до control_cutoff, не финализированные ранее и не истёкшие. Каноничен — все ControlObjects включены ровно один раз. Пропуск или добавление лишнего ControlObject = невалидный proposal = fallback.

Порядок внутри control_set: H(object) лексикографически (каноничен, не зависит от победителя).

Порядок внутри user_set: позиция получения в NodeChain победителя. Каждый узел помечает момент получения UserObject как позицию j в своём NodeChain (промежуточный хэш S_{i,s,j}). Победитель сортирует user_set по позиции j — хронологический порядок, верифицируемый через пересчёт NodeChain. Точность: единицы микросекунд (одна позиция = 60/m секунд).

Форки аккаунтов (два блока с одним prev_hash) разрешаются правилом: побеждает блок, полученный победителем раньше (меньшая позиция j в NodeChain). Верифицируемо — позиция является промежуточным хэшем VDF.

Два дедлайна в окне

|-------- τ₁ (60 сек) --------|--- R (12 сек) ---|
                               ^                   ^
                         block_cutoff       reveal_cutoff = control_cutoff
                       (C сек до закрытия)    (R сек после закрытия)
  • block_cutoff = C секунд до закрытия окна. UserObjects после block_cutoff переносятся в следующее окно. C достаточен для P2P-пропагации последних блоков. C = ⌈R / 2⌉.
  • reveal_cutoff = control_cutoff = R секунд после закрытия окна. VDF_Reveal и ControlObjects принимаются до этого момента.

R, F, C — калибруются в τ₂ (см. раздел «Калибровка R, F, C»). Генезис: R₀ = 12s, F₀ = 12s, C₀ = 6s.

После reveal_cutoff: определяется победитель лотереи, победитель собирает proposal.

Proposer

Победитель собирает proposal из двух наборов:

  • control_set: все ControlObjects до control_cutoff (каноничен, свобода = ноль)
  • user_set: все UserObjects из своего мемпула до block_cutoff

Свобода proposer над обработкой: ноль (порядок control_set = H(object), порядок user_set = позиция в NodeChain, state transition = детерминирован). Свобода над control_set: ноль (каноничен). Свобода над user_set: ограничена мемпулом (задержка, не раскол). Свобода над порядком user_set: ноль (определяется позицией получения в NodeChain, верифицируемо).

Proposal с невалидным объектом, пропущенным ControlObject или неверным state_root отклоняется, переход ко второму месту.

Финальность proposal

Финальность = подпись победителя на proposal header + независимая верифицируемость.

  1. Победитель публикует подписанный proposal header + control_set + user_set + active_vdf_list + late_vdf_list
  2. Каждый узел проверяет каждый блок из списка по правилам валидации
  3. Каждый узел применяет state transition детерминированно
  4. Каждый узел сравнивает вычисленный state_root с заявленным в proposal
  5. Совпадает — proposal принят, state обновлён
  6. Не совпадает — proposal отклонён, fallback на второе место

Proposal header:

Proposal header:
  prev_proposal_hash    32B
  prev_state_root       32B    <- State Root до применения блоков
  control_root          32B    <- Merkle root control_set (каноничен)
  user_root             32B    <- Merkle root user_set
  vdf_list_root         32B    <- Merkle root active_vdf_list (каноничен)
  late_vdf_root         32B    <- Merkle root late_vdf_list (каноничен, 0x00 если пуст)
  new_state_root        32B    <- State Root после применения всех блоков
  active_set_root       32B
  timechain_value          32B
  timecoin_hash         32B
  winner_node_id        32B
  wall_clock            8B     <- секунды с генезиса (локальное измерение победителя)
  fallback_depth        1B     <- 1 = первое место, 2+ = fallback (каноничен)
  signature            666B    <- FN-DSA-512, подписано node_pubkey победителя

Fallback: если proposal победителя не получен в пределах timeout (F секунд после reveal_cutoff) или отклонён — proposal формирует второе место (следующий min ticket). Каскад до третьего места и далее с тем же timeout. Молчание не наказывается — победитель теряет только TimeCoin за это окно. F калибруется в τ₂ (см. раздел «Калибровка R, F, C»).

Непрерывность VDF

VDF следующего окна вычисляется непрерывно, не ожидая завершения финализации предыдущего. TimeChain для окна N+1 детерминирован — каждый узел вычисляет его независимо. NodeChain для окна N+1 стартует сразу после закрытия окна N, используя собственный endpoint текущего окна и новое значение TimeChain. Reveal phase и финализация происходят параллельно с началом VDF следующего окна.

Async confirmations (для light clients)

После принятия proposal узлы публикуют confirmation:

Confirmation:
  node_id          32B
  proposal_hash    32B
  signature       666B

Confirmations не участвуют в консенсусе. Они предоставляют light clients аудиторский след: независимые узлы подтвердили корректность state_root. Light client взвешивает confirmations по chain_length из prev_state_root (состояние на начало окна, зафиксировано в proposal header). Не по количеству node_id. Порог доверия определяется light client.

State transition

При финализации proposal state transition применяется атомарно в фиксированном порядке:

apply_proposal(state, proposal) -> state':

  Шаг 1: применить control_set в порядке H(object) лексикографически.
    NodeInvitation:   записать pending_invite, invite_window и invite_expires в Node Table пригласившего.
    NodeRegistration: проверить приглашение, создать запись в Node Table
                      (chain_length=1, status=active). Очистить pending_invite пригласившего.
    EquivocationProof: применить санкцию к node_id в Node Table
                       (proposal_conflict или reveal_conflict: status=suspended, chain_length=0).

  Шаг 2: применить user_set в порядке позиции получения в NodeChain победителя.
    StateBlock:          обновить balance_commitment отправителя и получателя.
                         Если получатель не существует — создать запись в Account Table.
    OpenAccount:         записать pubkey в Account Table (аккаунт уже создан входящим переводом).
    ChangeKey:           обновить pubkey и suite_id в Account Table.
    AnchorBlock:         обновить balance_commitment отправителя (комиссия).
    PhoneLink:           записать phone_hash в Account Table, обновить balance_commitment (комиссия).

  Шаг 3: применить TimeCoin победителя.

  Шаг 4: обновить chain_length.
    Для каждого node_id из active_vdf_list в proposal:
    chain_length += 1 в Node Table.
    Для каждого node_id из late_vdf_list в proposal:
    chain_length += 1 в Node Table.

  Шаг 5: обработать expiry.
    Приглашения узлов: все записи Node Table где invite_expires <= current_window
    и invite_expires > 0 -> очистить pending_invite, invite_window и invite_expires.

  Шаг 6: вычислить new_state_root.

Порядок детерминирован. Control_set первым, user_set вторым, TimeCoin третьим, chain_length четвёртым, expiry последним. Каждый узел применяет одну и ту же последовательность шагов к одним и тем же наборам и получает один и тот же new_state_root.

Account зависит от TimeChain и NodeChain. Обратных зависимостей нет.

С ростом TPS сети дополнительные ядра подключаются для верификации блоков. Минимум для валидатора: 3 логических ядра (TimeChain + NodeChain + Account). Верификация блоков аккаунтов полностью параллелизуется — цепочки аккаунтов независимы.

Приглашение и регистрация

Два уровня входа в сеть. Узлы участвуют в консенсусе — жёсткий вход (приглашение + 14 дней VDF). Аккаунты держат и переводят средства — облегчённый вход (приглашение, немедленная активация).

Генезис: 12 узлов в разных локациях (hardcoded, аналог bootstrap nodes в Bitcoin).

Приглашение узла (NodeInvitation)

Вход узла в консенсус. Приглашение + 14 дней VDF + регистрация. Одновременно одно приглашение узла на пригласившего.

NodeInvitation:
  inviter_node_id    32B
  invited_pubkey    897B     <- публичный ключ приглашённого узла
  signature         666B     <- подписано inviter node_pubkey
Итого:          ~1 595B

NodeInvitation — ControlObject. Не содержит start_window — определяется при финализации.

Валидация:

  1. Подпись валидна для inviter node_pubkey из Node Table
  2. inviter status = active
  3. inviter pending_invite = 0x00..00 (нет активного приглашения узла)
  4. invited node_id = SHA-256("mt-node" || invited_pubkey) не существует в Node Table

При финализации в proposal P окна W:

  • inviter pending_invite = invited node_id
  • inviter invite_window = W
  • inviter invite_expires = W + 21 160 (20 160 окон + 1 000 окон запас)

Привязка NodeChain к моменту приглашения

Первое звено NodeChain приглашённого узла привязано к каноническим полям proposal в котором NodeInvitation финализирован:

nodechain_init = SHA-256("mt-nodechain-init" || control_root || timechain_value || node_id)

control_root и timechain_value — канонические поля из proposal header окна финализации. Не зависят от субъективного user_set победителя. Предвычисление VDF невозможно: timechain_value неизвестен до закрытия окна.

Приглашённый узел узнаёт control_root и timechain_value только увидев финализированный proposal → вычисляет nodechain_init → начинает NodeChain с окна W+1.

Регистрация узла

Приглашённый узел после финализации NodeInvitation:

  1. Наблюдает proposal с NodeInvitation → получает control_root и timechain_value
  2. Вычисляет nodechain_init = SHA-256("mt-nodechain-init" || control_root || timechain_value || node_id)
  3. Непрерывно строит NodeChain: 20 160 окон подряд (от W+1 до W+20 160), каждое звено якорится в соответствующий TimeChain
  4. Через ~14 дней получает proof_endpoint = S_{i,20159,m}
  5. Публикует NodeRegistration
NodeRegistration:
  type              1B
  suite_id          2B
  node_pubkey     897B     <- FN-DSA-512 ключ узла
  inviter_node_id  32B     <- кто пригласил
  proof_endpoint   32B     <- S_{i,20159,m} (endpoint после 20 160 окон VDF)
  signature       666B     <- подписано node_pubkey
Итого:        ~1 630B

NodeRegistration — ControlObject.

Валидация NodeRegistration:

  1. Подпись FN-DSA-512 валидна для node_pubkey
  2. node_id уникален (не существует в Node Table)
  3. inviter_node_id существует в Node Table, pending_invite = node_id
  4. invite_window + 20 160 < текущее окно (VDF завершён)
  5. Восстановить control_root и timechain_value из proposal окна invite_window
  6. Вычислить nodechain_init = SHA-256("mt-nodechain-init" || control_root || timechain_value || node_id) из proposal окна invite_window
  7. proof_endpoint верифицируем: пересчёт VDF от nodechain_init через 20 160 окон с якорением в TimeChain значения от invite_window+1

Верификация: 20 160 сегментов VDF проверяются параллельно. На C ядрах: ~(20 160/C) × t_segment.

При финализации: создать запись в Node Table (chain_length = 1, status = active). Очистить pending_invite, invite_window и invite_expires у пригласившего.

Истечение приглашения узла

Если NodeRegistration не финализирован до invite_expires (invite_window + 21 160) — приглашённый не завершил VDF. При обработке state transition: pending_invite, invite_window, invite_expires пригласившего очищаются автоматически. Узел может приглашать снова.

Создание аккаунта

Аккаунт не требует приглашений. Пользователь генерирует FN-DSA-512 keypair → вычисляет account_id = SHA-256("mt-account" || suite_id || pubkey) → получает адрес. Аккаунт появляется в Account Table при первом входящем переводе. Как Bitcoin: адрес существует математически, в блокчейне — при первом зачислении.

Sybil-барьер: каждый спам-аккаунт стоит отправителю min_fee за перевод. Пустые аккаунты бесполезны — без баланса ничего не делают.

Скорость роста сети

Узлы: каждый узел производит максимум одно приглашение узла за ~14 дней. Рост ограничен текущим размером сети:

Генезис:      12 узлов
14 дней:      24
28 дней:      48
1 год:        до 12 × 2^26 (~800M, теоретический максимум)

Аккаунты: без ограничений. Любой владелец TimeCoin может создать аккаунт любому, переведя средства на новый адрес. Рост пользовательской базы не ограничен протоколом.

Аппаратный бюджет сверх количества приглашений узлов бесполезен. 1000 узлов = максимум 1000 новых узлов за 14 дней, независимо от количества ядер.


Потоковая модель

Блоки аккаунтов текут непрерывно. Узел получает блок → проверяет подпись FN-DSA-512 и баланс → помечает позицию j в своём NodeChain → передаёт в P2P gossip. Время подтверждения определяется скоростью P2P-распространения (~2-5 секунд).

Позиция j — промежуточный хэш S_{i,s,j} NodeChain в момент получения блока. Криптографическое доказательство момента получения с точностью до микросекунд. Верифицируемо через пересчёт NodeChain.

Блок включается в канонический набор при включении победителем лотереи в proposal τ₁. Порядок блоков в proposal — хронологический по позиции в NodeChain победителя. Окна являются точками финализации, а не условиями приёма.

Кошелёк получателя отображает входящий перевод сразу после P2P-валидации (~2-5 секунд) как «подтверждён, ожидает финализации». После включения в proposal τ₁ — «финализирован». Подтверждение — безопасность подписи. Финализация — каноническое состояние.

Цепочки аккаунтов полностью независимы. Блоки разных аккаунтов обрабатываются параллельно без конфликтов.


Временные слои (τ)

τ₁ (60с) → τ₂ (20 160 × τ₁ ≈ 14 дней)

Одно окно — τ₁. Всё остальное — производные.

τ₁ — Окно (60 секунд)

Единственная единица протокольного времени. Слот, финализация и регистрация времени — одно и то же.

  • TimeChain продвигается на D хэшей
  • NodeChain продвигается на m хэшей с якорем в текущем T_s
  • Узлы валидируют и ретранслируют блоки аккаунтов по P2P
  • Active set (состав и веса участников) определяется на момент reveal_cutoff. VDF не зависит от active set — вычисляется непрерывно. К reveal_cutoff state transition предыдущего окна применён — active set актуален
  • control_set: все валидные ControlObjects до control_cutoff (каноничен)
  • user_set: все валидные UserObjects из мемпула победителя до block_cutoff, порядок по позиции получения в NodeChain
  • Узлы раскрывают NodeChain endpoint (reveal phase, R = 12 секунд)
  • Лотерея: ticket_i = -ln(S_{i,s,m} / 2^256) / weight_i, победитель = min(ticket_i) среди допущенных
  • Победитель публикует подписанный proposal
Proposal header:
  prev_proposal_hash    32B
  prev_state_root       32B
  control_root          32B
  user_root             32B
  vdf_list_root         32B
  late_vdf_root         32B
  new_state_root        32B
  active_set_root       32B
  timechain_value          32B
  timecoin_hash         32B
  winner_node_id        32B
  wall_clock            8B
  fallback_depth        1B
  signature            666B
  • Финальность: подпись победителя на proposal header. Каждый валидатор применяет блоки детерминированно и проверяет new_state_root
  • При финализации: state transition по фиксированному порядку (см. раздел «State transition» в Account)
  • TimeCoin: запись прошедшего времени (60 Ɉ = 60 секунд) + все комиссии → победителю
  • Supply audit: суммарная эмиссия TimeCoin от генезиса сверяется с supply(height) из issuance schedule
  • Разрешение форков: приоритет ветки с наибольшим суммарным TimeChain-доказательством

TimeChain safety: компрометация значения TimeChain требует нарушения свойства последовательности SHA-256 VDF.

TimeChain liveness: задержка продвижения TimeChain невозможна — TimeChain вычисляется каждым узлом независимо.

τ₂ — Адаптация (20 160 × τ₁ ≈ 14 дней)

  • Калибровка D и m (см. ниже)
  • Калибровка R, F, C (см. ниже)
  • State Root коммитится в каждом τ₁ (в proposal header). State Root покрывает весь Global State: Account Table, Node Table. τ₂ фиксирует канонический State Root для Fast Sync
  • Криптографическая амнезия: подписанные proposals сохраняются навсегда — верифицируемая цепочка state commitments. Тела блоков аккаунтов, подписи FN-DSA-512 и данные execution objects удаляются после 8 × τ₂ (112 дней). Proposals доказывают что конкретное состояние было закоммичено победителем; восстановление содержимого состояния требует snapshot или архива. Equivocation proofs по объектам старше 8 × τ₂ не принимаются — все споры разрешаются внутри окна
  • Пересчёт параметров размера окна τ₁

Калибровка D и m

Каждый proposal содержит wall_clock — секунды с генезиса по локальным часам победителя. Одно измерение субъективно. Медиана 20 160 измерений за τ₂ — распределённый эталон.

Валидация wall_clock при получении proposal:

wall_clock > prev_proposal.wall_clock                    (монотонность)
wall_clock < prev_proposal.wall_clock + 2 × 60           (не более 2× target вперёд)

Формула пересчёта D на границе τ₂:

intervals[i] = proposal[i].wall_clock - proposal[i-1].wall_clock
               для i = 1..20 159

actual_interval = median(intervals)
target_interval = 60

D_new = D_old × actual_interval / target_interval
D_new = clamp(D_new, D_old × 0.5, D_old × 1.5)

Формула точная — D корректируется ровно пропорционально отклонению. Страховочный clamp ±50% защищает от чёрного лебедя (массовый выход узлов, аппаратная революция). При нормальной работе отклонение медианы от 60 секунд — единицы секунд, коррекция D — единицы процентов.

Медиана > 60 → окна медленнее цели → D слишком велик → уменьшить. Медиана < 60 → окна быстрее → D увеличить.

m калибруется пропорционально: m_new = m_old × (D_new / D_old).

Генезис: D₀ и m₀ калибруются при запуске для целевых 60 секунд. Абсолютный якорь: 09.01.2026 00:00:00 MSK. Медиана 20 159 интервалов — для сдвига необходим контроль >50% proposals (>50% веса сети).

Калибровка R, F, C

Входные данные — из proposals за τ₂ (канонические, детерминированно вычисляются всеми узлами):

late_ratio     = Σ|late_vdf_list| / (Σ|active_vdf_list| + Σ|late_vdf_list|)
fallback_ratio = count(fallback_depth > 1) / 20160

Формула пересчёта:

R_new = clamp(R_old × late_ratio / late_target, R_old × 0.8, R_old × 1.2)
R_new = clamp(R_new, R_min, R_max)

F_new = clamp(F_old × fallback_ratio / fallback_target, F_old × 0.8, F_old × 1.2)
F_new = clamp(F_new, F_min, F_max)

C = ⌈R / 2⌉

Протокольные константы:

late_target     = 5%
fallback_target = 5%
R_min = 4s,   R_max = 30s
F_min = 4s,   F_max = 30s

Генезис: R₀ = 12s, F₀ = 12s, C₀ = 6s. Шаг: ±20% за τ₂. C — производная от R, отдельной калибровки не требует.


Консенсус — Proof of Time (PoT)

Три цепочки

TimeChain — глобальные часы. Чистая VDF-цепочка T_r = SHA-256^D(T_{r-1}). Первичный продукт протокола. Источник времени и случайности. Продвигается по расписанию окон.

NodeChain — персональная цепочка узла. VDF-цепочка конкретного node_id, якорится в TimeChain каждое окно. Доказывает присутствие при каждом тике часов. Раскрытие endpoint при закрытии τ₁ = +1 звено = +1 к весу.

Account — состояние счёта. Два набора: control_set (каноничен, все ControlObjects) + user_set (из мемпула победителя). Порядок и обработка детерминированы.

Зависимости односторонние: TimeChain → NodeChain → Account. Отказ в Account не останавливает часы. Отказ конкретного узла в NodeChain не заражает общий ритм.

Стаж и вес

Определение

Вес узла — суммарное количество подписанных τ₁:

weight_i = chain_length_i

Вес — единственная мера влияния узла в протоколе. Определяется только количеством подписанных окон.

Как растёт

Подписал окно — плюс один. Не подписал — ничего не произошло. Equivocation — chain_length = 0.

Узел раскрывает VDF_Reveal после закрытия τ₁ (см. раздел «VDF Reveal»). active_vdf_list каноничен: все валидные reveal до reveal_cutoff, порядок по node_id. Свобода победителя: ноль. State transition: chain_length += 1 для каждого node_id из active_vdf_list и late_vdf_list. Пропуск окна не наказывается — узел просто не получает +1. Late reveal (опоздавший на одно окно) сохраняет chain_length, но не участвует в лотерее и не получает TimeCoin.

На что влияет вес

Вес определяет две вещи:

1. Лотерея. Вероятность победы в τ₁ строго пропорциональна весу:

ticket_i = -ln(S_{i,s,m} / 2^256) / weight_i
winner = min(ticket_i)
P(node_i) = weight_i / Σ weight(all_nodes)

Доказательство: S_{i,s,m} / 2^256 приближает U[0,1]. -ln(U) ~ Exp(1). -ln(U)/w ~ Exp(w). Минимум независимых Exp(w_i): P(i wins) = w_i / Σw_j. Точно пропорционально при любых весах.

Узел с weight = 0 не участвует в лотерее.

2. Допуск. weight = 0 означает: узел участвует в сети (валидация, ретрансляция), но не участвует в лотерее и не может быть победителем τ₁.

Победитель τ₁

Победитель определяется после закрытия окна τ₁. Каждый узел раскрывает свой NodeChain endpoint. Минимальный ticket среди допущенных — победитель.

Победитель публикует подписанный proposal: control_set (каноничен) + user_set (из мемпула) + TimeCoin + active_vdf_list (каноничен) + late_vdf_list (каноничен). Порядок и обработка детерминированы. Валидация: control_set полон, все объекты валидны, state_root корректен. UserObjects не вошедшие в proposal переносятся в следующее окно.

Финальность — подпись победителя на proposal header. Верификация — независимый пересчёт state_root.

Верификация

Победитель публикует: {node_id, NodeChain endpoint, proposal}.

Верификация NodeChain за одно окно: пересчёт m хэшей. Параллелизация по сегментам — время верификации обратно пропорционально числу ядер.

Верификация proposal: независимое применение блоков из canonical set и сравнение state_root.

Устойчивость

  • Остановка часов исключена: каждый узел тикает независимо. Остановить протокол = остановить все VDF-осцилляторы одновременно
  • Искажение часов исключено: VDF последователен, результат детерминирован. Нельзя ускорить, замедлить или подделать
  • Proposer grinding исключён: порядок control_set = H(object) лексикографически, порядок user_set = позиция в NodeChain (верифицируемо), state transition детерминирован, свобода над обработкой = ноль
  • Front-running исключён: порядок user_set определяется позицией получения в NodeChain победителя, победитель неизвестен до reveal — атакующий не знает чей NodeChain определит порядок
  • Committee grinding исключён: TimeChain не зависит от состояния и транзакций, seed лотереи строится из TimeChain
  • Node_id гриндинг исключён: TimeChain неизвестен при регистрации
  • Предвычисление исключено: seed содержит текущее значение TimeChain
  • Replay исключён: TimeChain уникален для каждого τ₁
  • Аппаратное преимущество ограничено: последовательное хэширование масштабируется тактовой частотой и IPC, а не количеством ядер или бюджетом
  • Sybil-барьер: вход по приглашению (1 инвайт на узел, 1 одновременно) + регистрация = 20 160 окон VDF (~14 дней) + NodeChain (физическое ядро) + линейный рост веса. Скорость роста Sybil ограничена размером его текущей сети, а не бюджетом
  • Цензура UserObjects = задержка, не раскол. Пропущенный блок переносится в следующее окно. В account chain prev_hash резервирует место
  • Цензура ControlObjects исключена: control_set каноничен, пропуск = невалидный proposal = fallback
  • Liveness halt исключён: нет порогового голосования, финальность определяется одним победителем с fallback на следующий ticket
  • Fallback cascade: молчащий победитель теряет TimeCoin за это окно. Санкции без подписанного доказательства не применяются

Разрешение конфликтов и санкции

Два класса нарушений. Пользовательские конфликты разрешаются протокольными правилами без санкций. Валидаторский equivocation — через аннулирование конфликтующих сообщений и санкции.

Пользовательские конфликты

Двойной блок аккаунта (два блока с одним prev_hash): побеждает блок полученный победителем раньше (меньшая позиция в NodeChain). Верифицируемо через VDF. Без санкции.

Невалидный proposal: валидаторы отклоняют, переход ко второму месту. Без санкции (потерянный TimeCoin — достаточное наказание).

Валидаторские нарушения

Два типа нарушений. Оба доказуемы криптографически — подписанные конфликтующие сообщения. Санкции возникают только из подписанного доказательства, не из отсутствия сообщения.

Proposal conflict — победитель публикует два разных proposal для одного τ₁ (одинаковые (node_id, window_index), разный proposal_hash):

Сразу: status = suspended, chain_length = 0

Reveal conflict — узел публикует два разных VDF_Reveal для одного τ₁ (одинаковые (node_id, window_index), разный endpoint):

Сразу: status = suspended, chain_length = 0

Молчание победителя (proposal не опубликован) не является нарушением. Победитель теряет TimeCoin за это окно. chain_length не затрагивается (VDF_Reveal доказывает присутствие). Fallback на следующий ticket без санкций.

Санкции вступают в силу с следующего τ₁. Active set определяется на момент reveal_cutoff — к этому моменту state transition предыдущего окна применён.

Equivocation proof

EquivocationProof:
  type             1B     <- proposal_conflict | reveal_conflict
  node_id         32B
  evidence_a             <- первое подписанное сообщение
  evidence_b             <- второе конфликтующее подписанное сообщение

Публикует любой узел обнаруживший конфликт. Верификация: оба сообщения подписаны одним node_id с одинаковым window_index и разным содержимым. Подписи FN-DSA-512 криптографически верифицируемы.

Proof включается в канонический набор τ₁. При финализации state transition применяет санкцию к node_id.


Адреса и переводы

Полный флоу перевода

1. Боб: кошелёк генерирует keypair -> account_id (постоянный адрес, существует математически)
2. Боб -> Алисе: "отправь на mt4ZGfe..." (account_id)
3. Алиса формирует StateBlock в своей цепочке:
   prev_hash: хэш её предыдущего блока
   link: account_id Боба
   link_amount: 50 Ɉ
   balance: 49.999 Ɉ (100 - 50 - 0.001 fee)
4. Алиса подписывает FN-DSA-512
5. Алиса рассылает блок узлам сети
6. Каждый узел проверяет:
   FN-DSA-512 подпись валидна для pubkey Алисы
   prev_hash совпадает с frontier Алисы
   fee = 100 - 49.999 - 50 = 0.001 Ɉ >= min_fee
   balance >= 0
   link_amount > 0
7. Блок распространяется через P2P gossip (~2-5 секунд)
   Кошелёк Боба: «подтверждён, ожидает финализации»
8. Победитель лотереи включает блок в proposal τ₁
9. При финализации proposal:
   Баланс Алисы: 50 Ɉ (из StateBlock)
   Если Боб новый — создать запись в Account Table (balance = 50 Ɉ)
   Если Боб существует — баланс увеличен на 50 Ɉ (детерминированно)
   Кошелёк Боба: «финализирован»

Баланс

Баланс аккаунта — одно число в таблице аккаунтов. Обновляется при финализации: исходящие переводы (из StateBlock отправителя) и входящие зачисления (детерминированно по финализированным блокам).

Бэкап = seed (для деривации приватного ключа FN-DSA-512).


Эмиссия

Единица

Монета: TimeCoin (тикер: $TimeCoin, символ: Ɉ).

1 секунда = 1 TimeCoin = 1 Ɉ = 1 000 mɈ = 1 000 000 μɈ = 1 000 000 000 nɈ 60 секунд = 1 минута = 60 TimeCoin 3 600 секунд = 1 час = 3 600 TimeCoin 86 400 секунд = 1 день = 86 400 TimeCoin

Одно окно τ₁ регистрирует 60 прошедших секунд = 60 TimeCoin.

Точность: 9 знаков после запятой (наносекунда). Все расчёты эмиссии в nɈ (целочисленная арифметика, без плавающей точки).

Issuance schedule

Одна секунда протокольного времени порождает одну монету. С первого блока и навсегда.

Параметр Значение
Генезис 09.01.2026 00:00:00 MSK
TIME_RECORD 60 000 000 000 nɈ (60 Ɉ)

Регистрация времени

time_record(height) = 60_000_000_000 nɈ

Каждое окно τ₁ регистрирует 60 прошедших секунд = 60 Ɉ. Без халвингов, без фаз, без исключений. Одна константа на весь горизонт существования протокола.

Supply audit

supply(height) = 60_000_000_000 × (height + 1) nɈ

Одно умножение. Проверяемо каждым узлом в каждом τ₁. O(1).

Инфляция

Supply растёт линейно. Инфляция снижается асимптотически к нулю — константная эмиссия делится на растущий supply:

Год 1:     100%
Год 2:      50%
Год 5:      20%
Год 10:     10%
Год 50:      2%
Год 100:     1%
Год 1000:    0.1%

Bootstrap

Ранние участники записывают непропорционально большую долю времени через вероятность лотереи. При 10 узлах каждый побеждает примерно раз в 10 минут. При 100 000 — примерно раз в 70 дней. Bootstrap встроен в математику лотереи, а не в расписание часов.

Распределение

Победитель τ₁ записывает прошедшее время и получает все комиссии окна через TimeCoin в своей цепочке. Одно правило. Неизменно с генезиса.

Базовый бюджет: 60 Ɉ/τ₁ (запись 60 секунд) + комиссии. Реальный бюджет безопасности в покупательной способности зависит от рынка.

1 TimeCoin = 1 секунда описывает скорость хода часов. Не ценовой peg, не гарантия покупательной способности.


Пропускная способность

Размер StateBlock: ~779B.

Канал узла TPS
10 Mbps ~1 620
100 Mbps ~16 200
1 Gbps ~162 000

Адаптивный размер окна

Пересчёт в τ₂:

  • Заполненность > 80% → увеличение размера окна
  • Заполненность < 20% → уменьшение размера окна
  • Шаг: ±20% за τ₂
  • Диапазон: 1 MB — 100 MB

Хранение

Модель: глобальное состояние + локальная история

Узлы хранят глобальное состояние (Account Table, Node Table, proposals). Тела блоков аккаунтов хранятся у владельцев. После финализации state transition применён — commitment в таблице обновлён, тело блока сети больше не нужно.

Три уровня участников

Узел (валидатор) — десктоп или сервер, 24/7:

Хранит:
  Account Table       (commitments, frontier_hash, pubkey, phone_hash)
  Node Table          (node_id, pubkey, chain_length, status)
  Proposals           (навсегда)
  Blob Buffer         (зашифрованные сообщения для владельца, TTL = τ₂)

Делает:
  TimeChain VDF       (1 ядро, 24/7)
  NodeChain VDF       (1 ядро, 24/7)
  Валидация блоков    (1+ ядро)
  P2P gossip          (блоки, reveals, proposals)
  Почтовый ящик       (хранит сообщения для своего владельца пока тот офлайн)

Кошелёк (клиент) — телефон, онлайн когда используется:

Хранит:
  Своя цепочка блоков   (история переводов)
  Свои ключи            (seed → keypairs)
  Свои контакты         (адресная книга: имя → mt-адрес)
  Blinding factors      (для расшифровки commitments)
  Сообщения             (локальная история переписки)

Делает:
  Отправка/получение переводов
  Мессенджер (P2P напрямую через libp2p)
  Discovery (phone_hash → account_id → pubkey, локально)
  Запрос pubkey и proposals у узлов сети

Доверенный узел — узел друга, семьи, сообщества:

Делает:
  Всё что узел + хранит Blob Buffer для привязанных аккаунтов
  Владелец аккаунта привязывает свой account_id к доверенному узлу
  Узел хранит зашифрованные сообщения (содержимое скрыто)
  Владелец забирает сообщения когда появляется онлайн

Размеры

Участник Данные Размер
Узел (1M аккаунтов) Account Table + Node Table + Proposals ~2 GB
Узел (10M аккаунтов) Account Table + Node Table + Proposals ~11 GB
Узел (100M аккаунтов) Account Table + Node Table + Proposals ~101 GB
Кошелёк (обычный) 100 блоков/год + контакты + сообщения ~1 MB
Кошелёк (активный) 10 000 блоков/год ~16 MB
Корпорация 1M AnchorBlocks/год ~1.1 GB

Потеря данных клиента

Потеря устройства: balance_commitment в Account Table цел, seed восстанавливает ключи, баланс доступен. История переводов и сообщений утрачена — как потерять выписку, не деньги. Если есть доверенный узел — зашифрованные сообщения можно восстановить.

Fast Sync (новый узел)

  1. Цепочка proposals от генезиса — проверка TimeChain-цепочки и подписей победителей (мегабайты)
  2. State Root из последнего τ₂ (покрывает весь Global State)
  3. Global State snapshot от пиров: каноническая сериализация всех листьев Merkle-дерева состояния (Account Table + Node Table). Верификация: пересчёт Merkle root из полученных листьев, сравнение с State Root из proposal τ₂. MerkleRoot(snapshot_leaves) == state_root_from_proposal
  4. Узел синхронизирован и готов к участию

Proposals доказывают цепочку state commitments. Snapshot восстанавливает содержимое состояния через пересчёт Merkle root. Тела блоков не нужны — состояние самодостаточно.


Application Layer

Montana — цифровой стандарт времени. Приложения управляют своим состоянием самостоятельно (серверы, базы данных, P2P). Montana хранит только криптографические отпечатки с привязкой ко времени — 32 байта на запись.

AnchorBlock

Один блок, одна комиссия, данные навсегда привязаны к timechain_value конкретного окна.

AnchorBlock:
  prev_hash              32B
  account_id             32B
  app_id                 32B     <- SHA-256("mt-app" || app_name)
  data_hash              32B     <- Merkle root, H(document), произвольный хэш
  balance_commitment     33B     <- Pedersen commitment (баланс скрыт)
  signature             666B
  range_proof           336B     <- Bulletproof
Итого:             ~1 163B

app_id — детерминированный идентификатор пространства имён. Вычисляется из имени приложения, регистрация не требуется. Позволяет фильтровать, индексировать, строить лёгкие клиенты для конкретного приложения.

Timestamp Proof

Стандартный формат доказательства: документ D существовал до момента T.

Доказательство:
  1. H(D)                              <- хэш документа
  2. AnchorBlock с data_hash            <- содержит MerkleRoot включающий H(D)
  3. Merkle proof: H(D) → data_hash     <- H(D) является листом дерева
  4. Proposal header окна               <- содержит AnchorBlock, timechain_value = T
  5. VDF-цепочка от генезиса            <- доказывает T

Верификация:
  1. Пересчитать Merkle proof: H(D) → data_hash
  2. Убедиться что AnchorBlock включён в proposal (user_root)
  3. Убедиться что proposal содержит валидный timechain_value
  4. Любой участник с доступом к цепочке proposals верифицирует

Proposals хранятся навсегда. Timestamp proof верифицируем в любой момент.

Примеры

Мессенджер. Каждое сообщение хэшируется, цепочка хэшей формирует Merkle root, Merkle root записывается в AnchorBlock раз в минуту или час. Montana хранит 32 байта — доказательство что набор сообщений существовал в конкретный момент. Подделать историю переписки невозможно — хэш не совпадёт.

Архив документов. Компания ежедневно записывает Merkle root документов. Через 10 лет регулятор спрашивает «существовал ли документ X на дату Y». Компания предоставляет документ, Merkle proof и ссылку на proposal. Верификация математическая.

Социальная сеть. Каждый пост привязан к Montana Time через AnchorBlock. Порядок публикаций доказуем. Редактирование не скрывает оригинал — хэш оригинала уже в цепочке.

Экономика

Каждый AnchorBlock платит комиссию. Тысячи приложений записывающих якоря — тысячи комиссий каждое окно. Спрос на токен привязан к утилитарной функции: запись времени, не спекуляция.

Не нужны смарт-контракты. Не нужен Turing-complete язык. Не нужен газ.

Phone Discovery

Привязка номера телефона к аккаунту. Опционально — пользователь решает сам.

phone_hash = SHA-256("mt-phone" || phone_number)

Пользователь записывает phone_hash в Account Table через PhoneLink блок (UserObject). Любой знающий номер вычисляет phone_hash локально → находит account_id в своей копии Account Table → получает pubkey. Ноль запросов к серверу. Ноль загрузки контактов.

PhoneLink:
  prev_hash            32B
  account_id           32B
  phone_hash           32B     <- SHA-256("mt-phone" || phone_number)
  balance_commitment   33B
  signature           666B
  range_proof         336B
Итого:            ~1 131B

Алиса открывает адресную книгу → вычисляет phone_hash каждого контакта локально → сверяет с Account Table на своём узле → мгновенно видит кто в Montana. Как WhatsApp, но без сервера.

Messenger

Мессенджер поверх Montana. Протокол отвечает за identity и время. Доставка — P2P между устройствами. Montana не хранит, не буферизирует, не маршрутизирует сообщения.

Что даёт Montana мессенджеру:

  • Discovery: phone_hash → account_id → pubkey (локально, из Account Table)
  • Шифрование: pubkey получателя из Account Table → E2E encryption
  • Timestamping: AnchorBlock с хэшем переписки → доказуемый момент

Что Montana НЕ делает:

  • Не хранит сообщения
  • Не маршрутизирует
  • Не буферизирует
Отправка:
  1. Алиса знает phone_hash Боба → account_id → pubkey (локально)
  2. Шифрует сообщение pubkey Боба
  3. Отправляет прямо устройству Боба через P2P (libp2p)
  4. Опционально: AnchorBlock с H(conversation_root) → привязка ко времени

Получение (онлайн):
  1. Боб получает сообщение по P2P напрямую
  2. Расшифровывает своим приватным ключом

Получение (офлайн):
  1. Боб офлайн → Алиса хранит сообщение на своём устройстве
  2. Боб появился → устройство Алисы доставляет
  3. Если у Боба есть узел (валидатор) — узел работает 24/7,
     принимает и хранит сообщения для своего владельца

Три уровня доставки:

Уровень 1 — оба онлайн:
  Телефон Алисы ←→ libp2p ←→ Телефон Боба
  Прямое P2P соединение. Мгновенно.

Уровень 2 — Боб офлайн, у Боба есть узел:
  Телефон Алисы → libp2p → Узел Боба (24/7)
  Узел хранит зашифрованное сообщение в Blob Buffer (TTL = τ₂)
  Боб появился → телефон забирает сообщения с узла

Уровень 3 — Боб офлайн, у Боба есть доверенный узел:
  Телефон Алисы → libp2p → Доверенный узел Боба
  Зашифровано pubkey Боба — доверенный узел не видит содержимое
  Боб появился → забирает сообщения

Fallback — оба офлайн, нет узла:
  Алиса хранит сообщение на своём устройстве
  Доставляет при следующей встрече онлайн

Сообщения хранятся у участников, не в сети. Montana хранит только хэши (32B) с привязкой ко времени. Подделать историю переписки невозможно — хэш в proposal навсегда.

Integration

Три операции для подключения внешних систем к Montana.

Write — запись

Внешняя система формирует AnchorBlock и отправляет в P2P-сеть.

Вход:  app_id (32B) + data_hash (32B) + подпись FN-DSA-512
Выход: AnchorBlock включён в proposal окна W с timechain_value T_W

data_hash — произвольный хэш: Merkle root документов, хэш batch'а Rollup, fingerprint состояния. Montana не интерпретирует содержимое — хранит 32 байта с привязкой ко времени.

Read — чтение

Внешняя система запрашивает доказательство по height или data_hash.

Вход:  proposal height или data_hash + app_id
Выход: proposal header + Merkle path от data_hash до user_root

Proposal header содержит timechain_value = доказуемый момент. Merkle path доказывает что data_hash включён в этот proposal.

Verify — верификация

Внешняя система проверяет proof автономно, без доверия к Montana-узлу.

1. Пересчитать Merkle path: data_hash → user_root
2. Сравнить user_root с полем в proposal header
3. Проверить подпись победителя на proposal header
4. Проверить timechain_value: пересчёт D хэшей от T_{W-1} до T_W
5. Проверить цепочку proposals: prev_proposal_hash связывает окна до генезиса

Шаги 13: миллисекунды. Шаг 4: ~60 секунд на одном ядре (один сегмент VDF). Шаг 5: проверка подписей и хэшей — линейна по количеству proposals, параллелизуема.

Полная верификация от генезиса: H сегментов VDF, каждый проверяется независимо. На C ядрах: ~(H/C) × 60 секунд. TimeChain хранит все промежуточные T_r в proposals — параллелизация полная.


Ключи

seed
├── Аккаунт:  FN-DSA-512 keypair → account_id = SHA-256("mt-account" || suite_id || account_pubkey)
└── Узел:     FN-DSA-512 keypair → node_id = SHA-256("mt-node" || node_pubkey)

Один seed порождает два FN-DSA-512 keypair: для аккаунта (подпись блоков) и для узла (подпись proposals и NodeChain endpoints). account_id и node_id выводятся из публичных ключей, верифицируемы без знания seed. Бэкап = seed.


Криптографическая реализация

Primitive layer

Собственная реализация криптографических примитивов запрещена. Только audited библиотеки с constant-time гарантиями и опубликованными test vectors.

Примитив Стандарт Роль
SHA-256 FIPS 180-4 TimeChain, NodeChain, адреса, Merkle-деревья
FN-DSA-512 Selected NIST candidate, forthcoming FIPS 206 Подписи блоков аккаунтов и proposals
Pedersen commitments Ristretto group Скрытие балансов и сумм
Bulletproofs Bünz et al. 2018 Range proofs, balance proofs. Без trusted setup

Consensus encoding layer

Консенсусно-критическая поверхность: каноническая сериализация, Merkle layout и domain separation. Разная сериализация одного объекта = разный хэш = форк. Требования:

  • Fixed binary encoding для каждого консенсусного объекта
  • Length-prefix кодирование полей, фиксированный endianness (little-endian)
  • Domain separation для всех хэшей:
Домен Контекст
mt-block Хэширование блоков аккаунтов
mt-header Хэширование proposal headers
mt-account Деривация account_id
mt-invitation Хэширование приглашений
mt-merkle-leaf Листья Merkle-деревьев
mt-merkle-node Внутренние узлы Merkle-деревьев
mt-timechain TimeChain VDF seed
mt-nodechain-init NodeChain init seed
mt-equivocation Хэширование equivocation proofs
mt-confirmation Хэширование async confirmations
mt-app Деривация app_id для Application Layer
mt-node Деривация node_id
mt-phone Деривация phone_hash для Phone Discovery
  • Альтернативные сериализации запрещены
  • Test vectors для каждого консенсусного объекта
  • Cross-implementation conformance tests перед запуском mainnet

Protocol layer

Собственная реализация поверх криптографического ядра:

Компонент Назначение
Merkle-деревья State Root, blocks_root (из SHA-256 вызовов)
VDF scheduling Управление TimeChain и NodeChain цепочками
State machine Account Table, Node Table, state transitions
P2P gossip Распространение блоков и proposals

Инфраструктура

Библиотека Назначение
RocksDB Хранение Account Table и блоков
libp2p P2P транспорт

Production: Rust.


Архитектура

┌─────────────────────────────────┐
│  Wallet                         │
│  Кошелёк, баланс, переводы     │
│  FN-DSA-512 keypair            │
└──────────────┬──────────────────┘
               │
┌──────────────┴──────────────────┐
│  Montana                        │
│  Децентрализованные часы        │
│                                 │
│  TimeChain ──→ NodeChain ──→ Account
│  (часы)     (присутствие)  (состояние)
│                                 │
│  Account Chain (Block Lattice)  │
│  Account Table, Proposals       │
│  SHA-256, FN-DSA-512            │
└─────────────────────────────────┘