montana/Монтана-Протокол/Код/ROADMAP.md

106 KiB
Raw Blame History

Roadmap — Montana Reference Implementation

Spec target: см. VERSION.md (single source of truth) Implementation version: 0.0.0 (pre-release, в разработке)


Scope

В scope: protocol core согласно спеке Montana (версия — VERSION.md) — криптография, TimeChain, NodeChain, AccountChain, Account Table, consensus (Proof of Time), P2P transport, Fast Sync, node binary.

Вне scope (отдельный workspace в будущем): Application Layer по спеке Montana App v2.4.2.md — Juno agent, messaging, файловое хранилище, профили, клиентская LLM runtime.


Принципы разработки

  1. Одна функция за раз. Explain → Write + tests → Test → Commit. Никаких «напишу весь модуль сразу».
  2. Success criteria блок до кода. Для каждой consensus-critical функции — spec quote + контракт + чек-лист критериев (см. CLAUDE.md → Verifiable success criteria).
  3. Автокоммит. Любое изменение в Протокол/Код/ автоматически завершается git commit. См. CLAUDE.md → Git discipline.
  4. Четыре обязательные команды зелёные перед каждым коммитом:
    • cargo fmt --all -- --check
    • cargo clippy --all-targets -- -D warnings
    • cargo test --all
    • cargo build --all --release
  5. Byte-for-byte детерминизм. Custom canonical encoding, explicit little-endian, BTreeMap вместо HashMap в consensus, no floats, no system clock. См. CLAUDE.md → Byte-for-byte determinism.
  6. Ссылки на спеку в коде. Каждое consensus-critical решение помечается комментарием // spec, раздел "<название>" без версии. Источник истины версии — VERSION.md. При spec bump VERSION.md обновляется, spec-комментарии в коде не трогаются, если раздел не переименован.
  7. Demo откладываются до готовности протокола. До M4 (consensus core) отдельные examples/demo.rs в пакетах не пишем — юнит-тестов достаточно для sanity check. После M4 создаём examples/ в корне workspace с end-to-end сценариями (OpenAccount → Transfer → BundledConfirmation → apply_proposal), когда есть реальный flow между пакетами.

Статусы crates

  • TODO — не начато, ожидает своей очереди
  • In progress — начата работа, не завершено
  • Written — код написан, тесты есть, ожидает review
  • Committed — зафиксировано в git, закрыто
  • Blocked — заблокирован gap в спеке, внешней зависимостью или другим crate

15 Crates — статус и зависимости

# Crate Milestone Зависит от Раздел спеки Статус
1 mt-codec M1 Consensus encoding layer Committed (90464a8)
2 mt-crypto M1 sha2, pqcrypto-falcon Криптография + Primitive layer Committed (df55372)
3 mt-merkle M1 mt-codec, mt-crypto Sparse Merkle Tree Committed (f242956)
4 mt-genesis M1 mt-codec, mt-crypto Genesis Decree + protocol params Committed (45c1e84)
5 mt-state M2 mt-codec, mt-crypto, mt-merkle Состояние сети (Account/Node/Candidate Tables + roots) Committed (ab99e23)
6 mt-timechain M2 mt-codec, mt-crypto, mt-genesis, mt-state TimeChain VDF + Adaptive D + cemented_bundle_aggregate Committed (76ed8da)
7 mt-account M3 mt-codec, mt-crypto, mt-state, mt-genesis, mt-timechain AccountChain operations + apply_proposal Committed (Phase A..F closed, 102 tests)
8 mt-lottery M4 mt-crypto, mt-state Node/Account lottery + BundledConfirmation Closed (6 phases, 96 тестов)
9 mt-consensus M4 mt-state, mt-lottery, mt-timechain Proposal + Lookback Leadership + fallback Closed (5 phases, 60 тестов)
10 mt-entry M4 mt-state, mt-timechain NodeRegistration + selection event + adaptive VDF Closed (5 phases, 39 тестов)
11 mt-store M5 mt-state, mt-consensus Filesystem persistence Closed (5 phases, 24 теста)
12 mt-net + mt-net-transport M6 mt-consensus, libp2p libp2p + IBT + Wire format + Dandelion++ + Mesh + S&F Closed (Phase A-G + C.0-C.4, 124 tests, in-process e2e; cross-machine pairing defer M8)
13 mt-sync M7 mt-state, mt-net Fast Sync TODO
14 montana-node M8 все Бинарь + CLI Closed (byte-exact rewrite через canonical apply_proposal, commit fb204ef; DEV-001..DEV-009 closed; DEV-010 acknowledged genesis bootstrap)
15 mt-conformance M9 все Test vectors + conformance suite READY initial (envelope A1-A3 + IBT B1 + PoW F1/F2); expansion в работе (12 TBD-A markers)

Milestones

M0 — Workspace skeleton

  • Cargo workspace, toolchain pin, rustfmt/clippy configs
  • VERSION.md, README.md, ROADMAP.md
  • Git repository в Протокол/Код/, parent gitignore настроен
  • 4 обязательные команды зелёные
  • [i] mt-version stub crate (удалён 2026-04-17 как нарушение SSOT [C-1])

Результат: база для всей реализации. Commit e2457ad.

M1 — Foundational layer ЗАКРЫТ

Четыре параллельных crate: fundamentals криптографии и сериализации.

  • mt-codec — canonical encoding (u8/u16/u32/u64/u128 LE, fixed bytes, 30 domain separators). 26 тестов. Commit 90464a8.
  • mt-crypto — SHA-256 + FN-DSA-512 обёртки (Hash32, PublicKey 897B, SecretKey 1281B, Signature 666B, SuiteId). 18 тестов. Commit df55372.
  • mt-merkle — sparse Merkle tree глубины 256, InclusionProof + verify_proof, empty_internal через OnceLock. 25 тестов. Commit f242956.
  • mt-genesis — ProtocolParams (25 полей, 2017 байт canonical), Genesis State Hash formula, OnceLock singleton. 19 тестов + 1 ignored (bootstrap keypairs TBD). Commit 45c1e84.

Итог M1: 4 пакета (mt-codec, mt-crypto, mt-merkle, mt-genesis), 88 тестов зелёных, ~1680 строк кода. Foundational layer закрыт. Найдена ambiguity в спеке: Genesis State Hash формула без domain separator — единственный такой случай, flagged для spec author review.

M2 — State & time ЗАКРЫТ

  • mt-state — Account Table, Node Table, Candidate Pool записи (1000/1043/1027 B) + три sparse Merkle tree + state_root композиция + derive_account_id/derive_node_id + is_active predicate. 32 теста. Commit ab99e23.
  • mt-timechain — vdf_step/verify (T_r = SHA-256^D), next_d (Adaptive D ±3%), cemented_bundle_aggregate (unpredictable-offline binding, 3 ветви). 23 теста. Commit 76ed8da.

Итог M2: 2 пакета, 55 тестов зелёных, ~1000 строк кода. State types + canonical time + anti-grinding binding готовы. Материализация Genesis state (создание bootstrap AccountTable/NodeTable записей) — отложена в M3, зависит от apply_proposal для консистентности.

M3 — AccountChain ЗАКРЫТ

Один пакет mt-account, 6 phases закрыты. 102 теста, ~1850 строк кода.

Phase Scope Commit
A operation types + canonical encoding + op_hash (identifier via signed_scope после v29.7.0) 0efd92d → refactor 1af1fff
B validation (OpError + 5 функций) cf95eaf
C apply individual operations 8fcca20
D emission (reward/bonus/bootstrap_cumulative/supply) 36503dc
E apply_proposal partial (steps 2/3.5/3.6/4, stubs 1/3a/3b) 5ab2a45
F Genesis state materialization 30400eb

Критерий закрытия выполнен: два аккаунта могут обменяться Transfer, balance updated, state_root детерминирован (проверено тестами apply_transfer_sum_delta_balance_is_zero, apply_proposal_state_root_deterministic).

M4 — Consensus core 🔄 Next

Три пакета параллельно — сердце консенсуса. Зависимости между ними:

mt-lottery (идентификация winner) ──┐
                                     ├─→ mt-consensus (proposal сборка)
mt-entry (node admission)  ─────────┘

mt-lottery TODO

Раздел спеки: «VDF Reveal и лотерея» (~строка 754+), «Confirmer threshold», «cemented_bundle_aggregate» (уже в mt-timechain).

  • Phase A. BundledConfirmation тип + layout (24 теста)
    • struct BundledConfirmation { node_id, endpoint, window_index, op_hashes[], reveal_hashes[], signature } — counts встроены через Vec::len(), encoded как u16 LE префиксы
    • CanonicalEncode + encode_signed_scope (SSI R1), bundle_hash = identifier (SSI R2, class "mt-bundle")
    • BundleError (6 вариантов): UnknownNode, UnsupportedSuite, OpsOutOfOrder, RevealsOutOfOrder, WrongEndpoint, InvalidSignature
    • validate: strict ascending op_hashes/reveal_hashes, endpoint == expected_T_r (caller), signature verify by NodeTable[node_id].node_pubkey
  • Phase B. VDF_Reveal тип + layout (15 тестов)
    • struct VdfReveal { node_id, window_index, endpoint, signature } — REVEAL_SIZE = 734 B
    • CanonicalEncode + encode_signed_scope (SSI R1), reveal_hash = identifier (SSI R2, class "mt-vdf-reveal")
    • compute_endpoint(t_r, cba_w_minus_2, node_id, window_index) = SHA-256("mt-lottery" || T_r(W) || cba(W-2) || node_id || window_index LE)
    • RevealError (5 вариантов): UnknownNode, UnsupportedSuite, WrongWindow, WrongEndpoint, InvalidSignature
    • validate_reveal: спек-правила 1, 2, 3, 5 («Валидация VDF_Reveal», строки 1020-1026). Правило 4 (weighted_ticket < target) — в Phase C
  • Phase C. Node lottery: weighted_ticket_node (24 теста)
    • seniority_bonus(chain_length, snapshot) = min(chain_length/69, snapshot) — u64 unsigned
    • lottery_weight = snapshot + seniority_bonus — u64 unsigned, DS-2 floor ≥ 1
    • log2_q64(endpoint) — bit-scan leading_zeros + нормализация мантиссы в [2^127, 2^128) + degree-3 Remez minimax polynomial log2(1+y) ≈ B0 + y·(B1 - y·(B2_abs - y·B3)) (halved form, unsigned u64 coefficients; max error 2^-10.62)
    • ln_q64(endpoint) = log2_q64 × LN2_Q64 >> 64, LN2_Q64 = 0xB17217F7D1CF79AB
    • weighted_ticket_node = ln_q64(endpoint) / (lottery_weight as u128) — u128 integer div toward zero
    • Monotonicity + determinism + boundary tests + 5 binding test vectors TV1-TV5 from spec
    • [I-9] compliance closed (binding coefficients B0..B3 + 14 binding test vectors total: 5 ln_q64 + 5 weighted_ticket_node + 4 weighted_ticket_account)
  • Phase D. Account lottery: weighted_ticket_account (13 тестов)
    • compute_account_endpoint(account_id, op_hash, t_r, cba_w_minus_2) = SHA-256("mt-account-lottery" || ...)
    • weighted_ticket_account = ln_q64(endpoint) / (account_chain_length_snapshot as u128) — u128 совместим с node ticket для argmin
    • AccountLotteryError (2 варианта): OperatorExcluded, ZeroSnapshot
    • validate_account_participation(is_node_operator, snapshot) — spec правила 2, 3 из «Валидация участия аккаунта»
  • Phase E. Winner determination (12 тестов)
    • Candidate { ticket: u128, class: u8, id: [u8;32] } + Winner
    • determine_winner(candidates) — argmin by (ticket asc, class asc, id lex asc) canonical rule
    • sorted_candidates_for_fallback — для fallback cascade
    • Tie-breaking ambiguity в спеке (probability ~ 2^-128) закрыт canonical rule в коде
  • Phase F. Quorum calculation (8 тестов)
    • quorum(active_chain_length) = (67 × X + 99) / 100 — u64 unsigned, [I-9] compliant (spec vectors passed)
    • is_cemented(cemented_sum, active) = cemented_sum ≥ quorum

mt-consensus TODO

