# TimeChain — Спецификация протокола Montana **Версия:** 15.9.0 (2026-04-03) ## Определение Децентрализованные криптографические часы. Сеть независимых VDF-осцилляторов, поддерживающих единую верифицируемую временную шкалу. Каждая зарегистрированная секунда = 1 Ɉ. Основная функция — хронометраж. Вторичная — определение ценности. Консенсус: **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 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 для независимой верификации порядка событий. TimeChain хранится навсегда. Временные метки верифицируемы любым узлом в любой момент. --- ## Криптография Два примитива с разделёнными ролями: - **SHA-256** — консенсус (TimeChain, NodeChain), адреса, Merkle-деревья, хэширование - **FN-DSA-512** (selected NIST candidate, forthcoming FIPS 206) — подписи транзакционных блоков SHA-256 обеспечивает квантовую устойчивость консенсуса: алгоритм Гровера сокращает безопасность с 256 до 128 бит. FN-DSA-512 обеспечивает математическую постквантовую устойчивость подписей на основе NTRU-решёток. ### Подписи — 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 ``` Аккаунт создаётся по приглашению от узла (AccountInvitation). Активация немедленная — аккаунт доступен для операций сразу после финализации приглашения в proposal (см. раздел «Приглашение аккаунта»). **StateBlock** — перевод: ``` prev_hash 32B <- хэш предыдущего блока в цепочке аккаунта account_id 32B link 32B <- account_id получателя link_amount 8B <- сумма перевода получателю balance 8B <- абсолютный баланс отправителя после операции flags 1B signature 666B Итого: ~779B ``` **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 8B <- абсолютный баланс после комиссии signature 666B <- FN-DSA-512 Итого: ~802B ``` AnchorBlock не перемещает средства. Поле link отсутствует, link_amount отсутствует. Единственная операция — запись data_hash в цепочку аккаунта с привязкой к timechain_value окна финализации. Комиссия: fee = prev_balance - new_balance >= min_fee. ### Верификация баланса Баланс в StateBlock абсолютный. StateBlock содержит поле balance (новый баланс отправителя после операции). Перевод содержит сумму в поле link_amount (8B, добавляется в формат StateBlock). ``` fee = prev_balance - new_balance - link_amount ``` Каждый узел проверяет: new_balance >= 0, link_amount > 0, fee >= min_fee. ### Комиссия Комиссия вычисляется из трёх известных величин: prev_balance (из Account Table), new_balance и link_amount (из StateBlock). Минимум 1 mɈ. Размер min_fee адаптивный — рассчитывается по формуле на основе заполненности окон τ₁. Пользователь знает комиссию до отправки. Победитель окна τ₁ получает сумму всех комиссий блоков в окне. ### Перевод Перевод на несуществующий account_id = невалидный StateBlock = отклоняется. Аккаунт получателя должен существовать в Account Table до отправки. ### TimeCoin Победитель τ₁ записывает прошедшее время: фиксирует TimeCoin в своей цепочке — 60 Ɉ (60 зарегистрированных секунд) + комиссии всех блоков окна. Supply audit при финализации τ₁: суммарная запись времени от генезиса сверяется с supply(height) из issuance schedule. ### Двойная трата Каждый аккаунт имеет одну цепочку. Два блока с одним prev_hash = форк. Форк обнаруживается мгновенно. Разрешается победителем τ₁: canonical rule lower H(block) wins. Второй блок отбрасывается. --- ## Состояние сети Глобальное состояние = Account Table + Node Table. ``` Account Table (запись на аккаунт): account_id 32B balance 8B frontier_hash 32B <- хэш последнего блока в цепочке block_height 4B suite_id 2B current_pubkey 897B 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 если нет) account_invites_this_epoch 2B <- счётчик приглашений аккаунтов за текущий τ₂ 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 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 подпись, баланс >= 0, prev_hash, link_amount > 0, fee >= min_fee, получатель существует в Account Table | | OpenAccount | Создание аккаунта | FN-DSA-512 подпись, account_id существует в Account Table | | ChangeKey | Смена ключа | FN-DSA-512 подпись старым ключом, new_pubkey | | AnchorBlock | Якорь данных ко времени | FN-DSA-512 подпись, баланс >= 0, prev_hash, fee >= min_fee, app_id = 32B, data_hash = 32B | | AccountInvitation | Приглашение аккаунта | FN-DSA-512 подпись узла, status = active, account_id не существует, account_invites_this_epoch < K | **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. Порядок внутри обоих наборов: H(object) лексикографически. Форки аккаунтов (два блока с одним prev_hash) разрешаются правилом lower H(block) wins. #### Два дедлайна в окне ``` |-------- τ₁ (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 над обработкой: ноль (порядок = H(object), state transition = детерминирован). Свобода над control_set: ноль (каноничен). Свобода над user_set: ограничена мемпулом (задержка, не раскол). 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 в порядке H(object) лексикографически. StateBlock: обновить баланс отправителя, кредитовать баланс получателя. OpenAccount: создать запись в Account Table (balance = 0). ChangeKey: обновить pubkey и suite_id в Account Table. AnchorBlock: обновить баланс отправителя (комиссия), записать data_hash в цепочку. AccountInvitation: создать запись в Account Table (status = active, balance = 0), инкрементировать account_invites_this_epoch пригласившего. Шаг 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. Сброс счётчика: на границе τ₂ -> account_invites_this_epoch = 0 для всех узлов. Шаг 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 пригласившего очищаются автоматически. Узел может приглашать снова. #### Приглашение аккаунта (AccountInvitation) Вход пользователя. Приглашение от узла, активация немедленная. Аккаунт не участвует в консенсусе — нет VDF, нет ожидания. Не блокирует invite slot узла для приглашений других узлов. ``` AccountInvitation: inviter_node_id 32B invited_pubkey 897B <- публичный ключ приглашённого аккаунта signature 666B <- подписано inviter node_pubkey Итого: ~1 595B ``` AccountInvitation — UserObject. Включается в user_set победителя. Валидация: 1. Подпись валидна для inviter node_pubkey из Node Table 2. inviter status = active 3. invited account_id = SHA-256("mt-account" || suite_id || invited_pubkey) не существует в Account Table При финализации в proposal P окна W: - Создать запись в Account Table (status = active, activation_window = W, balance = 0) Ограничение спама: каждый узел может пригласить не более K аккаунтов за τ₂. K = 100 (генезис). Счётчик `account_invites_this_epoch` в Node Table, сбрасывается на границе τ₂. Sybil-стоимость: 1 узел (14 дней VDF + 1 ядро) = максимум 100 аккаунтов за 14 дней. Без узла аккаунт не создать. #### Скорость роста сети Узлы: каждый узел производит максимум одно приглашение узла за ~14 дней. Рост ограничен текущим размером сети: ``` Генезис: 12 узлов 14 дней: 24 28 дней: 48 1 год: до 12 × 2^26 (~800M, теоретический максимум) ``` Аккаунты: каждый узел приглашает до K аккаунтов за τ₂. При 12 узлах: до 1 200 аккаунтов за 14 дней. При 1 000 узлах: до 100 000 аккаунтов за 14 дней. Масштабируется с ростом сети. Аппаратный бюджет сверх количества приглашений узлов бесполезен. 1000 узлов = максимум 1000 новых узлов за 14 дней, независимо от количества ядер. --- ## Потоковая модель Блоки аккаунтов текут непрерывно. Узел получает блок -> проверяет подпись FN-DSA-512 и баланс -> передаёт в P2P gossip. Время подтверждения определяется скоростью P2P-распространения (~2-5 секунд). Блок включается в канонический набор при включении победителем лотереи в proposal τ₁. Окна являются точками финализации, а не условиями приёма. Кошелёк получателя отображает входящий перевод сразу после 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 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** исключён: порядок = H(object) лексикографически, state transition детерминирован, свобода над обработкой = ноль - **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): разрешается правилом lower H(block) wins. Без санкции. **Невалидный 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. Боб: кошелёк создаёт аккаунт -> 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) Баланс Боба: увеличен на 50 Ɉ (детерминированно) Кошелёк Боба: «финализирован» ``` ### Баланс Баланс аккаунта — одно число в таблице аккаунтов. Обновляется при финализации: исходящие переводы (из StateBlock отправителя) и входящие зачисления (детерминированно по финализированным блокам). Бэкап = seed (для деривации приватного ключа FN-DSA-512). --- ## Эмиссия ### Единица 1 секунда Montana = 1 $MONT = 1 Ɉ = 1 000 mɈ = 1 000 000 μɈ = 1 000 000 000 nɈ Точность: 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 Ɉ = 1 секунда описывает скорость хода часов. Не ценовой peg, не гарантия покупательной способности. --- ## Пропускная способность Размер StateBlock: ~779B. | Канал узла | TPS | |-----------|-----| | 10 Mbps | ~1 620 | | 100 Mbps | ~16 200 | | 1 Gbps | ~162 000 | ### Адаптивный размер окна Пересчёт в τ₂: - Заполненность > 80% → увеличение размера окна - Заполненность < 20% → уменьшение размера окна - Шаг: ±20% за τ₂ - Диапазон: 1 MB — 100 MB --- ## Хранение ### Состояние (Global State) | Аккаунтов | Размер таблицы | |-----------|---------------| | 1M | ~1 GB | | 10M | ~10 GB | | 100M | ~100 GB | ### История блоков | TPS | Архивный (20 лет) | Полный (112 дней) | Pruned | Light | |-----|-------------------|-------------------|--------|-------| | 7 | ~3.4 TB | ~42 GB | Account Table | Account Table | | 100 | ~49 TB | ~600 GB | Account Table | Account Table | | 1000 | ~486 TB | ~5.9 TB | Account Table | Account Table | | Тип узла | Содержимое | Лотерея | |----------|-----------|---------| | Полный | Скользящее окно 8 × τ₂ + Global State + proposals | weight × 1 | | Архивный | Полная история от генезиса | weight × 1 | | Pruned | Global State + proposals | Наблюдатель | | Light | Global State | Наблюдатель | Узел самостоятельно выбирает тип. Участие в лотерее: полный или архивный узел. Вес определяется только количеством подписанных окон. Тип узла на вес не влияет. Архивный узел — добровольная роль. Протокол хранит доказательства (подписанные proposals навсегда). Рынок хранит исторические данные (тела блоков). Консенсус не зависит от архивов. ### 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. Блоки аккаунтов за последние 112 дней (скользящее окно) 5. Узел синхронизирован и готов к участию 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 8B signature 666B Итого: ~802B ``` 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 язык. Не нужен газ. Один тип блока, 802 байта, комиссия, навсегда. --- ## Ключи ``` 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 | ### 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 | - Альтернативные сериализации запрещены - 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 Time │ │ Децентрализованные часы │ │ │ │ TimeChain ──→ NodeChain ──→ Account │ (часы) (присутствие) (состояние) │ │ │ Account Chain (Block Lattice) │ │ Account Table, Proposals │ │ SHA-256, FN-DSA-512 │ └─────────────────────────────────┘ ```