106 KiB
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.
Принципы разработки
- Одна функция за раз. Explain → Write + tests → Test → Commit. Никаких «напишу весь модуль сразу».
- Success criteria блок до кода. Для каждой consensus-critical функции — spec quote + контракт + чек-лист критериев (см.
CLAUDE.md→ Verifiable success criteria). - Автокоммит. Любое изменение в
Протокол/Код/автоматически завершаетсяgit commit. См.CLAUDE.md→ Git discipline. - Четыре обязательные команды зелёные перед каждым коммитом:
cargo fmt --all -- --checkcargo clippy --all-targets -- -D warningscargo test --allcargo build --all --release
- Byte-for-byte детерминизм. Custom canonical encoding, explicit little-endian, BTreeMap вместо HashMap в consensus, no floats, no system clock. См.
CLAUDE.md→ Byte-for-byte determinism. - Ссылки на спеку в коде. Каждое consensus-critical решение помечается комментарием
// spec, раздел "<название>"без версии. Источник истины версии —VERSION.md. При spec bumpVERSION.mdобновляется, spec-комментарии в коде не трогаются, если раздел не переименован. - 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 тестов. Commit90464a8.mt-crypto— SHA-256 + FN-DSA-512 обёртки (Hash32, PublicKey 897B, SecretKey 1281B, Signature 666B, SuiteId). 18 тестов. Commitdf55372.mt-merkle— sparse Merkle tree глубины 256, InclusionProof + verify_proof, empty_internal через OnceLock. 25 тестов. Commitf242956.mt-genesis— ProtocolParams (25 полей, 2017 байт canonical), Genesis State Hash formula, OnceLock singleton. 19 тестов + 1 ignored (bootstrap keypairs TBD). Commit45c1e84.
Итог 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 теста. Commitab99e23.mt-timechain— vdf_step/verify (T_r = SHA-256^D), next_d (Adaptive D ±3%), cemented_bundle_aggregate (unpredictable-offline binding, 3 ветви). 23 теста. Commit76ed8da.
Итог 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 unsignedlottery_weight = snapshot + seniority_bonus— u64 unsigned, DS-2 floor ≥ 1log2_q64(endpoint)— bit-scan leading_zeros + нормализация мантиссы в [2^127, 2^128) + degree-3 Remez minimax polynomiallog2(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 = 0xB17217F7D1CF79ABweighted_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 для argminAccountLotteryError(2 варианта): OperatorExcluded, ZeroSnapshotvalidate_account_participation(is_node_operator, snapshot)— spec правила 2, 3 из «Валидация участия аккаунта»
- Phase E. Winner determination ✅ (12 тестов)
Candidate { ticket: u128, class: u8, id: [u8;32] }+Winnerdetermine_winner(candidates)— argmin by (ticket asc, class asc, id lex asc) canonical rulesorted_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 теста)
ProposalHeaderstruct, 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 listfallback_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, WrongWinnervalidate_proposer_is_canonical(header, bootstrap, sorted_W-2)— сверка с fallback_proposer(depth)validate_bundles_threshold— делегирует mt_lottery::is_cementedvalidate_included_reveals— byte-exact equality reveal_hashesvalidate_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% capselection_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-1843apply_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/ subdirectoryStoreError: Io, CorruptedLength, ParseFailed, NotFound
- Phase B. Table persistence ✅
save/load_account_table— canonical_encode concat → accounts.bin, fixed-size parsesave/load_node_table— nodes.binsave/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}.binget_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 windowverify_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:
cargo test -p mt-store= 24/24 ✓full_restart_cycle_state_preservedintegration test — open → populate → save → close → reopen → load → roots byte-equal ✓verify_consistency_detects_missing_proposalfault 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 проходится пошагово, по одному сценарию за сессию:
- Architect пишет
examples/mN_name.rsbinary только для текущего сценария - Автор запускает команды на своей машине, копирует output
- Architect разбирает output построчно, сверяет с expected
- Критик задаёт adversarial checklist
- Автор либо подтверждает, либо флагует finding
- Всё OK → статус сценария
✅ passed {дата}в tracker-е ниже - 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_seed → Result<_, 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 — начало следующей сессии
Стандартная последовательность для новой сессии после этой точки:
-
Архитектор реализации погружается в роль. Прочитать
Код/CLAUDE.md(v1.4.0+) построчно. Показать критерии работы. ПроверитьVERSION.md— актуальныйSpec target(на 2026-04-21 = Montana v29.13.0). -
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.
-
Выбрать следующий сценарий Validation Gate (incremental, один за сессию). Первый pending — сценарий 0 «User onboarding». Последовательность: 0 → 1 → 2 → 3 → 4 → 5.
-
Прогон сценария:
- Автор copy-paste команды из соответствующего блока «Сценарий N» ниже.
- Автор запускает на своей машине, копирует output в чат.
- Архитектор разбирает построчно, сверяет с acceptance criteria.
- Критик (роль
Код/CRITIC.mdv1.3.0+) применяет adversarial checklist + min 2 perspectives с отдельными выводами per perspective. - Всё OK → сценарий
✅ passed {дата}в status tracker. - Finding → architect предлагает fix → автор apply
делай→ retry.
-
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 verifiedmnemonic "<24 words>": displayed 24 слова из canonical wordlist, master_seed 64 bytes displayed, 3 derived seeds (48B/48B/64B) displayedkeypair <hex32>: entropy hex → mnemonic 24 words → master_seed → 3 roles seedsroundtrip: entropy → mnemonic → master_seed → mnemonic (roundtrip) → same master_seed idempotency proofall: запускает все 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 classessign: секция «SIGN + VERIFY» —signature 666 bytes,verify(mutated signature) false, 4 adversarial теста все passkeypair N: N × (pubkey 897B, secret 1281B),uniqueness N distinct pubkeysmerkle-empty:TREE_DEPTH 256, derivation trace уровни 0..2, finaleempty_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""])vshash(b"mt-op", &[])— одинаковы? (должны — empty-parts collapse proof в output)hash(b"mt-op", &[b"aa", b"bb"])vshash(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_dboundary 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
На каждую сессию — один сценарий:
- Автор открывает новую сессию репликой «начинаем Gate Scenario N» (или «пиши» если уже понятно).
- Architect пишет
crates/mt-examples/examples/mN_name.rs(новый binary для текущего сценария). - Architect даёт команды одной строкой + expected output pattern.
- Автор запускает на своей машине, копирует output в чат.
- Architect читает output построчно, сверяет с expected.
- Критик задаёт adversarial checklist questions (5-10 вопросов из scenario-блока).
- Автор: «всё работает как ожидал» либо флагует подозрительное.
- Всё OK → architect обновляет status tracker выше: строка сценария →
✅ passed {дата}, commit в ROADMAP. - Finding → fix (в binary или в core crate) → повторный прогон → retry.
Next session trigger: автор открывает новую сессию после passed предыдущего сценария.
Критерий закрытия Validation Gate: все 5 строк tracker-а = ✅ passed. Только после этого — M6.
Starter — с чего начать новую сессию
Первая Gate сессия (Scenario 1):
- Автор: «начинаем Scenario 1 M1 Crypto» (или просто «пиши» после тега ROADMAP контекста)
- Architect: создаёт
crates/mt-examples/crate (Cargo.toml + пустойsrc/lib.rsили dummy), добавляет workspace members entry - Architect: пишет
crates/mt-examples/examples/m1_crypto.rsс 4 subcommands (fips-abc, falcon-roundtrip, merkle-empty, domain-separation) - Architect: даёт команды автору
- 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
Этапы:
-
Фаза 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
-
Фаза 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
- Свежая установка через
-
Фаза 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
-
Фаза 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 финализация 🔄 Текущий
CloseAccountopcode0x0B— полная реализация после 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.
Удалено:
MonetaryStatestruct + 24B persistent state +apply_step+ 3 controlled overflow panicsmonetary_epoch_tickapply_proposal Step 2.5r_baseline_at_epochreconstruction helpersave_monetary_state/load_monetary_statemt-store API +monetary.binfile- ProtocolParams поля
monetary_epoch_windows,inflation_num,inflation_den(−24B encoded, 4118 → 4094) m33_emissionexample- 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_monetasupply_moneta(W, params)— closed-formemission × (W + 1)compute_state_root(node_root, candidate_root, account_root)— без MonetaryState argapply_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 F11f7232, Phase G (этот)
Manual validation готов к авторскому прогону (Validation Gate Scenario 0 и 1 ready):
m1_mnemonic recovery-fingerprint— single 64-char hex для two-device validationm1_mnemonic keypair— terminal observable IDs (account_id, node_id) + 6 byte-exact key partsm1_crypto keypair— deterministic recovery primitive sanitym1_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 |