Раздел спеки: «Закрытие окна (Lookback Leadership Finalization)», «Proposal header», «Canonical acceptance», «fallback cascade».

  • Phase A. Proposal header тип + layout (22 теста)
    • ProposalHeader struct, 18 полей, PROPOSAL_HEADER_SIZE = 1080 B (target 16B u128, winner_class ∈ {1,2} validated)
    • encode_signed_scope (R1) + proposal_hash (R2, class "mt-proposal")
    • HeaderError: UnknownProposer, UnsupportedSuite, InvalidSignature, WindowNotMonotone, ProtocolVersionDecreased, ProtocolVersionUnsupported, FallbackDepthZero
    • validate_header: invariants window monotone, protocol_version monotone + ≤ local_max, fallback_depth ≥ 1, signature R1
  • Phase B. Lookback Leadership — proposer_W = winner_{W-2} (11 тестов)
    • canonical_proposer(current_window, bootstrap, sorted_candidates_W-2) — первый node в sorted list
    • fallback_proposer(...) — Nth node для fallback cascade
    • Genesis bootstrap (W<2) + extended bootstrap (нет nodes в candidates)
    • Account winner case — proposer = ближайший node (spec строка 1315)
  • Phase C. control_set формула (10 тестов)
    • ControlObjectRef { op_hash, cemented_window }
    • compute_control_set(all_cemented, prev_window, W) — filter + sort (window asc, op_hash lex asc)
    • validate_control_set — равенство проверяется byte-exact
  • Phase D. Canonical acceptance validation (13 тестов)
    • AcceptanceError: ProposerNotCanonical, InsufficientBundles, IncludedRevealsMismatch, WrongWinner
    • validate_proposer_is_canonical(header, bootstrap, sorted_W-2) — сверка с fallback_proposer(depth)
    • validate_bundles_threshold — делегирует mt_lottery::is_cemented
    • validate_included_reveals — byte-exact equality reveal_hashes
    • validate_winner — argmin через mt_lottery::determine_winner
    • state_root verification — делегирован в mt-account::apply_proposal
  • Phase E. Finalization flow (4 теста)
    • FinalizationStatus { Cemented, Rejected } + finalization_status(sigs_sum, active)
    • leader_penalty_excluded_node(header) → NodeId для exclusion из lottery текущего окна

mt-entry CLOSED (5 phases, 39 тестов)

Раздел спеки: «Вход и регистрация», «apply_proposal Шаг 1/3a/3b», «Adaptive VDF».

  • Phase A. NodeRegistration (11 тестов)
    • struct NodeRegistration, NODE_REGISTRATION_SIZE = 1646 B
    • encode_signed_scope + nodereg_hash (R2 "mt-nodereg")
    • NodeRegError: UnsupportedSuite, InvalidSignature, NodeIdAlreadyIn{NodeTable,CandidatePool}, OperatorAccount{NotFound,AlreadyNode}, WStartOutOfRange, VdfChainTooShort
    • validate_noderegistration (structural checks 1-3)
  • Phase B. candidate_vdf_init + Candidate Pool (4 теста)
    • candidate_vdf_init(t_r, cba, node_id) = SHA-256("mt-candidate-vdf-init" || ...)
    • compute_expiry_window = registration + 3τ₂
    • apply_candidate_expiry(pool, window) — шаг 3a
  • Phase C. Selection event (9 тестов)
    • selection_slots(active_nodes) = max(1, active/130) — 1% cap
    • selection_sort_key(t_r, cba, node_id) = SHA-256("mt-selection" || ...)
    • is_selection_window(W) — каждые 336 окон
    • rank_candidates_for_selection + apply_selection_event — шаг 3b (chain_length=1 активация)
  • Phase D. Adaptive VDF (6 тестов)
    • required_vdf_length(pending, active, τ₂) — integer form через permille (per [I-9])
    • pressure_permille > 10 (1%) → τ₂ × pressure_permille / 10; иначе τ₂
  • Phase E. apply_proposal orchestration (5 тестов)
    • nr_sort_key(t_r, cba, node_pubkey) per spec строки 1838-1843
    • apply_noderegistrations_batch — incremental apply с pending growth
    • Caller (mt-node / integration test) orchestrates: shag 1 = batch, 3a = expiry, 3b = selection, затем mt-account::apply_proposal для steps 2/3.5/3.6/4

Критерий закрытия M4: 2 узла могут запустить локальную сеть in-memory, проходить окна, рассчитывать winner, cementing proposals. Полный state transition apply_proposal замкнут.

M5 — Persistence Next after M4

Раздел спеки: «Fast Sync», «Хранение».

Цель: локальный узел перезапускается с восстановлением state с диска; crash-consistent между apply_proposal commit-ами.

mt-store CLOSED (5 phases, 24 теста)

Реализация: filesystem + fixed-size records (не RocksDB/sled — pure std::fs, минимум deps, максимум простоты для Manual Validation Gate).

  • Phase A. FsStore
    • FsStore::open(path) — создаёт root + proposals/ subdirectory
    • StoreError: Io, CorruptedLength, ParseFailed, NotFound
  • Phase B. Table persistence
    • save/load_account_table — canonical_encode concat → accounts.bin, fixed-size parse
    • save/load_node_table — nodes.bin
    • save/load_candidate_pool — candidates.bin
    • Decode функции — inverse CanonicalEncode, byte-exact (root byte-equal round-trip verified)
  • Phase C. Proposal archive
    • archive_proposal(header)proposals/{window:020}.bin
    • get_proposal_by_window(W) → Option, byte-exact decode
    • Test 100 proposals random access passes
  • Phase D. Crash recovery
    • meta.last_cemented.bin — u64 LE, last committed window
    • verify_consistency() — meta указывает на archived proposal?
    • Test: meta=100 без archive → NotFound finding
  • Phase E. Pruning
    • prune_proposals_before(threshold) — delete files window < threshold
    • AccountTable/NodeTable/CandidatePool untouched
    • Test prune 9/20 proposals, current state preserved

Критерий закрытия M5 passes:

  1. cargo test -p mt-store = 24/24 ✓
  2. full_restart_cycle_state_preserved integration test — open → populate → save → close → reopen → load → roots byte-equal ✓
  3. verify_consistency_detects_missing_proposal fault flagging ✓

Hands-on example examples/m5_persist.rs — будет delivered в Validation Gate.


Локальный shakedown — Manual Validation Gate (между M5 и M6)

Смысл: до написания сетевого слоя (M6 libp2p, Dandelion++, IBT) автор вручную на своей машине прогоняет каждую шестерёнку протокола с hands-on example binaries. Архитектор (я) объясняет output, критик (я в другой роли) задаёт adversarial вопросы на каждом шаге. Цель — убедиться что протокол работает вживую, не только в unit tests.

Режим прохождения — incremental, scenario-by-scenario

Зафиксировано автором 2026-04-20. Gate проходится пошагово, по одному сценарию за сессию:

  1. Architect пишет examples/mN_name.rs binary только для текущего сценария
  2. Автор запускает команды на своей машине, копирует output
  3. Architect разбирает output построчно, сверяет с expected
  4. Критик задаёт adversarial checklist
  5. Автор либо подтверждает, либо флагует finding
  6. Всё OK → статус сценария ✅ passed {дата} в tracker-е ниже
  7. Finding → fix → повторный прогон → retry до passed

Ценность incremental над batch: ошибки ловятся в момент написания соответствующего binary; не накапливаются до середины Gate. Каждая шестерёнка exercised отдельно.

Scenario crate: все binaries живут в crates/mt-examples/ — отдельный crate с dependencies на все необходимые modules, каждый scenario — отдельный examples/mN_*.rs (cargo example). Создаётся в первой Gate сессии.

Audit findings (M1-F audit closure — все 7 closed)

M1-F audit критика реализации (2026-04-26). 7 findings surface-нуты, все 7 закрыты конструкцией. Status: READY FOR EXTERNAL AUDIT (M1 foundational layer scope). Audit package: AUDIT.md + docs/audit-checklist.md.

Finding Закрытие Commit
F-1: нет Drop+zeroize для SecretKey/MlkemSecretKey workspace dep zeroize=1.8.1 + Drop impls 3333738
F-2: panic в lib коде на FFI ошибку sign/keypair_from_seedResult<_, CryptoError> e1164ad
F-3: KAT-baselines self-derived без NIST FIPS oracle NIST ACVP differential testing — 51/51 byte-exact 6b7ff30
F-4: cfg!(target_os) неверно для cross-compile CARGO_CFG_TARGET_OS env var 9f2ba93
F-5: keypair() слабая энтропия в публичном API #[cfg(any(test, feature = "testing"))] гейт e1164ad
F-6: один error code маскирует разные fail paths 6 новых раздельных error codes (7-12) 71896f6
F-7: SecretKey::from_array без validation implicit closure через F-2 (invalid SK → Err при первом sign) e1164ad

F-3 closure detail (NIST ACVP differential testing):

Sparse clone https://github.com/usnistgov/ACVP-Server (Apache-2.0, public domain test vectors из NIST CAVP). Extracted ML-DSA-65 + ML-KEM-768 KeyGen + SigGen deterministic test cases в crates/mt-crypto-native/tests/fixtures/nist_acvp/ (~500 KB JSON, 3 files). Integration test crates/mt-crypto-native/tests/nist_acvp_kat.rs runs differential testing.

51/51 NIST KAT byte-exact PASS:

  • ML-DSA-65 KeyGen 25/25 (FIPS 204 Algorithm 1 deterministic seed → pubkey/secretkey)
  • ML-KEM-768 KeyGen 25/25 (FIPS 203 Algorithm 16 (d, z) → ek/dk)
  • ML-DSA-65 SigGen deterministic external pure empty-context 1/1 (FIPS 204 Algorithm 2 — Montana usage pattern)

Conformance proof: OpenSSL 3.5.5 LTS backend через mt-crypto-native FFI производит выходы байт-в-байт идентичные NIST FIPS 204/203 reference. Cross-implementation conformance доказана на public NIST oracle.

Reproduction:

cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo test -p mt-crypto-native --test nist_acvp_kat -- --nocapture

Known deferred (lesser scope, явно документированы в AUDIT.md §3):

  • ML-DSA-65 SigGen с non-empty context — current API не принимает context parameter (Montana usage pattern не использует FIPS context); расширение FFI signature когда понадобится
  • ML-DSA-65 SigVer NIST KAT direct — косвенно подтверждено через round-trip + NIST sign byte-exact
  • ML-KEM-768 Encapsulate/Decapsulate NIST KAT — M1-F scope только KeyGen; encapDecap нужен в M6+ application layer

Эти limitations задокументированы с closure path и не препятствуют external audit M1 foundational layer.

Status tracker

Scenario Binary Status Дата
0. User onboarding (24-word mnemonic → identity) mt-examples/examples/m1_mnemonic.rs passed (positive end-to-end: 6/6 PASS — seeds, keypair (terminal), recovery-fingerprint, mnemonic, vectors (6 binding), roundtrip; adversarial negative-cases — unit tests mt-mnemonic отдельно, не покрыто example subcommands) 2026-04-30
1. M1 Crypto shakedown mt-examples/examples/m1_crypto.rs passed (5/5 PASS — keypair-deterministic, keypair-random, sign, hash, merkle-empty; finding в Subsection 4 cmd_sign: ожидание sig1 != sig2 противоречило spec строке 5286 «deterministic ML-DSA-65, RND = 0x00 × 32» — assertion инвертирована, title переписан на «Determinism…») 2026-04-30
2. M2 TimeChain + State mt-examples/examples/m2_timechain_state.rs passed (5/5 PASS — vdf-forward 1000, next-d-boundaries, cba-branches, state-root-compose, merkle-inclusion; binary написан 2026-04-30 коммитом 7e333e6, прогнан автором на iMac) 2026-04-30
3. M3 Account operations mt-examples/examples/m3_account.rs TODO
4. M4 Full consensus cycle mt-examples/examples/m4_local_net.rs TODO
5. M5 Persistence + restart mt-examples/examples/m5_persist.rs TODO

M6 unblock criterion: все 6 строк status = ✅ passed.

Starter instructions — начало следующей сессии

Стандартная последовательность для новой сессии после этой точки:

  1. Архитектор реализации погружается в роль. Прочитать Код/CLAUDE.md (v1.4.0+) построчно. Показать критерии работы. Проверить VERSION.md — актуальный Spec target (на 2026-04-21 = Montana v29.13.0).

  2. Sanity check build state:

cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings && cargo test --all 2>&1 | grep -E "test result.*passed|FAILED" | tail -5

