montana/Монтана-Протокол/Архив/TimeChain v15.7.0.md

1040 lines
65 KiB
Markdown
Raw Permalink Normal View History

# TimeChain — Спецификация протокола Montana
**Версия:** 15.7.0 (2026-04-02)
## Определение
Децентрализованные криптографические часы. Сеть независимых VDF-осцилляторов, поддерживающих единую верифицируемую временную шкалу. Каждая зарегистрированная секунда = 1 Ɉ.
Основная функция — хронометраж. Вторичная — передача ценности.
Консенсус: **Proof of Time (PoT)** — последовательное SHA-256 хэширование (VDF) как цифровой осциллятор. D последовательных хэшей = одно колебание часов. Временные окна возникают из вычислений. Протокол не использует время для консенсуса — протокол и есть ход времени, оцифрованный и криптографически верифицируемый.
Генезис: 09.01.2026 00:00:00 MSK.
---
## 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 VDF, Service VDF), адреса, 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
```
Аккаунт создаётся после активации (activation_window). Вход по приглашению, одинаковый для узлов и пользователей (см. раздел «Приглашение и регистрация»).
**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
```
### Верификация баланса
Баланс в 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
activation_window 4B <- окно активации (invite_window + 20 160)
status 1B <- pending | active
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 <- окно финализации Invitation (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 VDF → Service VDF → Execution.
### TimeChain VDF — осциллятор
Первичный продукт протокола. Непрерывная последовательная SHA-256 цепочка — цифровой осциллятор Montana Time:
```
T_r = SHA-256^D(T_{r-1})
```
D — количество последовательных хэшей за одно окно τ₁. Каждый хэш — один тик осциллятора. D хэшей — одно колебание. TimeChain продвигается по расписанию окон. Для фиксированного индекса r значение T_r совпадает у всех честных узлов. Каждый узел вычисляет TimeChain независимо — результат детерминирован.
TimeChain не зависит от состояния, транзакций и поведения отдельных узлов. Даже при отказе всего Execution слоя часы продолжают тикать.
### Service VDF — персональный VDF узла
Доказательство непрерывного присутствия конкретного 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 последовательных хэшей за окно — доказательство работы.
Инициализация: для первого окна нового узла предыдущий endpoint отсутствует. Service init привязан к каноническим данным proposal в котором Invitation финализирован:
```
S_{i,0,0} = SHA-256("mt-service-init" || control_root || timechain_value || node_id_i)
```
control_root и timechain_value из proposal header окна финализации Invitation. Оба канонические (не зависят от субъективного user_set). Предвычисление VDF невозможно — timechain_value неизвестен до закрытия окна. Grinding surface = ноль. Верифицируем любым узлом.
Service зависит от TimeChain. TimeChain не зависит от Service.
### 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 верифицируем: пересчёт Service VDF от предыдущего endpoint (или от service_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 за это окно.
### Execution — содержимое блока
Приём, верификация execution objects и формирование набора. Два класса объектов:
**UserObjects** — пользовательские операции:
| Тип | Описание | Валидация |
|-----|----------|-----------|
| StateBlock | Перевод средств | FN-DSA-512 подпись, баланс >= 0, prev_hash, link_amount > 0, fee >= min_fee, получатель существует в Account Table |
| OpenAccount | Создание аккаунта | FN-DSA-512 подпись, activation_window <= current_window |
| ChangeKey | Смена ключа | FN-DSA-512 подпись старым ключом, new_pubkey |
**ControlObjects** — объекты управляющие составом сети:
| Тип | Описание | Валидация |
|-----|----------|-----------|
| Invitation | Приглашение нового узла | 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 детерминирован — каждый узел вычисляет его независимо. Service VDF для окна 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) лексикографически.
Invitation: записать 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.
Шаг 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 Table где activation_window <= current_window
и status = pending -> status = active.
Шаг 6: вычислить new_state_root.
```
Порядок детерминирован. Control_set первым, user_set вторым, TimeCoin третьим, chain_length четвёртым, expiry последним. Каждый узел применяет одну и ту же последовательность шагов к одним и тем же наборам и получает один и тот же new_state_root.
Execution зависит от TimeChain и Service. Обратных зависимостей нет.
С ростом TPS сети дополнительные ядра подключаются для верификации блоков. Минимум для валидатора: 3 логических ядра (TimeChain + Service + Execution). Верификация блоков аккаунтов полностью параллелизуется — цепочки аккаунтов независимы.
### Приглашение и регистрация
Вход в сеть — по приглашению. Процедура одинакова для узлов и пользователей: приглашение → 14 дней ожидания → активация аккаунта. Каждый зарегистрированный узел может пригласить одного нового участника. Пока приглашённый в процессе регистрации, пригласивший не может приглашать других. Одновременно одно приглашение на узел.
Генезис: 12 узлов в разных локациях (hardcoded, аналог bootstrap nodes в Bitcoin).
#### Invitation
Зарегистрированный узел публикует приглашение:
```
Invitation:
inviter_node_id 32B
invited_pubkey 897B <- публичный ключ приглашённого
invited_type 1B <- node | account
signature 666B <- подписано inviter node_pubkey
Итого: ~1 596B
```
Invitation не содержит start_window — он определяется при финализации.
Валидация Invitation:
1. Подпись валидна для inviter node_pubkey из Node Table
2. inviter status = active
3. inviter pending_invite = 0x00..00 (нет активного приглашения)
4. Для node: invited node_id = SHA-256("mt-node" || invited_pubkey) не существует в Node Table
5. Для account: invited account_id = SHA-256("mt-account" || suite_id || invited_pubkey) не существует в Account Table
При финализации в proposal P окна W:
- inviter pending_invite = invited node_id или account_id
- inviter invite_window = W
- inviter invite_expires = W + 21 160 (20 160 окон + 1 000 окон запас)
- Для account: создать запись в Account Table (status = pending, activation_window = W + 20 160, balance = 0)
#### Привязка VDF к моменту приглашения
VDF seed приглашённого узла привязан к каноническим полям proposal в котором Invitation финализирован:
```
service_init = SHA-256("mt-service-init" || control_root || timechain_value || node_id)
```
control_root и timechain_value — канонические поля из proposal header окна финализации. Не зависят от субъективного user_set победителя. Предвычисление VDF невозможно: timechain_value неизвестен до закрытия окна.
Приглашённый узел узнаёт control_root и timechain_value только увидев финализированный proposal → вычисляет service_init → начинает VDF с окна W+1.
#### Регистрация узла
Приглашённый узел после финализации Invitation:
1. Наблюдает proposal с Invitation → получает control_root и timechain_value
2. Вычисляет service_init = SHA-256("mt-service-init" || control_root || timechain_value || node_id)
3. Непрерывно вычисляет Service VDF: 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:
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. Вычислить service_init = SHA-256("mt-service-init" || control_root || timechain_value || node_id) из proposal окна invite_window
7. proof_endpoint верифицируем: пересчёт VDF от service_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 пригласившего очищаются автоматически. Узел может приглашать снова.
#### Скорость роста сети
Каждый узел производит максимум одно приглашение за ~14 дней. Рост ограничен текущим размером сети:
```
Генезис: 12 узлов
14 дней: 24
28 дней: 48
1 год: до 12 × 2^26 (~800M, теоретический максимум)
```
Аппаратный бюджет сверх количества приглашений бесполезен. 1000 узлов = максимум 1000 новых за 14 дней, независимо от количества ядер.
---
## Потоковая модель
Блоки аккаунтов текут непрерывно. Узел получает блок -> проверяет подпись FN-DSA-512 и баланс -> передаёт в P2P gossip. Время подтверждения определяется скоростью P2P-распространения (~2-5 секунд).
Блок включается в канонический набор при включении победителем лотереи в proposal τ₁. Окна являются точками финализации, а не условиями приёма.
Кошелёк получателя отображает входящий перевод сразу после P2P-валидации (~2-5 секунд) как «подтверждён, ожидает финализации». После включения в proposal τ₁ — «финализирован». Подтверждение — безопасность подписи. Финализация — каноническое состояние.
Цепочки аккаунтов полностью независимы. Блоки разных аккаунтов обрабатываются параллельно без конфликтов.
---
## Временные слои (τ)
```
τ₁ (60с) → τ₂ (20 160 × τ₁ ≈ 14 дней)
```
Одно окно — τ₁. Всё остальное — производные.
### τ₁ — Окно (60 секунд)
Единственная единица протокольного времени. Слот, финализация и регистрация времени — одно и то же.
- TimeChain продвигается на D хэшей
- Service VDF продвигается на 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
- Узлы раскрывают Service VDF 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» в Execution)
- 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.8, D_old × 1.2)
```
Медиана > 60 → окна медленнее цели → D слишком велик → уменьшить. Медиана < 60 окна быстрее D увеличить.
m калибруется пропорционально: `m_new = m_old × (D_new / D_old)`.
Генезис: D₀ и m₀ калибруются при запуске для целевых 60 секунд. Абсолютный якорь: 09.01.2026 00:00:00 MSK. Шаг: ±20% за τ₂. Медиана 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** — осциллятор. Первичный продукт протокола. Чистая VDF-цепочка `T_r = SHA-256^D(T_{r-1})`. Глобальные цифровые часы и источник случайности. Продвигается по расписанию окон.
**Service VDF** — свидетельство присутствия. Персональный VDF, якорится в TimeChain каждое окно. Доказывает непрерывное присутствие node_id. Раскрытие endpoint при закрытии τ₁ = подпись окна = +1 к весу.
**Execution** — содержимое блока. Два набора: control_set (каноничен, все ControlObjects) + user_set (из мемпула победителя). Порядок и обработка детерминированы.
Зависимости односторонние: TimeChain VDF → Service VDF → Execution. Отказ в Execution не останавливает часы. Отказ конкретного узла в Service не заражает общий ритм.
### Стаж и вес
#### Определение
Вес узла — суммарное количество подписанных τ₁:
```
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 означает: узел участвует в сети (валидация, ретрансляция), но не участвует в лотерее и не может быть победителем τ₁.
### Победитель τ₁
Победитель определяется после закрытия окна τ₁. Каждый узел раскрывает свой Service VDF 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, Service VDF endpoint, proposal}`.
Верификация Service VDF за одно окно: пересчёт 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 дней) + Service VDF (физическое ядро) + линейный рост веса. Скорость роста 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. Блоки скользящего окна обеспечивают верификацию недавних переходов.
---
## Ключи
```
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 и Service VDF endpoints). account_id и node_id выводятся из публичных ключей, верифицируемы без знания seed. Бэкап = seed.
---
## Криптографическая реализация
### Primitive layer
Собственная реализация криптографических примитивов запрещена. Только audited библиотеки с constant-time гарантиями и опубликованными test vectors.
| Примитив | Стандарт | Роль |
|----------|----------|------|
| SHA-256 | FIPS 180-4 | TimeChain VDF, Service VDF, адреса, 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-service` | Service VDF seed |
| `mt-equivocation` | Хэширование equivocation proofs |
| `mt-confirmation` | Хэширование async confirmations |
- Альтернативные сериализации запрещены
- Test vectors для каждого консенсусного объекта
- Cross-implementation conformance tests перед запуском mainnet
### Protocol layer
Собственная реализация поверх криптографического ядра:
| Компонент | Назначение |
|-----------|------------|
| Merkle-деревья | State Root, blocks_root (из SHA-256 вызовов) |
| VDF scheduling | Управление TimeChain и Service цепочками |
| 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 ──→ Service ──→ Execution
│(осциллятор)(присутствие)(состояние)
│ │
│ Account Chain (Block Lattice) │
│ Account Table, Proposals │
│ SHA-256, FN-DSA-512 │
└─────────────────────────────────┘
```