Ожидание: fmt/clippy clean, 550+ tests passed, 0 failed.

  1. Выбрать следующий сценарий Validation Gate (incremental, один за сессию). Первый pending — сценарий 0 «User onboarding». Последовательность: 0 → 1 → 2 → 3 → 4 → 5.

  2. Прогон сценария:

    • Автор copy-paste команды из соответствующего блока «Сценарий N» ниже.
    • Автор запускает на своей машине, копирует output в чат.
    • Архитектор разбирает построчно, сверяет с acceptance criteria.
    • Критик (роль Код/CRITIC.md v1.3.0+) применяет adversarial checklist + min 2 perspectives с отдельными выводами per perspective.
    • Всё OK → сценарий ✅ passed {дата} в status tracker.
    • Finding → architect предлагает fix → автор apply делай → retry.
  3. M6 разблокируется когда все 6 сценариев passed. До этого — manual validation приоритет выше любых новых features.

Policy

  • Binary пишет в stdout структурированные events (hashes, state roots, winners, errors). Автор читает output глазами.
  • Формат сценария ниже: binary → команды → expected output → architect gate → critic checklist.
  • M6 не начинается пока ВСЕ 5 сценариев не passed с подписью архитектора и критика.

Формат каждого сценария

Сценарий N: {название}
Фаза покрытия:     {M1/M2/...}
Prerequisite:      {предыдущие сценарии}
Binary:            examples/mN_name.rs
Команды (one-line shell, абсолютные пути):
  cd "..." && cargo run --release --example mN_name -- {args}
Expected output pattern:
  {literal prefix / regex}
Architect gate:    {acceptance criterion}
Critic checklist:  {5-10 adversarial вопросов}

Сценарий 0 — User onboarding (24-word mnemonic → derived identity)

Binary: examples/m1_mnemonic.rs

Демонстрирует production user flow: как реальный пользователь создаёт identity в Montana — от 256-битной entropy через 24-словную мнемонику к master_seed и per-role keypair seeds для трёх ролей (account, node, app-encryption).

Что проверяем:

  • Canonical wordlist — 2048 слов, binding SHA-256 fingerprint 2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda матчит
  • M-1 Algorithm (mnemonic_to_master_seed) — PBKDF2-HMAC-SHA-256, salt="mt-seed", iter=2²⁰ → 64-байтовый master_seed
  • 24-слов mnemonic encoding — entropy (256 bit) + 8-bit checksum = 264 bit → 24 × 11 bit = 24 слова (последнее включает checksum)
  • Per-role HKDF-Expand — master_seed + info ("mt-account-key" / "mt-node-key" / "mt-app-encryption-key") → 48B/48B/64B seeds
  • Byte-exact binding vectors — 6 штук в спеке (3 entropy → master_seed + 3 per-role derivation)

Команды (actual subcommands):

cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_mnemonic -- vectors
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_mnemonic -- mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_mnemonic -- keypair 0000000000000000000000000000000000000000000000000000000000000000
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_mnemonic -- roundtrip 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_mnemonic -- all

Expected output markers:

  • vectors: «6 BINDING TEST VECTORS» секция, все с byte-exact match true; wordlist fingerprint verified
  • mnemonic "<24 words>": displayed 24 слова из canonical wordlist, master_seed 64 bytes displayed, 3 derived seeds (48B/48B/64B) displayed
  • keypair <hex32>: entropy hex → mnemonic 24 words → master_seed → 3 roles seeds
  • roundtrip: entropy → mnemonic → master_seed → mnemonic (roundtrip) → same master_seed idempotency proof
  • all: запускает все 4 subcommands подряд; finale [result] ALL M1 MNEMONIC: PASS

Architect gate:

  • 6 binding test vectors byte-exact match true
  • 24 слова actual visible в output (не просто count)
  • Wordlist fingerprint matches binding value
  • Roundtrip idempotency verified
  • Три per-role seeds distinct для same master_seed (HKDF domain separation works)

Critic adversarial checklist (применять с min 2 perspectives per CRITIC.md v1.3.0):

Perspective — Cryptographer:

  • PBKDF2 iter = 2²⁰ actual (не меньший для performance)?
  • HKDF info domain separation между three roles gives different seeds?
  • Wordlist fingerprint tampering detected (mutate one word bit → verify fingerprint fails)?

Perspective — Pen-tester:

  • Invalid mnemonic (word not in list, wrong checksum byte) → rejected с ясной ошибкой?
  • Partial mnemonic (23 or 25 words) → rejected?
  • mnemonic с valid words но invalid checksum (изменить последнее слово) → rejected?
  • Unicode / whitespace normalization — два visually-identical inputs producing same master_seed OR rejected?

Perspective — Production operator:

  • Error messages actionable (user знает как исправить)?
  • Timing для 2²⁰ iterations acceptable (должно быть 0.5-2 секунды)?
  • SK bytes не утекают в stdout (default behavior — redacted, M1_DUMP_SK=1 для opt-in, см. m1_crypto P2 external closure)?

Сценарий 1 — M1 Crypto shakedown

Binary: examples/m1_crypto.rs (backfill — пишем первым в Validation Gate)

Что проверяем:

  • SHA-256 FIPS 180-4 test vector: hash("abc") = ba7816bf... byte-exact
  • FN-DSA-512 keypair + sign message + verify → OK; mutate signature byte → verify fail
  • Merkle empty_internal(256) — 257 × 32B precomputed, derivation trace уровней 0-2
  • Domain separation для всех R2 class domains (mt-op, mt-nodereg, mt-proposal, mt-bundle, ...) — разные hashes

Команды (subcommands actual binary; подкоманда all прогоняет всё подряд):

cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_crypto -- hash
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_crypto -- sign "test"
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_crypto -- keypair 3
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_crypto -- merkle-empty
cd "/Users/kh./Python/Ничто/Монтана/Русский/Протокол/Код" && cargo run --release --example m1_crypto -- all

Expected output markers (байт-exact проверяется mt_crypto::hash против FIPS 180-4):

  • hash: секция «1. FIPS 180-4 §B.1 vector» строка byte-exact match true; секция «2. Empty-parts collapse» equal true; секция «3. Part concatenation» equal true; секция «4. Domain separation» — различные 32-байтовые хэши для ≥ 10 classes
  • sign: секция «SIGN + VERIFY» — signature 666 bytes, verify(mutated signature) false, 4 adversarial теста все pass
  • keypair N: N × (pubkey 897B, secret 1281B), uniqueness N distinct pubkeys
  • merkle-empty: TREE_DEPTH 256, derivation trace уровни 0..2, finale empty_internal(256) = <hex>

Architect gate: byte-exact match true в FIPS vector секции; unique hashes для каждого R2 domain; empty_internal(256) deterministic; sign verify mutated → false.

Critic checklist:

  • Запустить на Intel Mac + ARM Mac + Linux x86 — все hashes совпадают? (должны)
  • Re-run 2 раза sign "test"σ отличаются между запусками? (должно — FN-DSA-512 randomized sampler non-deterministic)
  • Оба σ верифицируются? (должны — stability через signed_scope, Правило R2)
  • hash(b"mt-op", &[b""]) vs hash(b"mt-op", &[]) — одинаковы? (должны — empty-parts collapse proof в output)
  • hash(b"mt-op", &[b"aa", b"bb"]) vs hash(b"mt-op", &[b"aabb"]) — одинаковы? (должны — concat proof в output)
  • Unique domain separation — все R2 classes производят разные hashes для того же input? (должны — см. секция 4)

Сценарий 2 — M2 TimeChain + State shakedown

Binary: examples/m2_timechain_state.rs

Что проверяем:

  • VDF forward: 1000 steps от genesis T_r_0, сверить T_r_1000 ручным пересчётом SHA-256^1000
  • next_d boundary cases: median_permille 850, 851, 949, 950, 1000 — верно увеличивает/оставляет/уменьшает
  • cemented_bundle_aggregate: window < 2 → zeros, empty set → SHA-256("mt-bc-aggregate-empty" || W), non-empty → sorted node_ids aggregate
  • State root composition: build AccountTable с 3 accounts + NodeTable 2 nodes + CandidatePool 1 → recompute state_root через SHA-256("mt-state-root" || ...) independently
  • Merkle inclusion proof: один account → extract proof → verify against root

Команды:

cd "..." && cargo run --release --example m2_timechain_state -- vdf-forward 1000
cd "..." && cargo run --release --example m2_timechain_state -- next-d-boundaries
cd "..." && cargo run --release --example m2_timechain_state -- cba-branches
cd "..." && cargo run --release --example m2_timechain_state -- state-root-compose
cd "..." && cargo run --release --example m2_timechain_state -- merkle-inclusion

Architect gate: все outputs byte-exact совпадают с hand-computed reference.

Critic checklist:

  • VDF backward — impossible? (должен — preimage resistance SHA-256)
  • next_d at median=850 exact — decreases or unchanged? (per spec: ≤ low → decrease)
  • cemented_bundle_aggregate с одинаковыми node_ids, разным порядком — same output? (должен — sorted по node_id)
  • state_root с reordering accounts in BTreeMap — stable? (должен — BTreeMap sorted by key)
  • Merkle proof на несуществующий key — absence proof returns leaf_value=empty, verify_proof returns true? (должен)

Сценарий 3 — M3 Account operations

Binary: examples/m3_account.rs

Что проверяем:

  • Scenario: создать bootstrap → accounts A, B (через TransferActivation от sponsor) → Transfer A→B → apply_proposal → state
  • Observable: balance A decreased, B increased; Σ delta_balance = 0; frontier_hash updated; account_chain_length inc
  • Emission: manually apply reward_moneta(params) = EMISSION_moneta (const 13 Ɉ) для нескольких окон; supply растёт линейно
  • Genesis state materialization: build_genesis_state → check bootstrap account + node with chain_length=1
  • DS-2 edge: attempt apply_proposal с lottery_weight = 0 (chain_length_snapshot=0) → winner_node panics? returns err?

Команды:

cd "..." && cargo run --release --example m3_account -- transfer-scenario
cd "..." && cargo run --release --example m3_account -- emission-schedule 0 10000 20000 60000
cd "..." && cargo run --release --example m3_account -- genesis-state
cd "..." && cargo run --release --example m3_account -- ds2-edge

Architect gate: Σ balance invariant после transfer; emission matches closed-form; genesis_state_root deterministic.

Critic checklist:

  • Transfer A→B с amount > balance A — отклоняется? (InsufficientBalance)
  • Transfer A→A — отклоняется? (SelfTransfer)
  • Двойной OpenAccount с тем же pubkey → DuplicateAccount?
  • Invalid prev_hash (не совпадает с frontier) → InvalidPrevHash?
  • Signature от другого keypair → InvalidSignature?
  • apply_proposal с одинаковым winner дважды (replay) → emission двойная? (нет — каждое W разный)

Сценарий 4 — M4 Full consensus cycle (ключевой)

Binary: examples/m4_local_net.rs --nodes 2 --windows 10

Что проверяем: in-memory 2+ synthetic узла проходят 10 окон с полным protocol cycle:

  • Каждое окно: узлы синтетически подписывают BCs окна W-1 (из testdata) → proposer_{W} = winner_{W-2} → собирает included_bundles + included_reveals → argmin winner → apply_proposal → cementing via quorum
  • Printout per window: winner_id, winner_class (Node/Account), reward_moneta, state_root, active_chain_length, quorum
  • После 10 окон: supply ledger (Σ rewards) == EMISSION_moneta × 10 — consistency

Команды:

cd "..." && cargo run --release --example m4_local_net -- --nodes 2 --windows 10
cd "..." && cargo run --release --example m4_local_net -- --nodes 2 --windows 10 --seed 42
cd "..." && cargo run --release --example m4_local_net -- --nodes 5 --windows 100 --verbose

Architect gate:

  • Σ reward == EMISSION_moneta × N_windows (supply invariant)
  • Все apply_proposal succeeded (no protocol violations)
  • state_root по окнам — деterministic для given --seed
  • Два запуска с --seed 42 → byte-exact same trace

Critic checklist:

  • Если один узел не подписывает BC → остаётся в active set? (да, пока < 2τ₂)
  • Если winner = offline узел → fallback на second_min? (proposer может timeout)
  • Fallback cascade: reject proposal → second_min → rejected → third_min? (работает до available candidates)
  • 67% quorum точно — что если ровно 67%? ((67X+99)/100 дает ceiling — включительно проходит)
  • Dependency rule: две операции от одного sender в одном окне — вторая отклоняется?
  • argmin tie-breaking: два кандидата с одинаковым weighted_ticket — как определяется winner? (сравнение by node_id lex asc после ticket equality)

Сценарий 5 — M5 Persistence + restart

Binary: examples/m5_persist.rs

Что проверяем:

  • Run M4 scenario → close → reopen → state_root byte-exact совпадает
  • Crash посреди apply_proposal (SIGKILL или std::process::exit) → reopen → state consistent (либо applied, либо not)
  • Prune 50 oldest proposals → current state untouched, recent proposals accessible
  • Concurrent read while writing (RocksDB snapshot): reader видит consistent view

Команды:

cd "..." && cargo run --release --example m5_persist -- restart-cycle 20
cd "..." && cargo run --release --example m5_persist -- crash-midcommit
cd "..." && cargo run --release --example m5_persist -- prune 50

Architect gate: state_root after restart == state_root before close; crash recovery replays partial commit cleanly; pruned proposals NotFound, current state intact.

Critic checklist:

  • Corrupt RocksDB файл (e.g., truncate) → open returns error, не panic?
  • Два процесса одновременно open одну базу → второй fails (RocksDB lock)?
  • Disk full во время commit → WriteBatch atomic — либо all, либо nothing?
  • Restart через 1 год simulated time — replay всех 13 млн окон работает за разумное время?

Проведение Validation Gate — incremental workflow

На каждую сессию — один сценарий:

  1. Автор открывает новую сессию репликой «начинаем Gate Scenario N» (или «пиши» если уже понятно).
  2. Architect пишет crates/mt-examples/examples/mN_name.rs (новый binary для текущего сценария).
  3. Architect даёт команды одной строкой + expected output pattern.
  4. Автор запускает на своей машине, копирует output в чат.
  5. Architect читает output построчно, сверяет с expected.
  6. Критик задаёт adversarial checklist questions (5-10 вопросов из scenario-блока).
  7. Автор: «всё работает как ожидал» либо флагует подозрительное.
  8. Всё OK → architect обновляет status tracker выше: строка сценария → ✅ passed {дата}, commit в ROADMAP.
  9. Finding → fix (в binary или в core crate) → повторный прогон → retry.

Next session trigger: автор открывает новую сессию после passed предыдущего сценария.

Критерий закрытия Validation Gate: все 5 строк tracker-а = ✅ passed. Только после этого — M6.

Starter — с чего начать новую сессию

Первая Gate сессия (Scenario 1):

  1. Автор: «начинаем Scenario 1 M1 Crypto» (или просто «пиши» после тега ROADMAP контекста)
  2. Architect: создаёт crates/mt-examples/ crate (Cargo.toml + пустой src/lib.rs или dummy), добавляет workspace members entry
  3. Architect: пишет crates/mt-examples/examples/m1_crypto.rs с 4 subcommands (fips-abc, falcon-roundtrip, merkle-empty, domain-separation)
  4. Architect: даёт команды автору
  5. Manual run + разбор + критик → passed → next session (Scenario 2)

Manual Validation Matrix M1-M5 (production-grade per-element)

Дополняет «Локальный shakedown» сценарии 0-5 (выше). Сценарии — happy-path scenario-by-scenario; матрица — каждый винтик на уровнях кода / протокола / пользователя / UX. Цель — production-grade проверка всех элементов перед external audit + перед запуском M6 сетевого слоя.

Формат каждого пункта:

[ ] Имя | file:line | spec ref | reproduce | expected | pass criterion

Принципы:

  • M6+ network исключён (не реализован)
  • Сценарии 0-5 дают smoke-pass; матрица детализирует — каждое test vector / invariant / API surface
  • Pass criterion бинарный: byte-exact match / numerical equality / observable behavior — без «вроде работает»
  • Зависимости: пункт может зависеть от предыдущих (например проверка transfer требует init готовый)

Уровень 1 — Code primitives (M1 Foundational)

M1.1 mt-codec — canonical encoding + domain registry

# Элемент file:line Reproduce Expected Pass
1.1.1 write_u8/u16/u32/u64/u128 LE crates/mt-codec/src/lib.rs cargo test -p mt-codec LE byte order, no platform dependence byte-exact match unit-tests
1.1.2 write_bytes(buf, &[u8]) mt-codec/src/lib.rs round-trip property test encode → decode = identity property test passes ≥1000 cases
1.1.3 CanonicalEncode trait impls для всех consensus types mt-codec/src/lib.rs + per-crate impl grep "impl CanonicalEncode" workspace-wide каждый consensus type implements NodeRegistration, ProposalHeader, BundledConfirmation, VdfReveal, AccountRecord, NodeRecord, CandidateRecord — все present
1.1.4 Domain registry 32 константы prefix-free mt-codec::domain grep "pub const " crates/mt-codec/src/lib.rs | grep -i domain список 32 domains, ни один не префикс другого prefix-free check: convention mt-{component}[-{subcomponent}] соблюдена для всех
1.1.5 hash(domain, parts) self-delimiting mt-crypto::hash manual reproduce: python3 -c "import hashlib; print(hashlib.sha256(b'mt-proposal' + bytes([0]) + payload).hexdigest())" vs Rust output byte-exact SHA-256 match byte-exact match для test inputs
1.1.6 sha256_raw(bytes) без domain mt-crypto:sha256_raw FIPS 180-4 §B.1 vector "abc" → ba7816bf… byte-exact "abc" → standard digest unit-test passes

M1.2 mt-crypto — PQ crypto + secret types

# Элемент file:line Reproduce Expected Pass
1.2.1 keypair_from_seed(seed) ML-DSA-65 deterministic mt-crypto/src/lib.rs cargo test -p mt-crypto-native --test nist_acvp_kat NIST FIPS 204 KAT byte-exact 25/25 KeyGen vectors PASS
1.2.2 sign(sk, msg) deterministic ML-DSA-65 (FIPS 204 Algorithm 2) mt-crypto::sign NIST ACVP SigGen 15 cases tgId=3 byte-exact deterministic signature 15/15 SigGen vectors PASS
1.2.3 verify(pk, msg, sig) constant-time mt-crypto::verify sign+verify roundtrip + tamper test accept good, reject mutated unit-test PASS, no panic
1.2.4 keypair_from_seed_mlkem ML-KEM-768 mt-crypto-native NIST KAT cargo test -p mt-crypto-native --test nist_acvp_kat FIPS 203 KAT byte-exact 25/25 KeyGen vectors PASS
1.2.5 SecretKey heap+mlock mt-crypto/src/lib.rs:130 cargo test -p mt-crypto --test security_invariants secret_key_is_heap_allocated size_of::() = ptr (8B), не 4032 invariant test PASS
1.2.6 SecretKey: !Clone mt-crypto::SecretKey trait check test compile-error на try_clone() compile-time enforce
1.2.7 SecretKey: !PartialEq (timing-leak protection) mt-crypto::SecretKey security_invariants test no == operator compile-time check PASS
1.2.8 Drop+zeroize для SecretKey/MlkemSecretKey mt-crypto/src/lib.rs Drop impl drop scope test bytes zeroed после drop unit-test verify post-drop memory
1.2.9 mlock/munlock 7 unsafe blocks с SAFETY mt-crypto/src/lib.rs:164,192,235,276,293,364,383 grep // SAFETY: каждый unsafe имеет comment 7/7 SAFETY comments present
1.2.10 No println on secret bytes grep rg "println" crates/mt-crypto/src нет matches на SK fields grep clean

M1.3 mt-crypto-native — FFI к OpenSSL EVP

# Элемент file:line Reproduce Expected Pass
1.3.1 mt_keypair_from_seed_mldsa C wrapper csrc/mt_crypto.c NIST KAT differential OpenSSL EVP_PKEY_KEYGEN с OSSL_PKEY_PARAM_ML_DSA_SEED byte-exact NIST FIPS 204
1.3.2 mt_sign_mldsa deterministic csrc/mt_crypto.c ACVP SigGen с OSSL_SIGNATURE_PARAM_DETERMINISTIC=1 FIPS 204 Algorithm 2 deterministic 15/15 PASS
1.3.3 mt_sign_mldsa_ctx (FIPS context support) csrc/mt_crypto.c empty ctx == ctx-equivalence один test case 1/1 PASS
1.3.4 OpenSSL 3.5.5 LTS pinned Cargo.toml cargo tree | grep openssl-src =300.5.5+3.5.5 exact pin exact version match
1.3.5 Reproducible builds через build.rs crates/mt-crypto-native/build.rs docker build -f docker/release-build.dockerfile two passes byte-identical binaries sha256 binary 1 == sha256 binary 2

M1.4 mt-mnemonic — 24-word recovery flow

# Элемент file:line Reproduce Expected Pass
1.4.1 entropy_to_mnemonic([u8; 32]) mt-mnemonic/src/mnemonic.rs KAT vectors test byte-exact 24 words from BIP-39 wordlist 5/5 KAT PASS
1.4.2 mnemonic_to_master_seed("...") PBKDF2 iter=2²⁰ mt-mnemonic/src/pbkdf2.rs RFC 7914 §11 / RFC 6070 vectors byte-exact 64B master_seed RFC vectors PASS
1.4.3 mldsa_seed_for_role(seed, domain) HKDF mt-mnemonic/src/hkdf.rs RFC 5869 §A.1 byte-exact 32B per-role seed RFC vectors PASS
1.4.4 mlkem_seed_for_role(seed, domain) 64B output mt-mnemonic/src/hkdf.rs KAT test byte-exact 64B (d ‖ z FIPS 203 §6.1) unit-test PASS
1.4.5 HMAC-SHA-256 RFC 4231 mt-mnemonic/src/hmac.rs RFC 4231 cases 1-7 byte-exact MAC 7/7 PASS
1.4.6 Wordlist binding SHA-256 mt-mnemonic/src/wordlist.rs hash binding test sha256(wordlist file) match expected hash match
1.4.7 Whitespace-tolerant parsing (F-12 closure) mt-mnemonic/src/mnemonic.rs:120 init --mnemonic " word1 word2 ..." accepts irregular whitespace regression test PASS
1.4.8 End-to-end recovery determinism mt-mnemonic/tests/e2e_recovery.rs cargo test -p mt-mnemonic --test e2e_recovery mnemonic → identity → mnemonic = identity 1/1 PASS

Уровень 2 — State foundation (M2)

M2.1 mt-merkle — Sparse Merkle Tree depth 256

# Элемент file:line Reproduce Expected Pass
2.1.1 leaf_hash(serialized) domain-separated mt-merkle/src/lib.rs:13 python3 -c "import hashlib; print(hashlib.sha256(b'mt-merkle-leaf' + bytes([0]) + bytes).hexdigest())" byte-exact SHA-256 external oracle match
2.1.2 internal_hash(left, right) order-sensitive mt-merkle/src/lib.rs swap left↔right test hash(L,R) ≠ hash(R,L) order-sensitivity PASS
2.1.3 empty_internal(k) precomputed cache levels 0-256 mt-merkle/src/lib.rs OnceLock manual shasum levels 0,1,2,3 recursive H(empty_internal(k-1), empty_internal(k-1)) 4/4 manual computations match
2.1.4 SparseMerkleTree::insert(key, value) mt-merkle/src/lib.rs insertion-order independence test вставка в разном порядке → одинаковый root order-independence PASS
2.1.5 SparseMerkleTree::root() BTreeMap canonical mt-merkle::SparseMerkleTree determinism_invariants tests byte-exact root для разного insertion order 10/10 invariants PASS
2.1.6 verify_proof(root, proof) inclusion mt-merkle::verify_proof construct proof, verify true для valid, false для tampered 2/2 cases PASS
2.1.7 verify_proof absence (empty leaf) mt-merkle::verify_proof proof for non-existent key true (absence verified) absence proof PASS
2.1.8 TREE_DEPTH = 256 mt-merkle::TREE_DEPTH const check == 256 static assertion
2.1.9 InclusionProof MAX_LEAF_VALUE_SIZE = 4096 (P10-1) mt-merkle/src/lib.rs:285 encode test с oversized leaf debug_assert panic bound enforced
2.1.10 InclusionProof MAX_SIBLINGS = TREE_DEPTH mt-merkle::MAX_SIBLINGS const == 256 static

M2.2 mt-genesis — ProtocolParams SSOT

# Элемент file:line Reproduce Expected Pass
2.2.1 genesis_params() singleton OnceLock mt-genesis/src/lib.rs повторные вызовы → same Arc ptr стабильный pointer unit-test PASS
2.2.2 ProtocolParams encoded size 4094B mt-genesis::PARAMS_ENCODED_SIZE assert_eq!(p.encode().len(), 4094) byte-exact 4094B static
2.2.3 emission_moneta = 13 × 10⁹ nɈ mt-genesis::ProtocolParams const check == 13_000_000_000 LE bytes [0x00, 0xc8, 0x4d, 0x21, 3, 0, 0, 0]
2.2.4 d0 = 325_000_000 mt-genesis::ProtocolParams runtime calibration on M-class Mac окно ≈ 60s wall-clock empirical 60-65s
2.2.5 tau2_windows = 20160 mt-genesis::ProtocolParams const == 20160 (~14 days) static
2.2.6 selection_interval = 336 mt-genesis::ProtocolParams const == 336 (1/60 of τ₂) static
2.2.7 bootstrap_node_pubkey placeholder mt-genesis::ProtocolParams byte check == [0u8; 1952] (pre-ceremony) static; post-ceremony updated
2.2.8 genesis_app_id() SHA-256 domain-separated mt-genesis/src/lib.rs manual recompute byte-exact hash unit-test PASS
2.2.9 compute_genesis_state_hash() determinism mt-genesis повторные вызовы byte-exact same hash determinism_invariants 7/7
2.2.10 is_genesis_bootstrap_finalized(params) mt-genesis predicate test placeholder zeros → false static check

M2.3 mt-state — AccountTable / NodeTable / CandidatePool

# Элемент file:line Reproduce Expected Pass
2.3.1 AccountRecord 2059B byte-exact mt-state/src/lib.rs:25 assert_eq!(record.encode().len(), ACCOUNT_RECORD_SIZE) == 2059 static
2.3.2 NodeRecord 2098B byte-exact mt-state/src/lib.rs:59 encode size == 2098 static
2.3.3 CandidateRecord 2082B byte-exact mt-state/src/lib.rs encode size == 2082 static
2.3.4 derive_account_id(suite_id, pk) SHA-256 domain mt-state::derive_account_id manual recompute via Python byte-exact external oracle PASS
2.3.5 derive_node_id(pk) distinct from account_id mt-state::derive_node_id derive both for same pk account_id ≠ node_id cross-distinctness PASS
2.3.6 AccountTable::insert/get BTreeMap mt-state::AccountTable order-independence test root hash same regardless insertion order invariant PASS
2.3.7 NodeTable::insert/contains mt-state::NodeTable duplicate check second insert returns false / errors unit-test PASS
2.3.8 CandidatePool::insert/remove/iter mt-state::CandidatePool TTL-based pruning apply_candidate_expiry удаляет expired scenario test
2.3.9 compute_state_root(node, candidate, account) mt-state::compute_state_root manual SHA-256 composition byte-exact tree-of-three external oracle PASS
2.3.10 is_active(node, current_window) predicate mt-state::is_active boundary at 2×τ₂ inclusive test true within window, false outside boundary tests PASS

M2.4 mt-timechain — VDF + adaptive D + cba

# Элемент file:line Reproduce Expected Pass
2.4.1 vdf_step(prev, d) SHA-256^d associative mt-timechain/src/lib.rs:13 composition test: vdf_step(vdf_step(x, a), b) == vdf_step(x, a+b) associativity holds invariant PASS
2.4.2 vdf_step(prev, 0) == prev identity mt-timechain zero-iteration check output == input unit-test PASS
2.4.3 vdf_verify(prev, expected, d) mt-timechain::vdf_verify sign correct + tamper accept correct, reject wrong claim 2/2 cases PASS
2.4.4 next_d(current, median_permille, params) adaptive mt-timechain/src/lib.rs:40-89 dead-zone (median == 1000): no change; high (>1030): +3%; low (<970): -3% integer permille arithmetic 3/3 boundary tests PASS
2.4.5 next_d overflow protection mt-timechain/src/lib.rs:51,65 extreme D close to u64::MAX panic с descriptive message overflow caught (~25_000 epochs)
2.4.6 cemented_bundle_aggregate(W, &[]) empty marker mt-timechain::cba window=0,1 → genesis zeros byte-exact [0u8; 32] static
2.4.7 cemented_bundle_aggregate(W, [node_ids]) canonical sort mt-timechain input order independence test byte-exact для разного порядка [I-8] invariant PASS
2.4.8 cemented_bundle_aggregate distinct empty vs non-empty mt-timechain empty [] vs [node_id] разные hashes distinctness PASS
2.4.9 TimeChain VDF на genesis узле тикает per окно montana-node start montana-node start --max-windows 2 && status T_r изменился, current_window инкрементировано на 2 observable PASS

Уровень 3 — apply_proposal (M3 mt-account)

# Элемент file:line Reproduce Expected Pass
3.1 Transfer 0x02 layout (TRANSFER_SIZE) mt-account/src/lib.rs encode size check byte-exact spec static const
3.2 validate_transfer signature + balance + chain_length mt-account::validate_transfer tests/transfer_*.rs reject bad sig / insufficient balance / non-monotone chain reject all 3 cases
3.3 apply_transfer atomic: balance debited, credited, frontier_hash bound mt-account::apply_transfer unit-test sender.balance -= amount, receiver.balance += amount, frontier_hash = op_hash invariant PASS
3.4 ChangeKey 0x03 layout mt-account::CHANGE_KEY_SIZE encode size byte-exact spec static const
3.5 validate_change_key signature под старым ключом mt-account::validate_change_key tamper test new sig under old key required unit-test PASS
3.6 apply_change_key rotates current_pubkey mt-account::apply_change_key before/after account.current_pubkey == new_pk, suite_id updated unit-test PASS
3.7 Anchor 0x04 layout mt-account::ANCHOR_SIZE encode size byte-exact spec static const
3.8 validate_anchor app_id + data_hash binding mt-account::validate_anchor tests data_hash 32B fixed, app_id present unit-test PASS
3.9 apply_anchor AccountChain entry, не меняет balance mt-account::apply_anchor balance check balance unchanged, chain_length += 1 unit-test PASS
3.10 TransferActivation 0x0A sponsor pattern mt-account::TRANSFER_ACTIVATION_SIZE encode size byte-exact spec static const
3.11 apply_transfer_activation создаёт новый AccountRecord mt-account::apply_transfer_activation before/after AccountTable new account inserted, balance=amount, is_node_operator=false unit-test PASS
3.12 op_hash(operation) SHA-256("mt-op" || signed_scope) mt-account/src/lib.rs manual recompute byte-exact external oracle PASS
3.13 settle_window(cemented_ops, W) сортировка по op_hash mt-account::settle_window unsorted input output sorted ascending op_hash invariant PASS
3.14 apply_proposal Step 2 (emission), 3.5 (chain_length++), 3.6 (checkpoint), 4 (state_root) mt-account::apply_proposal per-window in start.rs balance += emission_moneta, node.chain_length += 1, state_root updated observable balance growth
3.15 apply_emission(winner_account, params) const 13 Ɉ mt-account::apply_emission один tick account.balance += 13_000_000_000 nɈ observable PASS
3.16 supply_moneta(W, params) = emission × (W+1) closed-form mt-account::supply_moneta различные W linear formula unit-test PASS
3.17 build_genesis_state(params) bootstrap mt-account::build_genesis_state call + encode byte-exact deterministic state invariant PASS
3.18 genesis_state_root(params) byte-exact mt-account::genesis_state_root manual recompute == compute_state_root результат external oracle PASS
3.19 Conservation: Σ delta_balance == 0 per Transfer/Activation mt-account::apply_* property test sum unchanged invariant PASS
3.20 checked_sub on balance underflow → panic с protocol invariant breach mt-account::apply_transfer underflow case panic с descriptive message controlled halt

Уровень 4 — Consensus mechanics (M4)

M4.1 mt-lottery — BundledConfirmation + VdfReveal + lottery

# Элемент file:line Reproduce Expected Pass
4.1.1 BundledConfirmation::encode_signed_scope byte-exact mt-lottery/src/lib.rs:34 size check fixed overhead 76B + var hashes static
4.1.2 bundle_hash(bc) SHA-256("mt-bundle" || scope) mt-lottery/src/lib.rs:69 external oracle byte-exact match external PASS
4.1.3 validate_bundle rules a-d mt-lottery/src/lib.rs:102 per-rule rejection tests UnknownNode/UnsupportedSuite/WrongEndpoint/OpsOutOfOrder/RevealsOutOfOrder/InvalidSignature 6/6 reject paths PASS
4.1.4 validate_bundle u16 cap (M4-1 closure) mt-lottery/src/lib.rs:120 op_hashes.len() > u16::MAX → TooManyOps reject before signature verify functional test PASS
4.1.5 op_hashes ascending strictly mt-lottery::is_strictly_ascending unsorted input → reject OpsOutOfOrder error unit-test PASS
4.1.6 VdfReveal layout 32+8+32+SIG bytes mt-lottery::REVEAL_SIZE size check == REVEAL_SIZE const static
4.1.7 reveal_hash(reveal) SHA-256("mt-vdf-reveal" || scope) mt-lottery::reveal_hash external oracle byte-exact external PASS
4.1.8 validate_reveal (5 rules) mt-lottery::validate_reveal per-rule reject similar to bundle unit-test PASS
4.1.9 compute_endpoint(T_r, cba(W-2), my_node, W) lottery formula с [I-8] binding mt-lottery::compute_endpoint manual SHA-256 composition byte-exact, [I-8] satisfied external oracle PASS
4.1.10 log2_q64(x_q64) Q64.64 polynomial mt-lottery/src/lib.rs (B0..B3 coefficients) reference Python implementation max error 2^-10.62 precision match
4.1.11 ln_q64(x_q64) derived from log2_q64 mt-lottery::ln_q64 reference test byte-exact match unit-test PASS
4.1.12 weighted_ticket_node(endpoint, w, snapshot) u128 integer division mt-lottery::weighted_ticket_node various w deterministic ticket determinism_invariants 34/34
4.1.13 lottery_weight(chain_length, snapshot) integer mt-lottery::lottery_weight boundary chain_length monotone non-decreasing unit-test PASS
4.1.14 seniority_bonus integer formula mt-lottery::seniority_bonus various inputs spec-exact unit-test PASS
4.1.15 determine_winner argmin (ticket asc, class asc, id lex asc) mt-lottery::determine_winner tie cases canonical ordering rule tie-breaker tests PASS
4.1.16 quorum(active_chain_length) 67% ceiling (67×X+99)/100 mt-lottery::quorum various X integer ceiling formula spec-binding PASS
4.1.17 is_cemented(c, active) predicate mt-lottery::is_cemented boundary cases true if c >= quorum(active) boundary tests PASS

M4.2 mt-consensus — ProposalHeader R1/R2

# Элемент file:line Reproduce Expected Pass
4.2.1 ProposalHeader 3722B fixed (17 полей) mt-consensus/src/lib.rs:33 encode size == PROPOSAL_HEADER_SIZE = 3722 static
4.2.2 encode_signed_scope 413B (без подписи) mt-consensus/src/lib.rs encode_signed_scope().len() == 413 static computation
4.2.3 proposal_hash(header) SHA-256("mt-proposal" || scope) mt-consensus::proposal_hash external oracle byte-exact external PASS
4.2.4 validate_header rules a-f mt-consensus::validate_header per-rule tests fallback_depth ≥ 1, window_index = prev+1, protocol_version monotone, proposer registered, suite Mldsa65, sig verify 6/6 reject paths PASS
4.2.5 target поле u128 LE 16B (P5 closure) mt-consensus::ProposalHeader encode field u128 16 bytes LE static
4.2.6 canonical_proposer(W) Lookback Leadership mt-consensus::canonical_proposer proposer_W = winner_{W-2} spec-exact unit-test PASS
4.2.7 fallback_proposer(depth) cascade mt-consensus::fallback_proposer depth 1, 2, 3 byzantine-safe ordering unit-test PASS
4.2.8 compute_control_set(headers) filter + sort mt-consensus::compute_control_set unordered input sorted by (window asc, op_hash lex asc) invariant PASS
4.2.9 validate_proposer_is_canonical mt-consensus mismatched proposer reject unit-test PASS
4.2.10 finalization_status(header) Cemented/Rejected mt-consensus::finalization_status various confirmer counts per is_cemented unit-test PASS

M4.3 mt-entry — NodeRegistration + Selection

# Элемент file:line Reproduce Expected Pass
4.3.1 NodeRegistration 5344B fixed mt-entry/src/lib.rs encode size == NODE_REGISTRATION_SIZE static
4.3.2 nodereg_hash SHA-256("mt-nodereg" || scope) mt-entry::nodereg_hash external oracle byte-exact external PASS
4.3.3 validate_noderegistration 3 rules mt-entry::validate_noderegistration per-rule reject suite supported, sig verify, node_id unique, operator account exists+!is_node_operator 4/4 reject paths PASS
4.3.4 candidate_vdf_init([T_r, cba(W-2), node_id]) [I-8] mt-entry::candidate_vdf_init external oracle byte-exact composition external PASS
4.3.5 compute_expiry_window(W) 3τ₂ mt-entry::compute_expiry_window various W == W + 3 × 20160 static formula
4.3.6 apply_candidate_expiry(pool, W) removes expired mt-entry::apply_candidate_expiry per-window call pool.len() decreases observable PASS
4.3.7 selection_slots 1% admission cap (ADMISSION_DIVISOR=130) mt-entry::selection_slots active_chain_length 13000 → 100 slots per-formula spec-binding PASS
4.3.8 selection_sort_key SHA-256("mt-selection" || ...) mt-entry::selection_sort_key external oracle byte-exact external PASS
4.3.9 rank_candidates_for_selection canonical sort mt-entry::rank_candidates_for_selection unsorted input sorted by sort_key invariant PASS
4.3.10 apply_selection_event(pool, nodes, accounts, W, params) mt-entry::apply_selection_event candidates ready inserted into NodeTable, removed from pool, operator account flagged observable PASS
4.3.11 apply_noderegistrations_batch incremental sort+apply mt-entry::apply_noderegistrations_batch per-window applied count == 1 if vdf_chain_length ≥ τ₂ observable PASS
4.3.12 required_vdf_length(pending, active, τ₂) integer permille mt-entry::required_vdf_length pending=0,active=0 → tau2_windows spec-binding unit-test PASS

Уровень 5 — Persistence (M5 mt-store)

# Элемент file:line Reproduce Expected Pass
5.1 FsStore::open(data_dir) создаёт proposals/ mt-store/src/lib.rs удалить proposals/, открыть store proposals/ создан observable PASS
5.2 cleanup_orphan_tmp (M5-LOW-8 closure) mt-store/src/lib.rs:67 placement orphan .tmp file удалён при open regression test PASS
5.3 save_account_table + load_account_table round-trip mt-store/src/lib.rs:271-281 save → load byte-exact decode round-trip PASS
5.4 save_node_table + load_node_table round-trip mt-store/src/lib.rs:301-311 save → load byte-exact decode round-trip PASS
5.5 save_candidate_pool + load_candidate_pool round-trip mt-store/src/lib.rs:331-341 save → load byte-exact decode round-trip PASS
5.6 archive_proposal(header) создаёт proposals/{W:020}.bin mt-store::archive_proposal per-window файл 3722B на диске ls data/proposals/
5.7 get_proposal_by_window(W) byte-exact decode mt-store::get_proposal_by_window archived header recovered identical round-trip PASS
5.8 save_meta_last_cemented(W) u64 LE mt-store::save_meta_last_cemented per-window meta_last_cemented.bin = 8B u64 LE hexdump check
5.9 verify_consistency() crash recovery mt-store::verify_consistency удалить proposals/ файл, оставить meta_last_cemented StoreError::NotFound error path PASS
5.10 prune_proposals_before(window) mt-store::prune_proposals_before call с threshold удаляет файлы window < threshold filesystem state
5.11 Atomic rename pattern (tempfile → rename) mt-store/src/lib.rs:87-114 crash mid-write simulation partial files либо clean state atomicity PASS
5.12 decode_X size validation перед read mt-store/src/lib.rs corrupted file (wrong size) StoreError::CorruptedLength error path PASS

Уровень 6 — Integration layer (montana-node CLI)

N1. Identity management commands

# Команда Reproduce Expected Pass
6.1 montana-node init (random) ./montana-node init 24 words displayed in 4×6 grid + identity.bin (15627B) 24-word grid + file size byte-exact
6.2 montana-node init --mnemonic "..." recovery from words byte-exact same identity as original identity match
6.3 montana-node init --entropy <hex32> deterministic from hex byte-exact same on re-run determinism PASS
6.4 montana-node init --force overwrite second init без --force отклонён, с --force OK IdentityFileExists error без --force error path PASS
6.5 montana-node inspect (default) без --reveal-master-seed fingerprint only, no master_seed observable PASS
6.6 montana-node inspect --reveal-master-seed флаг present master_seed shown (64 hex) observable PASS
6.7 Recovery determinism cross-machine machine 1 init → mnemonic; machine 2 init --mnemonic identical account_id, node_id byte-exact match

N2. Node lifecycle commands

# Команда Reproduce Expected Pass
6.8 montana-node start --max-windows 2 finite run 2 windows processed, balance += 26 Ɉ, exit 0 observable PASS
6.9 montana-node start --d-test-override 1000000 TEST-ONLY override warning printed, fast windows observable PASS
6.10 montana-node status AccountTable + NodeTable + CandidatePool dump Σ balance, supply, phase, current_window correct observable PASS
6.11 montana-node time window timing info current_window, ближайший selection (W % 336==0), эпоха τ₂ static check
6.12 montana-node help / --help / -h help text full usage + flag descriptions text match
6.13 Exit codes invalid args → exit 2; ok → exit 0; runtime err → exit 1 per-spec exit code check
6.14 LocalNodeError variants Display trigger каждую ошибку typed error message в stderr manual cases

N3. Phase transitions auto-detection

# Сценарий file:line Reproduce Expected Pass
6.15 Genesis bootstrap (placeholder zeros) node_lifecycle.rs:70 is_bootstrap_node cargo run -p montana-node start --max-windows 1 (свежий узел) phase = Active immediately, NodeTable содержит self observable PASS
6.16 Candidate path (post-ceremony, mismatch pubkey) node_lifecycle.rs:78 finalized bootstrap_node_pubkey != identity.node_pk phase = Bootstrap → CandidateVdf на первом окне DEV-010 logic PASS (не тестируется до ceremony)
6.17 DEV-010 status docs/SPEC_DEVIATIONS.md:172 grep Status acknowledged auto-detection static

N4. State migration

# Сценарий file:line Reproduce Expected Pass
6.18 current_window.bin v0 → v1 auto-upgrade clock.rs:31-58 вручную создать 8B legacy file → start узел → check size next save → 16B v1 формат round-trip PASS
6.19 identity.bin magic+version validation identity.rs:226-231 tamper magic byte → load_identity InvalidMagic error error path PASS
6.20 timechain.bin magic+version timechain_state.rs:47-52 tamper magic InvalidMagic error path PASS
6.21 node_state.bin magic+version node_lifecycle.rs:82-87 tamper magic InvalidMagic error path PASS

Уровень 7 — Operator surface (UX/UI)

O1. launchd integration (macOS)

# Элемент Reproduce Expected Pass
7.1 plist Label = "org.montana.node" cat ~/Library/LaunchAgents/org.montana.node.plist | grep Label production reverse-domain (no dev.*/local.*) static check
7.2 RunAtLoad=true plist автозапуск при логине login test
7.3 KeepAlive Crashed=true plist restart on crash kill -9 <PID>, узел restart-ит через ThrottleInterval=10
7.4 StandardOutPath/StandardErrorPath plist logs в data/logs/ tail -F data/logs/montana.log
7.5 SIGTERM graceful shutdown start.rs:31-33 + 545-546 launchctl unload -w plist узел сохраняет state, exit clean
7.6 ThrottleInterval=10 rate limit plist crash несколько раз минимум 10s между restart

O2. .command файлы

# Файл Reproduce Expected Pass
7.7 1. Запуск и логи узла.command дабл-клик в Finder Terminal открывается, status + tail -F observable open Terminal
7.8 2. Остановить узел.command дабл-клик launchctl unload, "ГОТОВО" message узел остановлен
7.9 Relative $DIR paths (не hardcoded) grep "$HOME/Applications" в .command пусто path-portable PASS

O3. Install scripts

# Скрипт Reproduce Expected Pass
7.10 scripts/install-local-mac.sh bash scripts/install-local-mac.sh (свежая система) build + install + identity init + launchd setup full e2e install
7.11 scripts/install-mac.sh curl ... | bash git clone + build + install full e2e install
7.12 scripts/install-vps.sh (Linux) bash scripts/install-vps.sh Ubuntu/Debian systemd setup + start systemctl status active
7.13 INSTALL_DIR env var override INSTALL_DIR=/custom/path bash scripts/install-local-mac.sh install в custom location observable PASS
7.14 dist/macOS/install.command дабл-клик full install + open Finder GUI flow PASS

O4. State files inventory

# Файл Path Expected size/format Pass
7.15 identity.bin data/identity.bin 15627B fixed, magic="mt-local", version=1 size + magic byte-exact
7.16 accounts.bin data/accounts.bin N × 2059B (растёт с TransferActivation) size кратен 2059
7.17 nodes.bin data/nodes.bin N × 2098B size кратен 2098
7.18 candidates.bin data/candidates.bin N × 2082B size кратен 2082
7.19 meta/current_window.bin data/meta/current_window.bin 16B v1 (либо 8B v0 legacy) size 16 либо 8
7.20 meta/timechain.bin data/meta/timechain.bin magic "mttc" + version + T_r + D + last_window hexdump magic
7.21 meta/node_state.bin data/meta/node_state.bin magic "mtns" + version + lifecycle fields hexdump magic
7.22 meta_last_cemented.bin data/meta_last_cemented.bin 8B u64 LE (либо absent если pre-genesis) hexdump
7.23 proposals/{window:020}.bin data/proposals/00000000000000000001.bin каждый файл 3722B size byte-exact
7.24 logs/montana.log + montana.err.log data/logs/ log files (launchd redirect) files present

O5. Crash recovery

# Сценарий Reproduce Expected Pass
7.25 Power loss mid-write (atomic rename) start узел, pkill -9 montana-node во время save_progress partial .tmp файл либо clean state, не corrupted bin atomicity PASS
7.26 SIGKILL → reload state kill -9 <PID> → restart continues с last cemented window (state preserved) observable continuity
7.27 Corrupted file detection tamper data/accounts.bin (truncate) StoreError::CorruptedLength на load error path PASS
7.28 meta_last_cemented vs proposals/ inconsistency удалить proposals/.bin, оставить meta_last_cemented StoreError::NotFound, не silent skip verify_consistency PASS
7.29 Re-open после crash valid state crash → restart узел продолжает с того окна, balance preserved observable PASS

Уровень 8 — Conformance & test vectors

C1. NIST KAT (cross-implementation conformance)

# Vector Source Reproduce Pass
8.1 ML-DSA-65 KeyGen 25 cases NIST ACVP-Server ML-DSA-keyGen-FIPS204 cargo test -p mt-crypto-native --test nist_acvp_kat 25/25 byte-exact
8.2 ML-DSA-65 SigGen 15 cases (1 empty + 14 ctx) NIST ACVP-Server ML-DSA-sigGen-FIPS204 tgId=3 same test 15/15 byte-exact
8.3 ML-KEM-768 KeyGen 25 cases NIST ACVP-Server ML-KEM-keyGen-FIPS203 same test 25/25 byte-exact
8.4 mt_sign_mldsa ≡ mt_sign_mldsa_ctx (empty ctx) own correctness check same test 1/1 PASS

C2. RFC test vectors

# Vector Source Pass
8.5 SHA-256 "abc" → ba7816bf...15ad FIPS 180-4 §B.1 unit-test PASS
8.6 HMAC-SHA-256 cases 1-7 RFC 4231 7/7 PASS
8.7 HKDF-Expand vectors RFC 5869 §A.1 PASS
8.8 PBKDF2 vectors RFC 7914 §11 / RFC 6070 PASS

C3. Determinism invariants (191 automated total)

# Crate Test count Reproduce Pass
8.9 mt-merkle 10 cargo test -p mt-merkle --test determinism_invariants 10/10
8.10 mt-genesis 7 cargo test -p mt-genesis --test determinism_invariants 7/7
8.11 mt-state (per file) cargo test -p mt-state --test determinism_invariants all PASS
8.12 mt-timechain 19 cargo test -p mt-timechain --test determinism_invariants 19/19
8.13 mt-account 29 cargo test -p mt-account 29/29
8.14 mt-lottery 34 cargo test -p mt-lottery 34/34
8.15 mt-consensus 27 cargo test -p mt-consensus 27/27
8.16 mt-entry 24 cargo test -p mt-entry 24/24
8.17 mt-store 17 cargo test -p mt-store 17/17

C4. Security invariants (13 automated)

# Invariant Reproduce Pass
8.18 SecretKey: !Clone cargo test -p mt-crypto --test security_invariants secret_key_is_not_clone compile-PASS
8.19 SecretKey: !PartialEq same test compile-PASS
8.20 SecretKey heap-allocated same test size_of == ptr
8.21 SecretKey impl Drop same test trait check
8.22 MlkemSecretKey same 4 invariants same test 4/4 PASS
8.23 FFI fills SK с non-zero bytes same test bytes != [0; N]
8.24 No println/log on SK in lib code same test grep clean
8.25 PublicKey/Signature: Clone (sanity) same test trait check

Уровень 9 — DEV-001..DEV-011 status verification

# DEV-N Spec ref Status verify Code location Pass
9.1 DEV-001..DEV-009 — закрыты через canonical apply_proposal pipeline rewrite docs/SPEC_DEVIATIONS.md:1-170 grep "Status: закрыто" docs/SPEC_DEVIATIONS.md | wc -l 9 closed entries static
9.2 DEV-010 — genesis bootstrap auto-detection docs/SPEC_DEVIATIONS.md:172 is_bootstrap_node logic в node_lifecycle.rs:70-79 runtime check via montana-node status observable
9.3 DEV-011 — hardware calibration initial D docs/SPEC_DEVIATIONS.md:N runtime D adjustment per τ₂ post-Genesis ceremony auto-adjust static acknowledgment
9.4 Pre-commit hook DEV count enforcement scripts/pre-commit.sh Gate 3 edit DEV in code without doc → reject enforced commit gate static

Прохождение matrix

Этапы:

  1. Фаза A — automated baseline (1-2 часа)

    • cargo test --all — все 191 determinism + 13 security + NIST KAT + RFC vectors
    • Pass criterion: 0 failures
    • Result: матрица пунктов 8.x = automated PASS
  2. Фаза B — operator surface manual (1 рабочий день)

    • Свежая установка через scripts/install-local-mac.sh
    • Прохождение пунктов 6.x (CLI commands), 7.x (operator surface)
    • Verify state files размеры/magic
    • Verify launchd integration (start/stop/restart on crash)
    • Verify install scripts на VPS (Linux) + macOS
  3. Фаза C — protocol per-element manual (3-5 рабочих дней)

    • Каждый пункт 1.x (M1 primitives), 2.x (M2 state), 3.x (apply_proposal), 4.x (consensus)
    • External oracle cross-checks (Python SHA-256 для hash compositions)
    • Boundary tests (overflow, underflow, edge cases)
    • Per-DEV deviation status confirmation
  4. Фаза D — recovery & crash (0.5 рабочего дня)

    • Сценарии 7.25-7.29 — atomic rename, SIGKILL recovery, corrupted file detection

Pass criterion для всей matrix: 100% пунктов с PASS либо явный finding с DEV-NNN reference в docs/SPEC_DEVIATIONS.md.

Output: заполненная matrix (markdown table со столбцом "Status: PASS/FAIL/N/A") в отдельном документе docs/manual-validation-results-YYYY-MM-DD.md. Это deliverable перед external audit / перед запуском M6 network layer.

Tooling:

  • Automated: cargo test --all для пунктов 8.x
  • External oracle: Python 3 + hashlib для SHA-256 cross-checks
  • Diff: diff -u expected actual для byte-exact verification
  • Hexdump: hexdump -C file | head для file format verification

Связь с существующим Manual Validation Gate

«Локальный shakedown» сценарии 0-5 (выше) дают smoke-pass — happy-path запуск каждого слоя. Matrix M1-M5 — детализация: каждый scenario разворачивается в 20-50 проверок per-element.

Сценарии 0-5 → entry-level demonstration. Matrix → production-grade audit-ready evidence.


M6 — Network ЗАКРЫТ (in-process e2e); cross-machine pairing defer M8

  • mt-net (~2700 LOC, no_std) — wire format envelope + 12 structured payloads + IBT online/mesh proofs + Bootstrap PoW + Uniform Framing + peer selection (4-level diversity + LRU) + Dandelion++ stem-fluff + NAT traversal + Mesh Transport + Store-and-Forward. 110 unit + integration tests PASS.
  • mt-net-transport (~470 LOC, std + libp2p) — libp2p TCP+TLS 1.3+Noise+Yamux upgrade chain + MontanaCodec для request-response + MontanaBehaviour + IBT classify_proof. 14 tests PASS включая 3 e2e two-node communication.

Phases закрытия:

  • Phase A wire envelope + payloads (commits 9de287b, bc694a5)
  • Phase B IBT + Bootstrap PoW (commit 26e76c9)
  • Phase D Uniform Framing (commit cce189f)
  • Phase E peer selection + diversity + LRU (commit f34726f)
  • Phase F Dandelion + NAT (commit 7d44687)
  • Phase G Mesh + Store-and-Forward (commit 60466c1)
  • Critic-fix bundle P-C1..P-C8 (commit 93b9cdc) — 8 code findings closed (domain SSOT, prefix-free rename, fuzz harnesses, try_new constructors, forward-compat, O(1) verify, unwrap/expect refactor)
  • Phase C.0 mt-net-transport skeleton + libp2p [C-5] capability checklist 8/8 PASS (commit ea6608e)
  • Phase C.1 MontanaCodec (commit 11f3b80)
  • Phase C.2 MontanaBehaviour wrapper (commit ba00051)
  • Phase C.3 e2e two-node handshake — Manual Validation Gate scenario 6 PASS (commit 9a15f49)
  • Phase C.4 e2e proposal exchange + 512 KiB boundary — scenario 7 PASS (commit 04f8d29)

Критерий закрытия M6: 2 узла на разных machines обмениваются proposals через network — closed in-process (e2e tests), cross-machine pairing defer to M8 montana-node binary distribution.

M7 — Fast Sync

  • mt-sync — snapshot делiver + verify через Merkle root сравнение, catch-up от snapshot до current window, genesis content replication.

Критерий закрытия M7: новый узел синхронизируется от 0 до текущего state за минуты, не часы.

M8 — Node binary

  • mt-node — CLI, config, wire всё вместе, logging, metrics, graceful shutdown.

Критерий закрытия M8: один бинарь mt-node запускает узел с конфигом, участвует в сети, зарабатывает Монтану (Ɉ) в лотерее.

M9 — Conformance suite READY (initial); expansion in progress

  • mt-conformance (~150 LOC) — публичный test vectors набор для cross-implementation byte-exact verification. Initial vectors: envelope A1/A2/A3 + IBT B1 (after P-C2 rename) + Bootstrap PoW F1/F2 target derivation. 2 unit tests PASS.
  • Expansion: 12 TBD-A markers (consensus objects 0x01..0x22 + app-layer 0x60/0x61/0x64) defer until app-layer payload format finalization.

Критерий закрытия M9: вторая реализация (Swift iOS либо Go) проходит все vectors байт-в-байт. iOS Phase 2.1 — port done (MontanaTests/MTConformanceVectors.swift mirror), pending Xcode xcodebuild test verification.

M10 — Spec compliance cleanup ЗАКРЫТ

Полное приведение кода в соответствие спеке после серии breaking changes (winner_class removal, TransferActivation opcode, single-path node lottery, ACCOUNT_RECORD_SIZE refactor). Закрыто 7 commits, все 4 обязательные проверки зелёные, 593+ тестов passed.

M11 — CloseAccount финализация 🔄 Текущий

  • CloseAccount opcode 0x0B — полная реализация после spec-финализации payload формата: struct + Инварианты CloseAccount, apply_proposal dispatch (вычитание баланса в supply, удаление AccountRecord, освобождение operator-binding если is_node_operator), binding test vectors.

Критерий закрытия M11: payload format CloseAccount специфицирован в спеке, реализация byte-exact, все 4 обязательные проверки зелёные.

M12 — Pure conservation monetary policy compliance ЗАКРЫТ (superseded by M13)

Pre-mainnet миграция к pure geometric pin 41/40, без bootstrap-надбавки, без сжигания, без opcodes прикладного слоя (никнеймы, сервисные кредиты). Superseded M13 const emission cleanup — geometric step-up baseline (41/40) удалён в пользу const EMISSION_moneta = 13 Ɉ per окно. Историческая запись остаётся для context.

M13 — Const emission cleanup ЗАКРЫТ

Pre-mainnet упрощение монетарной политики: pin 41/40 + carry-recurrence заменён на единственную константу EMISSION_moneta = 13 × 10⁹ nɈ per окно. reward_moneta(W) = EMISSION_moneta (const, навсегда), supply_moneta(W) = EMISSION_moneta × (W + 1) closed-form.

Удалено:

  • MonetaryState struct + 24B persistent state + apply_step + 3 controlled overflow panics
  • monetary_epoch_tick apply_proposal Step 2.5
  • r_baseline_at_epoch reconstruction helper
  • save_monetary_state/load_monetary_state mt-store API + monetary.bin file
  • ProtocolParams поля monetary_epoch_windows, inflation_num, inflation_den (24B encoded, 4118 → 4094)
  • m33_emission example
  • Constitutional declaration spec section (Freigeld/Gesell/Frederick/Keynes/Friedman/Schmitt-Grohé/Bordo/Onken/Wörgl academic apparatus)
  • Equilibrium analysis для (41/40)^e + Binding test vectors с эпохами

Замена:

  • ProtocolParams: единственное поле emission_moneta: u128 = 13_000_000_000
  • reward_moneta(params: &ProtocolParams) -> u128 — однострочник params.emission_moneta
  • supply_moneta(W, params) — closed-form emission × (W + 1)
  • compute_state_root(node_root, candidate_root, account_root) — без MonetaryState arg
  • apply_proposal(account_table, node_table, candidate_pool, input, params) — без monetary mut arg, без Step 2.5

Constitutional break: новый Genesis State Hash (ProtocolParams layout + state_root composition меняются) — pre-mainnet нормально.

Критерий закрытия M13: spec + code grep на pattern set (MonetaryState|r_baseline|carry_current|inflation_num|inflation_den|monetary_epoch|R_GENESIS|r_genesis_moneta|MONETARY_STATE|geometric step|41/40|2\.5%|Freigeld|Gesell|Frederick) даёт 0 hits в спеке и в коде.

M1-E — Миграция подписи FN-DSA-512 → ML-DSA-65 ЗАКРЫТ

Pre-mainnet breaking wire-format change: переход на NIST FIPS 204 финализированный стандарт. Закрывает [I-1] (PQ-secure) более mainstream путём (NIST level 3, formally finalized) и устраняет dependency на необфициальный pqcrypto-falcon binding wrapper.

Все 11 шагов Phase E plan executed:

  • E.1: проверка test_vectors.rs (vectors уже обновлены под L=32 для ML-DSA, L=64 для ML-KEM)
  • E.2: 5 KAT vectors сгенерированы через keygen_vectors.rs reference implementation
  • E.3: спека v31.0.0 обновлена — Derivation Vectors 1+2 hex заменён на реальные значения; новый раздел «Binding KAT vectors для KeyGen → terminal observable output» с 5 KAT (SHA-256 fingerprints для byte-exact cross-impl conformance)
  • E.4: mt-crypto::self_test() обновлён — KAT 1 byte-exact conformance check вместо placeholder; добавлены EXPECTED_KAT_1_PK_SHA256 / EXPECTED_KAT_1_SK_SHA256 константы
  • E.5: integration test e2e_recovery.rs — 3 теста (terminal_observable_byte_exact, distinct_entropies_distinct_terminals, account_node_keys_differ_same_master)

Phase F (examples):

  • F.1: m1_mnemonic refactor — cmd_keypair → cmd_seeds, новый cmd_keypair (terminal output), новый cmd_recovery_fingerprint; mt-codec domain registry +1 (mt-recovery-fingerprint)
  • F.2: m1_crypto refactor — cmd_keypair → cmd_keypair_random + новый cmd_keypair_deterministic (default keypair)
  • F.3: verify через release binaries — все subcommands PASS

Phase G (closure):

  • G.1: VERSION.md updated, M1-E entry added
  • G.2: ROADMAP.md M1-E milestone closure
  • G.3: 4 обязательные проверки green (fmt/clippy/test/build --release)
  • G.4: 3 commits — Phase E 8be11f3, Phase F 11f7232, Phase G (этот)

Manual validation готов к авторскому прогону (Validation Gate Scenario 0 и 1 ready):

  • m1_mnemonic recovery-fingerprint — single 64-char hex для two-device validation
  • m1_mnemonic keypair — terminal observable IDs (account_id, node_id) + 6 byte-exact key parts
  • m1_crypto keypair — deterministic recovery primitive sanity
  • m1_mnemonic vectors — 6/6 binding vectors PASS

Acknowledgement: ml-dsa 0.1.0-rc.8 — RustCrypto pure-Rust, NOT formally audited; migration target libcrux-ml-dsa когда formally verified version stable. Любая смена implementation library обязана сохранять byte-identity с KAT fingerprints спеки (cross-impl conformance gate).


Dependency graph

                      sha2, ml-dsa, ml-kem
                                    │
                                    ▼
              ┌───────────────────────────────────────┐
              │                                       │
         mt-codec                               mt-crypto
              │                                       │
              └─────────────┬─────────────────────────┘
                            │
           ┌────────────────┴────────────────┐
           ▼                                 ▼
       mt-merkle                         mt-genesis
           │                                 │
           └──────────────┬──────────────────┘
                          ▼
                      mt-state ◄────── mt-timechain
                          │                  │
                          └────┬─────────────┘
                               ▼
                           mt-account
                               │
           ┌───────────────────┼───────────────────┐
           ▼                   ▼                   ▼
       mt-lottery         mt-consensus          mt-entry
           │                   │                   │
           └───────────────────┼───────────────────┘
                               ▼
                           mt-store
                               │
                               ▼
                           mt-net
                               │
                               ▼
                           mt-sync
                               │
                               ▼
                           mt-node
                               │
                               ▼
                       mt-conformance

Каждая стрелка — [dependencies] в Cargo.toml. Никаких циклов (Rust их запрещает на уровне компилятора).


История обновлений

Дата Изменение Commit
2026-04-21 ROADMAP actualization: R1 stale v29.8.x refs fix + R2 Сценарий 0 User onboarding (24-word mnemonic flow через m1_mnemonic) добавлен в Validation Gate + R3 history entries 2026-04-21 + R4 Starter instructions block для новой сессии. M6 unblock criterion: 6/6 сценариев passed. (этот)
2026-04-21 CRITIC.md v1.2.0 → v1.3.0: 3 новых прохода (Source→Sink Flow, Independent Oracle / Differential Check, Misuse-Resistance API Audit) + reframe mt-examples как operator-facing security surface + hard enforcement Multi-perspective rotation через obligatory per-perspective conclusions block. Catalyst — external critic v1.2.0 поднял 6 blind spots. 5d41d7c
2026-04-21 feat: domain separation structural fix (NUL separator) + external critic findings closure. mt_crypto::hash() теперь SHA-256(domain ‖ 0x00 ‖ parts) — self-delimiting guarantee против 8 prefix-collision pairs в registry. Новая sha256_raw() для FIPS/HMAC/raw. Spec v29.12.0 → v29.13.0. Closes 3 external findings (P1 domain sep, P2 SK leak, P3 label) + 2 mine (P4 empty_internal binding, P5 stale RECOVERY). d762cec
2026-04-21 CRITIC.md v1.1.0 → v1.2.0: 7 новых проходов (timing/side-channel, concurrency, version compat, deps source audit, resource exhaustion беyond DoS, test quality, deployment) + Multi-perspective rotation + Anti-recency bias check + known blind spots (M-1..M-5 documented). bcecc65
2026-04-21 CRITIC.md v1.0.0 → v1.1.0: 4 новых прохода (primitive byte-level audit, registry integrity, output/observable surface, bottom-up reading discipline) + поведение при external critic finding. Reactive response на domain separation bug surface-ит ранее. 74d2142
2026-04-21 doc: sync ROADMAP сценария 1 subcommands с actual m1_crypto binary interface (hash/sign/keypair/merkle-empty/all, не fips-abc/falcon-roundtrip/domain-separation) + m1_mnemonic stale spec refs cleanup. 4126006
2026-04-21 feat: [I-9] closure path A — poly3 minimax заменяет linear log2_q64. mt-lottery Phase C prototype linear (2^-3.5 error) → Remez degree-3 minimax (2^-10.62, theoretical optimum). Binding coefficients B0..B3 halved form unsigned u64, unsigned Horner с intermediate invariants proofs. 14 binding test vectors (5 ln_q64 + 5 weighted_ticket_node + 4 weighted_ticket_account) + 5 target_next vectors в спеке. Spec v29.10.1 → v29.12.0. 4 conformance pending formulas closed. 7b76dc9
2026-04-21 feat: validate_header winner_class ∈ {1,2} check — закрывает P2 external critic реализации (panic в apply_emission от malformed byte в подписанном proposal → liveness attack). HeaderError::InvalidWinnerClass + 2 тест-vectors. Spec v29.10.1 added invariant winner_class ∈ {1,2} в Proposal header. 589be89
2026-04-21 refactor: [C-1] SSOT — WINNER_CLASS_NODE/ACCOUNT в mt-state (единый источник), pub use re-exports в mt-account/mt-lottery. Закрывает P1 external critic реализации (duplicate constants). 5df072d
2026-04-21 Spec bump v29.9.0 → v29.10.1: mt-mnemonic closes [I-9] conformance (6 binding test vectors в спеке для M-1 Algorithm + per-role HKDF derivation). a35af3f
2026-04-20 Validation Gate режим зафиксирован: incremental scenario-by-scenario (один сценарий за сессию), status tracker, starter instructions для новой сессии. M6 unblock = 5/5 passed. (предыдущий)
2026-04-20 M4 + M5 CLOSED: mt-consensus (60 тестов, 5 phases — header + Lookback + control_set + Canonical acceptance + Finalization) + mt-entry (39 тестов, 5 phases — NodeRegistration + Candidate Pool + Selection event + Adaptive VDF + batch apply) + mt-store (24 теста, 5 phases — FsStore + table persist + proposal archive + crash recovery + pruning). Workspace 506 тестов. Next: Manual Validation Gate (5 сценариев). (предыдущий)
2026-04-20 Spec+role fix: target 8B → 16B u128 (header aligned с P5 P[I-9] integer form) + fallback_depth bound [1,255] specified + role 4.13.1 → 4.13.2 Gate 0.5 шаг (d.2) field name coverage. Proposal header [I-9] audit: 17 полей прогнаны — 1 finding (target, fixed), остальные clean. Critic findings P12-P15 закрыты. Ничто: 9113850. 9113850
2026-04-19 ROADMAP: expanded M5 до phases A-E + новая секция «Локальный shakedown — Manual Validation Gate» (5 сценариев между M5 и M6) + policy hands-on example per milestone 2f317b8
2026-04-19 M4 Phase C: mt-lottery Node lottery — seniority_bonus, lottery_weight, log2_q64 (bit-scan + Phase C prototype linear approx), ln_q64, weighted_ticket_node (24 теста). [I-9] compliant, polynomial deferred (closed 2026-04-21 path A) 4cb42bc
2026-04-19 Post-audit fix: critic findings P6-P9 (duplicate lottery formulas без integer form в spec) + P8 (methodology failure — пропуск Шага 8). Ничто commits: 21e1547 (spec duplicate fix), 7be68f5 (role 4.13.0 → 4.13.1 + усилен Gate 0.5 обязательным pre-edit duplicate scan). de68778
2026-04-19 C4 re-audit M2-M3 по [I-9]: next_d соответствует spec (code использовал permille, spec Q32.32 → aligned на permille); emission family (bonus/reward/bootstrap_cumulative/supply) полностью [I-9] compliant без изменений. Spec P4 patched in-place в Ничто; code comment добавлен. 2c14587
2026-04-19 Spec v29.7.1 → v29.8.0: [I-9] Bit-exact deterministic arithmetic инвариант + integer forms для P2/P3/P4/P5 (role+spec atomic в Ничто 1f3f3f9); Phase C unblocked; fix ROADMAP ×/+ ошибка 94c7002
2026-04-19 M4 Phase B: mt-lottery VdfReveal тип + encode/signed_scope/reveal_hash/compute_endpoint/validate_reveal (15 тестов) 5395581
2026-04-24 M10 ЗАКРЫТ — spec compliance cleanup v30.7.0 → v30.9.0. 11 phases закрыты (Phase 7 CloseAccount отложен в M11). 7 commits: 0b55d69 (ROADMAP), 7f19876 (AccountRecord +53B + winner_class cascade), 500fb9f (account lottery cleanup), 2ed18ab (TransferActivation + cooldown), 67868d9 (Nickname/Auction tables + ProtocolParams extension), 533c457 (price_at binding vectors), и этот closure commit. 593+ тестов зелёные, 4 проверки passed. (этот commit)
2026-04-23 M10 Phase 0: VERSION.md bump v30.7.0 → v30.9.0 + ROADMAP M10 milestone block + 13 phases plan + 21 drift points audit 0b55d69
2026-04-19 M4 Phase A: mt-lottery BundledConfirmation тип + encode/signed_scope/bundle_hash/validate (24 теста) 444e399
2026-04-19 Spec v29.7.0 → v29.7.1: editorial cleanup (S-1 дубль mt-node, S-2 genesis_candidate_root, F-1 Genesis State Hash domain) + code fix mt-genesis 6058f3b
2026-04-19 Роль v1.3.0 → v1.4.0: [C-2] Spec Flow Pre-verification глобальный инвариант 0197044
2026-04-19 M3 ЗАКРЫТ — mt-account 6 phases, 102 теста, ~1850 строк. ROADMAP: M4 детально, M5 crude. 30400eb
2026-04-19 M3 Phase F: Genesis state materialization (12 тестов) 30400eb
2026-04-19 M3 Phase E: apply_proposal partial steps 2/3.5/3.6/4 (15 тестов) 5ab2a45
2026-04-19 M3 Phase D: emission reward/bonus/supply (17 тестов) 36503dc
2026-04-19 M3 Phase C: apply individual ops (15 тестов) 8fcca20
2026-04-18 M3 Phase B: validation OpError + 5 функций (23 теста) cf95eaf
2026-04-18 Spec v29.6.9 → v29.7.0: signature architecture (SSI rules R1..R4), breaking cemented_bundle_aggregate → node_ids only 918cdf2
2026-04-17 ROADMAP: M3 phase-детализация + M4 crude phases (правило v1.3.0) e92f0b4
2026-04-17 Роль v1.2.0 → v1.3.0: правило детализации ROADMAP 0893fd4
2026-04-17 M3 Phase A: mt-account types + encoding + op_hash (18 тестов) 0efd92d
2026-04-17 M2 ЗАКРЫТ: mt-timechain (23 теста), итог M2 — 2 пакета, 55 тестов 76ed8da
2026-04-17 M2: mt-state закрыт (32 теста) ab99e23
2026-04-17 Spec v29.6.8 → v29.6.9 (противоречия #2, #3 устранены) 2c148b5
2026-04-17 mt-version crate удалён (нарушение SSOT [C-1]) 8bd008e
2026-04-17 [C-1] SSOT — глобальный инвариант кода в роли 2a9309d
2026-04-17 Spec v29.6.7 → v29.6.8 (противоречие #1: selection_interval) ff390f2
2026-04-17 M1 ЗАКРЫТ: mt-genesis (19+1 тест), итог M1 — 4 пакета, 88 тестов 45c1e84
2026-04-17 M1: mt-merkle закрыт (25 тестов) f242956
2026-04-17 Roadmap: demos отложены до M4+ 7c9219e
2026-04-17 Создан ROADMAP.md 65e34ec
2026-04-17 M1: mt-crypto закрыт (18 тестов) df55372
2026-04-17 M1: mt-codec закрыт (26 тестов) 90464a8
2026-04-17 Роль: post-commit test block обязателен 8d42073
2026-04-17 Роль: language policy — max Russian c06c56b
2026-04-17 Роль: terminal commands one-line a18acc6
2026-04-17 Роль: auto-commit policy cd2f719
2026-04-17 M0: workspace skeleton e2457ad