ticket #000035: flip φ_PRG counter big-endian → little-endian to match v7 TLV
v7's canonical integer byte-order was confirmed little-endian by
inspecting merkle-agi-dag_v7.txt §A1 — every to_bytes / astype in the
TLV encoding is little-endian (TLV length prefixes to_bytes(4,'little'),
enc_int to_bytes(8,'little'), quantized tensors '<i8'); no big-endian
anywhere. Per dav1d's 2026-05-11 review rule ("if v7 TLV canonical
integer encoding is little-endian, flip §3.4 to little-endian before
KAT freeze"), flip done — this is the -le variant.
Implementation (arborist/substrate/anchor_prg.py):
- PHI_PRG_VERSION → "phi-prg-v1-hmac-sha512-le" (still "v1";
the -le suffix records the endianness; future re-flip MUST bump).
- _expand: counter.to_bytes(4, 'big') → 'little'.
- _bytes_to_floats: int.from_bytes(..., 'big') → 'little' (the
uint32-word interpretation, for full consistency with v7).
- Module + function docstrings updated: little-endian throughout,
with the merkle-agi-dag_v7.txt §A1 verification note.
- Note: at counter=0 the bytes are identical regardless of
endianness, so 5 of the 10 KAT entries (dim_h ≤ 16, single block)
keep the same output_sha256; the 5 multi-block entries (dim_h 17/
32/64×3/4096) change.
KAT fixture (bench/fixtures/phi-prg/known-answer-tests.jsonl):
- Regenerated under the little-endian counter. Each entry now also
carries a "version" field (phi-prg-v1-hmac-sha512-le). Header
comment updated.
Tests (tests/test_anchor_prg.py, 30 → 31):
- test_module_exports_version_string: assert the -le suffix.
- test_bytes_to_floats_midpoint_maps_to_zero: 2^31 is b'\x00\x00\x00\x80'
in little-endian, not b'\x80\x00\x00\x00'.
- New test_bytes_to_floats_reads_little_endian: pins the byte-order
so an accidental re-flip is caught.
- test_phi_prg_first_block_matches_direct_hmac: uint32-word reads
little-endian (counter=0 bytes unchanged either way).
- test_phi_prg_known_answer_tests: assert kat['version'] == module
version when present.
Spec text (#000035 §3.4): folded the little-endian variant of
dav1d's §9.10 wording — counter_le32, uint32_le word reads, an
"all integers little-endian, matching v7 TLV §A1" preamble, and an
"Endianness — RESOLVED 2026-05-11" note replacing the open
big-vs-little question. soft-hash-channel-analysis.md §9.2/§11 +
#000035 status + TICKETS.md row updated. AUTOCOUNT for
test_anchor_prg.py bumped 30 → 31; PHI_PRG_VERSION refs in docs
bumped to -le.
Full suite: 2312 passed, 28 skipped.
This commit is contained in:
parent
d78dccc8ed
commit
101b101281
9 changed files with 144 additions and 94 deletions
|
|
@ -12,13 +12,21 @@ Construction — HMAC-SHA-512 in NIST SP 800-108 KDF counter mode:
|
||||||
i = 0
|
i = 0
|
||||||
out = b""
|
out = b""
|
||||||
while len(out) < n_bytes:
|
while len(out) < n_bytes:
|
||||||
out += HMAC-SHA-512(SEED, C(M) || i.to_bytes(4, 'big'))
|
out += HMAC-SHA-512(SEED, C(M) || i.to_bytes(4, 'little'))
|
||||||
i += 1
|
i += 1
|
||||||
return out[:n_bytes]
|
return out[:n_bytes]
|
||||||
|
|
||||||
Float conversion (uint32_be → [-1, 1)):
|
Float conversion (uint32_le → [-1, 1)):
|
||||||
f(u32) := 2 * (u32 / 2**32) - 1
|
f(u32) := 2 * (u32 / 2**32) - 1
|
||||||
|
|
||||||
|
Byte order is **little-endian** throughout — matching v7's canonical
|
||||||
|
TLV encoding (verified 2026-05-11 against ``merkle-agi-dag_v7.txt``
|
||||||
|
§A1: TLV length prefixes ``to_bytes(4, "little")``, ``enc_int`` is
|
||||||
|
``to_bytes(8, "little")``, tensors are ``<i8``). dav1d's 2026-05-11
|
||||||
|
§3.4 review: "if v7 TLV canonical integer encoding is little-endian,
|
||||||
|
flip §3.4 to little-endian before KAT freeze." Done — this is the
|
||||||
|
``-le`` variant; ``PHI_PRG_VERSION`` carries the ``-le`` suffix.
|
||||||
|
|
||||||
Security: HMAC-SHA-512 is a PRF under the standard SHA-512 + HMAC
|
Security: HMAC-SHA-512 is a PRF under the standard SHA-512 + HMAC
|
||||||
assumption. Distinguishing advantage from random is bounded by the
|
assumption. Distinguishing advantage from random is bounded by the
|
||||||
SHA-512 collision-resistance bound (~2^256), which structurally
|
SHA-512 collision-resistance bound (~2^256), which structurally
|
||||||
|
|
@ -58,7 +66,7 @@ import hmac
|
||||||
# constant exists so that test fixtures and KAT data have a stable
|
# constant exists so that test fixtures and KAT data have a stable
|
||||||
# placeholder to reference. Bytes-literal so accidentally substituting
|
# placeholder to reference. Bytes-literal so accidentally substituting
|
||||||
# a string raises a clear TypeError at hmac.new() time.
|
# a string raises a clear TypeError at hmac.new() time.
|
||||||
PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512"
|
PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512-le"
|
||||||
|
|
||||||
# Maximum dim_h: a 4-byte counter admits 2^32 HMAC-SHA-512 blocks of
|
# Maximum dim_h: a 4-byte counter admits 2^32 HMAC-SHA-512 blocks of
|
||||||
# 64 bytes = 16 float32-words each (#000035 §3.4). Astronomically above
|
# 64 bytes = 16 float32-words each (#000035 §3.4). Astronomically above
|
||||||
|
|
@ -138,38 +146,35 @@ def _expand(seed: bytes, hard_hash: bytes, n_bytes: int) -> bytes:
|
||||||
|
|
||||||
Block size is the HMAC-SHA-512 output (64 bytes); we ceil-divide
|
Block size is the HMAC-SHA-512 output (64 bytes); we ceil-divide
|
||||||
to the smallest counter range that yields ``n_bytes`` output, then
|
to the smallest counter range that yields ``n_bytes`` output, then
|
||||||
truncate the last block. Counter is big-endian 4-byte unsigned,
|
truncate the last block. Counter is **little-endian** 4-byte
|
||||||
starting at 0. The ``dim_h ≤ 16·2^32`` guard in ``phi_prg`` keeps
|
unsigned, starting at 0 — matching v7's TLV integer encoding
|
||||||
the counter from exhausting; if a bug ever drove it past 2^32
|
(#000035 §3.4, dav1d review 2026-05-11). The ``dim_h ≤ 16·2^32``
|
||||||
blocks, ``int.to_bytes(4, 'big')`` would raise ``OverflowError``
|
guard in ``phi_prg`` keeps the counter from exhausting; if a bug
|
||||||
rather than silently wrapping.
|
ever drove it past 2^32 blocks, ``int.to_bytes(4, 'little')``
|
||||||
|
would raise ``OverflowError`` rather than silently wrapping.
|
||||||
**Endianness note (#000035 §3.4).** Big-endian here is pinned to
|
|
||||||
the v7 spec amendment and the 10 KAT vectors. If v7's canonical
|
|
||||||
TLV integer encoding turns out to be little-endian, this flips —
|
|
||||||
but only with a ``PHI_PRG_VERSION`` bump + regenerated KATs.
|
|
||||||
"""
|
"""
|
||||||
out = bytearray()
|
out = bytearray()
|
||||||
counter = 0
|
counter = 0
|
||||||
while len(out) < n_bytes:
|
while len(out) < n_bytes:
|
||||||
msg = hard_hash + counter.to_bytes(4, "big")
|
msg = hard_hash + counter.to_bytes(4, "little")
|
||||||
out += hmac.new(seed, msg, hashlib.sha512).digest()
|
out += hmac.new(seed, msg, hashlib.sha512).digest()
|
||||||
counter += 1
|
counter += 1
|
||||||
return bytes(out[:n_bytes])
|
return bytes(out[:n_bytes])
|
||||||
|
|
||||||
|
|
||||||
def _bytes_to_floats(raw: bytes) -> list[float]:
|
def _bytes_to_floats(raw: bytes) -> list[float]:
|
||||||
"""Map each big-endian uint32 to a float in [-1, 1).
|
"""Map each little-endian uint32 to a float in [-1, 1).
|
||||||
|
|
||||||
Per #000035 §2.3 / §3.4: ``f(u32) = 2 * (u32 / 2**32) - 1``.
|
Per #000035 §2.3 / §3.4: ``f(u32) = 2 * (u32 / 2**32) - 1``. Words
|
||||||
Output is uniform over a 2^32-point grid in ``[-1, 1)`` with step
|
are read **little-endian** to match v7's TLV integer convention
|
||||||
``2^-31``: ``f(0) = -1.0`` (reachable), ``f(2^32-1) = 1 - 2^-31``
|
(dav1d review 2026-05-11). Output is uniform over a 2^32-point grid
|
||||||
(so ``+1.0`` is never reached). The finite-grid mean is ``−2^−32``
|
in ``[-1, 1)`` with step ``2^-31``: ``f(0) = -1.0`` (reachable),
|
||||||
— negligible for the anchor-map use case, but **not exactly
|
``f(2^32-1) = 1 - 2^-31`` (so ``+1.0`` is never reached). The
|
||||||
zero-mean**. If exact zero-mean is ever required, switch to the
|
finite-grid mean is ``−2^−32`` — negligible for the anchor-map use
|
||||||
midpoint map ``f(u32) = 2*((u32+0.5)/2^32) - 1`` (range ``(-1, 1)``,
|
case, but **not exactly zero-mean**. If exact zero-mean is ever
|
||||||
exact zero mean) with a ``PHI_PRG_VERSION`` bump + new KATs —
|
required, switch to the midpoint map ``f(u32) = 2*((u32+0.5)/2**32)
|
||||||
never change the formula silently.
|
- 1`` (range ``(-1, 1)``, exact zero mean) with a ``PHI_PRG_VERSION``
|
||||||
|
bump + new KATs — never change the formula silently.
|
||||||
"""
|
"""
|
||||||
n = len(raw) // 4
|
n = len(raw) // 4
|
||||||
if len(raw) != n * 4:
|
if len(raw) != n * 4:
|
||||||
|
|
@ -180,7 +185,7 @@ def _bytes_to_floats(raw: bytes) -> list[float]:
|
||||||
floats: list[float] = []
|
floats: list[float] = []
|
||||||
inv_2_32 = 1.0 / (1 << 32)
|
inv_2_32 = 1.0 / (1 << 32)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
u32 = int.from_bytes(raw[4 * i : 4 * (i + 1)], "big")
|
u32 = int.from_bytes(raw[4 * i : 4 * (i + 1)], "little")
|
||||||
floats.append(2.0 * (u32 * inv_2_32) - 1.0)
|
floats.append(2.0 * (u32 * inv_2_32) - 1.0)
|
||||||
return floats
|
return floats
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
# arborist v7 phi_prg known-answer tests — version phi-prg-v1-hmac-sha512
|
# arborist v7 phi_prg known-answer tests — version phi-prg-v1-hmac-sha512-le
|
||||||
# Pinned (seed, hard_hash, dim_h) → SHA-256 of raw byte output
|
# Pinned (seed, hard_hash, dim_h) → SHA-256 of raw byte output
|
||||||
# (HMAC-SHA-512 counter-mode expansion before float conversion).
|
# (HMAC-SHA-512 little-endian counter-mode expansion, before float
|
||||||
# Algorithm change MUST bump PHI_PRG_VERSION and create a new
|
# conversion). Regenerated 2026-05-11 after the big-endian → little-
|
||||||
# fixture file; do not overwrite — old runs replay against old data.
|
# endian counter flip (dav1d review; PHI_PRG_VERSION carries -le).
|
||||||
|
# Algorithm change MUST bump PHI_PRG_VERSION and create a new fixture
|
||||||
{"label": "placeholder-seed/zero-hash/dim_h=1", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 1, "output_sha256": "93618e085f1afae3368cabb57b328f2f01a81cb45c768e02264b13d5ec52732a", "output_bytes": 4}
|
# file; do not overwrite — old runs replay against old data.
|
||||||
{"label": "placeholder-seed/zero-hash/dim_h=8", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 8, "output_sha256": "b7e7af7180105100e94fcb4361799e2820d78f9cf2636643dea2a367ea16beb8", "output_bytes": 32}
|
{"label": "placeholder-seed/zero-hash/dim_h=1", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 1, "output_sha256": "93618e085f1afae3368cabb57b328f2f01a81cb45c768e02264b13d5ec52732a", "output_bytes": 4}
|
||||||
{"label": "placeholder-seed/zero-hash/dim_h=32", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 32, "output_sha256": "537bf81bdb0bc6300ffe9e9853ecf42a19ee4ea10be11d76624dd83d5528d55a", "output_bytes": 128}
|
{"label": "placeholder-seed/zero-hash/dim_h=8", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 8, "output_sha256": "b7e7af7180105100e94fcb4361799e2820d78f9cf2636643dea2a367ea16beb8", "output_bytes": 32}
|
||||||
{"label": "placeholder-seed/all-ones-hash/dim_h=16", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "dim_h": 16, "output_sha256": "2466a75eda007980acea20ed9f1d8676c36700b6da15220455268151a0f6072c", "output_bytes": 64}
|
{"label": "placeholder-seed/zero-hash/dim_h=32", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000", "dim_h": 32, "output_sha256": "1b5e89f20777000d0051daffb79aaf93e1e8bba7d3a77d5f6b4354561800d365", "output_bytes": 128}
|
||||||
{"label": "seed=A/hash=B/dim_h=64", "seed_hex": "14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "c6b107e73d4b4f23f3ee8991ab953dc4ecc5af35405bf5a97ce66a0fd41c4245", "output_bytes": 256}
|
{"label": "placeholder-seed/all-ones-hash/dim_h=16", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "ef532720a49159beb6816d98e13a162bac63c531b631bd1adb0fcca96b467ff3", "hard_hash_hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "dim_h": 16, "output_sha256": "2466a75eda007980acea20ed9f1d8676c36700b6da15220455268151a0f6072c", "output_bytes": 64}
|
||||||
{"label": "seed=A/hash=B'/dim_h=64 (one-bit-flip from prior)", "seed_hex": "14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "223cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "a29cbf930ae89cd10c816fc5dd03b97354ee0a303e62e92822f9331c42b94f96", "output_bytes": 256}
|
{"label": "seed=A/hash=B/dim_h=64", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "3c45b49e21fa5c13b847325b3a974de2545ef578b3856466b3760e5d8dc25a11", "output_bytes": 256}
|
||||||
{"label": "seed=A'/hash=B/dim_h=64 (one-bit-flip seed)", "seed_hex": "94ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "ce1b60357bbd3354f65a16c1e51bd12de186bbb7765a728fae04fdbb09973f31", "output_bytes": 256}
|
{"label": "seed=A/hash=B'/dim_h=64 (one-bit-flip from prior)", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "14ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "223cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "0cea076d0f088f3888312ec33c8fa60602ad7a5e4a2fd5c78584737d613b9888", "output_bytes": 256}
|
||||||
{"label": "block-boundary/dim_h=16", "seed_hex": "cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58", "hard_hash_hex": "724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", "dim_h": 16, "output_sha256": "75d1eb90b4d385b8d475fa53539eac129b0abfab9e784c0f3be5cf2738c40dec", "output_bytes": 64}
|
{"label": "seed=A'/hash=B/dim_h=64 (one-bit-flip seed)", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "94ab2dab0d3cddeaa58ec70632d3ff4f5de2c514004a92144e260dfe384d912e", "hard_hash_hex": "a23cb10b94660f062b467f313a9dc9d84f2dc2748c3627c661082dda7f55e3bb", "dim_h": 64, "output_sha256": "ab0350482a8734c40ddfb965eb8f4996a8cd4c896d419f292dc66863a7c014bf", "output_bytes": 256}
|
||||||
{"label": "block-boundary/dim_h=17", "seed_hex": "cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58", "hard_hash_hex": "724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", "dim_h": 17, "output_sha256": "3d9226cff6da50e5610cceba54b191223b2dfd5bbbdd3bd0f807062762fb3827", "output_bytes": 68}
|
{"label": "block-boundary/dim_h=16", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58", "hard_hash_hex": "724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", "dim_h": 16, "output_sha256": "75d1eb90b4d385b8d475fa53539eac129b0abfab9e784c0f3be5cf2738c40dec", "output_bytes": 64}
|
||||||
{"label": "stress/dim_h=4096", "seed_hex": "0ddd62c311f88ebe2d4f6cd5d9d1374474dfd645e012043648dd966a71785c95", "hard_hash_hex": "e605ede3d9d0d13c6d7d32c5c424b998677eef0689a0d9f0fa4ebd1bb4307cb9", "dim_h": 4096, "output_sha256": "5072d05b17eb4f4b3356bfc66f772330337dfcc1b3bb8fa87fc0ae0568e9a387", "output_bytes": 16384}
|
{"label": "block-boundary/dim_h=17", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "cfd60c2bda64ebcefbb23a5b28d98269c9c4f8b8ac77f6f9ca7a0f4865b10f58", "hard_hash_hex": "724cd966a7bfe78ba802877510ffb90c67f385a1d3135e4e1b8a1b38f744c6da", "dim_h": 17, "output_sha256": "abce601ee72fdc7ca1bf1ee4aba58cc879a6e83d3d7e41e996db29cd30b0d3d4", "output_bytes": 68}
|
||||||
|
{"label": "stress/dim_h=4096", "version": "phi-prg-v1-hmac-sha512-le", "seed_hex": "0ddd62c311f88ebe2d4f6cd5d9d1374474dfd645e012043648dd966a71785c95", "hard_hash_hex": "e605ede3d9d0d13c6d7d32c5c424b998677eef0689a0d9f0fa4ebd1bb4307cb9", "dim_h": 4096, "output_sha256": "ff01f8bf2c516d2c54f595338c981cd853b0cff077ecf8eefdb5cf0dae69e55c", "output_bytes": 16384}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ Newest first. Update on every open/close.
|
||||||
| #000038 | Phase 4 content acquisition — proprietary textbook license decisions for warrant coverage | closed · obviated 2026-05-10 by alias-substitution sprint under #000031 (74 rows in #000041 + 13 rows in #000042); 92/92 records now resolve. Residue (multilingual PD, Hilbert-Ackermann OCR, Knuth permission, personal-copy path B) preserved as design log §8 | 2026-05-09 | — |
|
| #000038 | Phase 4 content acquisition — proprietary textbook license decisions for warrant coverage | closed · obviated 2026-05-10 by alias-substitution sprint under #000031 (74 rows in #000041 + 13 rows in #000042); 92/92 records now resolve. Residue (multilingual PD, Hilbert-Ackermann OCR, Knuth permission, personal-copy path B) preserved as design log §8 | 2026-05-09 | — |
|
||||||
| #000037 | Prometheus-Σ recursive falsification controller (bicameral substrate) | in progress · Phases 0 + 1 + 1.b + 1.c + 2 landed 2026-05-10; **§12 Trigger 2 fired** (divergence variance 0.575 / N=37); §22 Findings 2 + 3 RESOLVED (kernel/llm cost split + sweep_weights §15.4 + per-mode τ_qa); `controller_events` carries 4 event kinds (decision · difficulty · budget_allocation · falsification_proposal) feeding `arborist controller-events` inspector + live-harvest third bucket in `bench/scripts/harvest_falsification_proposals.py`; §12 Trigger 1 probe wired 2026-05-11 (`trigger_1_branch_density` reads `fork_score_branches` — measurable, not yet fired); Phase 3 sleep-sweep scheduler tracked under #000045 (gating ticket) | 2026-05-09 | — |
|
| #000037 | Prometheus-Σ recursive falsification controller (bicameral substrate) | in progress · Phases 0 + 1 + 1.b + 1.c + 2 landed 2026-05-10; **§12 Trigger 2 fired** (divergence variance 0.575 / N=37); §22 Findings 2 + 3 RESOLVED (kernel/llm cost split + sweep_weights §15.4 + per-mode τ_qa); `controller_events` carries 4 event kinds (decision · difficulty · budget_allocation · falsification_proposal) feeding `arborist controller-events` inspector + live-harvest third bucket in `bench/scripts/harvest_falsification_proposals.py`; §12 Trigger 1 probe wired 2026-05-11 (`trigger_1_branch_density` reads `fork_score_branches` — measurable, not yet fired); Phase 3 sleep-sweep scheduler tracked under #000045 (gating ticket) | 2026-05-09 | — |
|
||||||
| #000036 | T3 per-window covert-channel budget bound | in progress · Phase 1 + dav1d review 2026-05-11 → Tier-1 + Tier-2 (Option B = `b1_model=max_envelope` default, applied *in v1*, no v2 fork) both landed 2026-05-11; KAT regenerated (12 entries, active); baseline 625.87 → 6183.02 (max_envelope); 53 → 83 tests; both prior closure blockers cleared — remaining = fox final close-or-iterate call | 2026-05-09 | — |
|
| #000036 | T3 per-window covert-channel budget bound | in progress · Phase 1 + dav1d review 2026-05-11 → Tier-1 + Tier-2 (Option B = `b1_model=max_envelope` default, applied *in v1*, no v2 fork) both landed 2026-05-11; KAT regenerated (12 entries, active); baseline 625.87 → 6183.02 (max_envelope); 53 → 83 tests; both prior closure blockers cleared — remaining = fox final close-or-iterate call | 2026-05-09 | — |
|
||||||
| #000035 | PRG choice for φ_PRG (HMAC-SHA-512 expansion) | in progress · Phase 1 landed 2026-05-10; §9.10 amendment text **dav1d-reviewed final 2026-05-11** (manifest field → `anchor_prg_seed`; float-map prose corrected to "negligible mean −2^−32" not "unbiased"; `dim_h ≤ 16·2^32` guard + bool-reject added to impl; seed-independence + M1-policy-separation added; HMAC-SHA-512 / 32-byte / uint32-be / SHALL all LOCKED). Remaining: confirm v7 integer endianness + land §9.10 into v7 spec when plastic-training has a deployment target | 2026-05-09 | — |
|
| #000035 | PRG choice for φ_PRG (HMAC-SHA-512 expansion) | in progress · Phase 1 landed 2026-05-10; §9.10 amendment text **dav1d-reviewed final + endianness resolved 2026-05-11** (manifest field → `anchor_prg_seed`; v7 TLV confirmed little-endian → counter + uint32-word reads flipped big→little, `PHI_PRG_VERSION → -le`, 10 KATs regenerated; float-map prose corrected to "negligible mean −2^−32" not "unbiased"; `dim_h ≤ 16·2^32` guard + bool-reject added; seed-independence + M1-policy-separation added; HMAC-SHA-512 / 32-byte / SHALL all LOCKED; 31 tests). Remaining: land §9.10 into v7 spec when plastic-training has a deployment target (exogenous) | 2026-05-09 | — |
|
||||||
| #000034 | Hessian alignment under φ_linear | in progress · Phase 1a landed 2026-05-10 (synthetic-ablation probe + KAT fixture); Phase 1b parks for v7 ramp-up | 2026-05-09 | — |
|
| #000034 | Hessian alignment under φ_linear | in progress · Phase 1a landed 2026-05-10 (synthetic-ablation probe + KAT fixture); Phase 1b parks for v7 ramp-up | 2026-05-09 | — |
|
||||||
| #000033 | Claim-pack pillar VII (combinatorics) | closed · landed 2026-05-09 (live in shard 000.db; lift verified) | 2026-05-09 | — |
|
| #000033 | Claim-pack pillar VII (combinatorics) | closed · landed 2026-05-09 (live in shard 000.db; lift verified) | 2026-05-09 | — |
|
||||||
| #000032 | combinatorics@v1 π* (integer counting kernel) | closed · landed 2026-05-09 | 2026-05-09 | — |
|
| #000032 | combinatorics@v1 π* (integer counting kernel) | closed · landed 2026-05-09 | 2026-05-09 | — |
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ The reduction in §4 leaves three loose threads:
|
||||||
**Phase 1 landed 2026-05-10** under #000035: reference
|
**Phase 1 landed 2026-05-10** under #000035: reference
|
||||||
implementation at `arborist/substrate/anchor_prg.py`
|
implementation at `arborist/substrate/anchor_prg.py`
|
||||||
(HMAC-SHA-512 counter-mode KDF, pure stdlib — `hashlib` +
|
(HMAC-SHA-512 counter-mode KDF, pure stdlib — `hashlib` +
|
||||||
`hmac`, no third-party crypto dep). <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->30<!--/AUTOCOUNT--> tests in
|
`hmac`, no third-party crypto dep). <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT--> tests in
|
||||||
`tests/test_anchor_prg.py` covering determinism, range
|
`tests/test_anchor_prg.py` covering determinism, range
|
||||||
invariants, chi² uniformity, dim_h boundary (1, 16384),
|
invariants, chi² uniformity, dim_h boundary (1, 16384),
|
||||||
seed-bit-flip and hash-bit-flip avalanche, hand-computed
|
seed-bit-flip and hash-bit-flip avalanche, hand-computed
|
||||||
|
|
@ -428,7 +428,7 @@ The reduction in §4 leaves three loose threads:
|
||||||
covering block-boundary cases (dim_h=16 = one HMAC block;
|
covering block-boundary cases (dim_h=16 = one HMAC block;
|
||||||
dim_h=17 = two blocks with truncation), one-bit-flip
|
dim_h=17 = two blocks with truncation), one-bit-flip
|
||||||
variants, and a 4096-element counter-rollover stress sample.
|
variants, and a 4096-element counter-rollover stress sample.
|
||||||
Module exports `PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512"`
|
Module exports `PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512-le"`
|
||||||
so future algorithm rotation is detectable at the call site
|
so future algorithm rotation is detectable at the call site
|
||||||
without string-comparing module paths.
|
without string-comparing module paths.
|
||||||
|
|
||||||
|
|
@ -436,19 +436,24 @@ The reduction in §4 leaves three loose threads:
|
||||||
#000035 §3.4 holds the final wording. Decisions: HMAC-SHA-512
|
#000035 §3.4 holds the final wording. Decisions: HMAC-SHA-512
|
||||||
LOCK; **manifest field `anchor_prg_seed`** (purpose-scoped;
|
LOCK; **manifest field `anchor_prg_seed`** (purpose-scoped;
|
||||||
`phi_prg_seed` is a code-local alias only); 32-byte seed LOCK;
|
`phi_prg_seed` is a code-local alias only); 32-byte seed LOCK;
|
||||||
`uint32` big-endian counter from 0 LOCK (flip to little-endian
|
**`uint32` little-endian counter + uint32-word reads from 0** —
|
||||||
only if v7's TLV convention already is); float map
|
matching v7's canonical TLV integer encoding (`merkle-agi-dag_v7.txt`
|
||||||
`2·(u32/2^32)−1` kept for KAT compatibility but the prose now
|
§A1: TLV length prefixes + `enc_int` are `to_bytes(…, "little")`,
|
||||||
says "uniform over a 2^32-point grid in [-1, 1) with negligible
|
tensors are `<i8`). dav1d's review rule said "if v7 TLV is
|
||||||
finite-grid mean −2^−32" rather than "unbiased"; `SHALL`
|
little-endian, flip §3.4 to little-endian before KAT freeze" —
|
||||||
replace `embed_hard_to_vec` when M1 enabled (M1 enablement
|
done; `PHI_PRG_VERSION → "phi-prg-v1-hmac-sha512-le"`, the 10
|
||||||
itself is a separate mitigation-selection-policy question, with
|
KAT vectors regenerated under the little-endian counter. Float
|
||||||
the NO_ALIGNMENT-skip rule documented in §9.10.1); plus a
|
map `2·(u32_le/2^32)−1`; prose says "uniform over a 2^32-point
|
||||||
`dim_h ≤ 16·2^32` exhaustion guard and a seed-independence /
|
grid in [-1, 1) with negligible finite-grid mean −2^−32" rather
|
||||||
single-purpose-seed requirement. The reference implementation +
|
than "unbiased"; `SHALL` replace `embed_hard_to_vec` when M1
|
||||||
tests + KAT vectors are pinned to these choices. Phase 2 lands
|
enabled (M1 enablement itself is a separate
|
||||||
the §9.10 text into the v7 plastic-training spec when one gains
|
mitigation-selection-policy question, with the NO_ALIGNMENT-skip
|
||||||
an active deployment target.
|
rule documented in §9.10.1); plus a `dim_h ≤ 16·2^32` exhaustion
|
||||||
|
guard and a seed-independence / single-purpose-seed requirement.
|
||||||
|
The reference implementation + 31 tests + 10 KAT vectors are
|
||||||
|
pinned to these choices. Phase 2 lands the §9.10 text into the
|
||||||
|
v7 plastic-training spec when one gains an active deployment
|
||||||
|
target.
|
||||||
|
|
||||||
3. **T3 per-window budget formal bound.** ~~§6 lists "bounded by
|
3. **T3 per-window budget formal bound.** ~~§6 lists "bounded by
|
||||||
per-window budget" without giving the bound.~~ **Closed
|
per-window budget" without giving the bound.~~ **Closed
|
||||||
|
|
|
||||||
|
|
@ -731,7 +731,7 @@ Two cryptographic-primitive Phase 1 deliverables landed
|
||||||
`bench/fixtures/phi-alignment/synthetic-checkpoints.jsonl`.
|
`bench/fixtures/phi-alignment/synthetic-checkpoints.jsonl`.
|
||||||
- **#000035 Phase 1** (earlier today): φ_PRG reference impl at
|
- **#000035 Phase 1** (earlier today): φ_PRG reference impl at
|
||||||
`arborist/substrate/anchor_prg.py` (HMAC-SHA-512 counter-mode
|
`arborist/substrate/anchor_prg.py` (HMAC-SHA-512 counter-mode
|
||||||
KDF) + <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->30<!--/AUTOCOUNT--> tests (was 20; +7 from `de997f7` 2026-05-10 pattern
|
KDF) + <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT--> tests (was 20; +7 from `de997f7` 2026-05-10 pattern
|
||||||
backfill — prefix-extension closure invariant, hand-computed
|
backfill — prefix-extension closure invariant, hand-computed
|
||||||
HMAC-SHA-512 first-block formula, parametrized invalid-input
|
HMAC-SHA-512 first-block formula, parametrized invalid-input
|
||||||
cones) + <!--AUTOCOUNT:fixture-rows:bench/fixtures/phi-prg/known-answer-tests.jsonl-->10<!--/AUTOCOUNT-->-vector KAT fixture at
|
cones) + <!--AUTOCOUNT:fixture-rows:bench/fixtures/phi-prg/known-answer-tests.jsonl-->10<!--/AUTOCOUNT-->-vector KAT fixture at
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Ticket #000035 — PRG choice for φ_PRG
|
# Ticket #000035 — PRG choice for φ_PRG
|
||||||
|
|
||||||
**Status:** in progress · Phase 1 landed 2026-05-10; **§9.10 spec amendment text dav1d-reviewed final 2026-05-11** (§3.4 below holds the folded version). Review decisions applied: manifest field renamed `phi_prg_seed → anchor_prg_seed` (purpose-scoped; `phi_prg_seed` kept as a code-local alias); float-map prose corrected ("uniform over a 2^32-point grid in [-1, 1) with negligible mean −2^−32", not "unbiased" — formula unchanged for KAT compat); `dim_h ≤ 16·2^32` exhaustion guard added to `arborist/substrate/anchor_prg.py` (+ bool-`dim_h` reject); seed-independence / single-purpose-seed rule added; M1-enablement-policy separation (§9.10.1: skipping M1 governed by mitigation-selection policy, e.g. NO_ALIGNMENT per #000034, not by §9.10). HMAC-SHA-512 / 32-byte seed / uint32-be counter / `SHALL`-replace all LOCKED. Remaining: confirm v7's canonical integer endianness (big-endian assumed) before the §9.10 text freezes into the v7 spec; the actual landing-into-v7-spec step still parks on v7 plastic-training gaining a deployment target.
|
**Status:** in progress · Phase 1 landed 2026-05-10; **§9.10 spec amendment text dav1d-reviewed final 2026-05-11** (§3.4 below holds the folded version). Review decisions applied: manifest field renamed `phi_prg_seed → anchor_prg_seed` (purpose-scoped; `phi_prg_seed` kept as a code-local alias); float-map prose corrected ("uniform over a 2^32-point grid in [-1, 1) with negligible mean −2^−32", not "unbiased"); `dim_h ≤ 16·2^32` exhaustion guard added to `arborist/substrate/anchor_prg.py` (+ bool-`dim_h` reject); seed-independence / single-purpose-seed rule added; M1-enablement-policy separation (§9.10.1: skipping M1 governed by mitigation-selection policy, e.g. NO_ALIGNMENT per #000034, not by §9.10). **Endianness RESOLVED 2026-05-11**: v7's canonical TLV integer encoding is little-endian (`merkle-agi-dag_v7.txt` §A1), so per dav1d's review rule the φ_PRG counter + uint32-word reads were flipped big-endian → little-endian; `PHI_PRG_VERSION → phi-prg-v1-hmac-sha512-le`; the 10 KAT vectors regenerated; `test_anchor_prg.py` updated (30 → 31). HMAC-SHA-512 / 32-byte seed / `SHALL`-replace all LOCKED. Remaining: land §9.10 into the v7 plastic-training spec when that spec gains a deployment target (exogenous).
|
||||||
**Opened:** 2026-05-09
|
**Opened:** 2026-05-09
|
||||||
**Scope:** Pin a specific cryptographic PRG construction for the
|
**Scope:** Pin a specific cryptographic PRG construction for the
|
||||||
M1 mitigation (PRG-based anchor map) proposed in #000018 §5.2 +
|
M1 mitigation (PRG-based anchor map) proposed in #000018 §5.2 +
|
||||||
|
|
@ -192,25 +192,34 @@ regression coverage. Generated once, pinned.
|
||||||
|
|
||||||
### 3.4 Spec amendment
|
### 3.4 Spec amendment
|
||||||
|
|
||||||
v7 § 9.10 amendment text — **dav1d-reviewed final (2026-05-11)**.
|
v7 § 9.10 amendment text — **dav1d-reviewed final (2026-05-11),
|
||||||
Decision set (`RESPONSE` 2026-05-11): HMAC-SHA-512 — LOCK; manifest
|
little-endian variant**. Decision set (`RESPONSE` 2026-05-11) +
|
||||||
|
endianness resolution (2026-05-11): HMAC-SHA-512 — LOCK; manifest
|
||||||
field `anchor_prg_seed` (purpose-scoped, not implementation-scoped
|
field `anchor_prg_seed` (purpose-scoped, not implementation-scoped
|
||||||
— `phi_prg_seed` is an acceptable code-local alias only); seed
|
— `phi_prg_seed` is an acceptable code-local alias only); seed
|
||||||
size 32 bytes — LOCK; counter `uint32` big-endian from 0 — LOCK
|
size 32 bytes — LOCK; counter `uint32` **little-endian** from 0,
|
||||||
(flip to little-endian only if v7's TLV convention already is);
|
and the uint32-word interpretation is also little-endian — matching
|
||||||
float map `2·(u32/2^32)−1` kept for KAT compatibility but the
|
v7's canonical TLV integer encoding (verified against
|
||||||
prose must say "uniform over a 2^32-point grid in [-1, 1) with
|
`merkle-agi-dag_v7.txt` §A1: TLV length prefixes
|
||||||
|
`to_bytes(4, "little")`, `enc_int` is `to_bytes(8, "little")`,
|
||||||
|
quantized tensors are `<i8`). Per dav1d's review rule ("if v7 TLV
|
||||||
|
canonical integer encoding is little-endian, flip §3.4 to
|
||||||
|
little-endian before KAT freeze"). Float map `2·(u32_le/2^32)−1`;
|
||||||
|
prose says "uniform over a 2^32-point grid in [-1, 1) with
|
||||||
negligible finite-grid mean −2^−32", **not** "unbiased"; `SHALL`
|
negligible finite-grid mean −2^−32", **not** "unbiased"; `SHALL`
|
||||||
replace `embed_hard_to_vec` when M1 is enabled (whether M1 itself
|
replace `embed_hard_to_vec` when M1 is enabled (whether M1 itself
|
||||||
is mandatory is a separate mitigation-selection-policy question).
|
is mandatory is a separate mitigation-selection-policy question).
|
||||||
Plus an explicit `dim_h ≤ 16·2^32` exhaustion guard and a
|
Plus an explicit `dim_h ≤ 16·2^32` exhaustion guard and a
|
||||||
seed-independence requirement.
|
seed-independence requirement. `PHI_PRG_VERSION` carries the `-le`
|
||||||
|
suffix (`phi-prg-v1-hmac-sha512-le`); the 10 KAT vectors were
|
||||||
|
regenerated 2026-05-11 under the little-endian counter.
|
||||||
|
|
||||||
```
|
```
|
||||||
§ 9.10 Anchor PRG map φ_PRG
|
§ 9.10 Anchor PRG map φ_PRG
|
||||||
|
|
||||||
When M1 is enabled, the v7 anchor map φ_PRG(C(M), dim_h) is defined
|
When M1 is enabled, the v7 anchor map φ_PRG(C(M), dim_h) is defined
|
||||||
as follows.
|
as follows. All integers below are encoded little-endian, matching
|
||||||
|
v7's canonical TLV integer convention (§A1).
|
||||||
|
|
||||||
Let anchor_prg_seed be the v7 boot manifest's dedicated 32-byte PRG
|
Let anchor_prg_seed be the v7 boot manifest's dedicated 32-byte PRG
|
||||||
seed. The seed MUST be generated independently of the model
|
seed. The seed MUST be generated independently of the model
|
||||||
|
|
@ -220,11 +229,11 @@ before the corresponding anchor map is evaluated. anchor_prg_seed
|
||||||
is single-purpose — it MUST NOT be reused for any other PRG domain.
|
is single-purpose — it MUST NOT be reused for any other PRG domain.
|
||||||
|
|
||||||
Let C(M) be the fixed-length committed model digest. Let counter
|
Let C(M) be the fixed-length committed model digest. Let counter
|
||||||
range over unsigned 4-byte big-endian integers starting at 0.
|
range over unsigned 4-byte little-endian integers starting at 0.
|
||||||
|
|
||||||
For counter = 0, 1, 2, … compute:
|
For counter = 0, 1, 2, … compute:
|
||||||
|
|
||||||
block_counter = HMAC-SHA-512(anchor_prg_seed, C(M) ‖ counter_be32)
|
block_counter = HMAC-SHA-512(anchor_prg_seed, C(M) ‖ counter_le32)
|
||||||
|
|
||||||
Concatenate successive blocks until at least dim_h · 4 bytes are
|
Concatenate successive blocks until at least dim_h · 4 bytes are
|
||||||
available; truncate to exactly dim_h · 4 bytes. A 4-byte counter
|
available; truncate to exactly dim_h · 4 bytes. A 4-byte counter
|
||||||
|
|
@ -232,7 +241,7 @@ admits 2^32 HMAC-SHA-512 blocks of 64 bytes each, so dim_h MUST
|
||||||
satisfy dim_h ≤ 16 · 2^32; a request beyond that is an error.
|
satisfy dim_h ≤ 16 · 2^32; a request beyond that is an error.
|
||||||
|
|
||||||
Partition the byte stream into dim_h successive 4-byte words.
|
Partition the byte stream into dim_h successive 4-byte words.
|
||||||
Interpret each word as an unsigned big-endian integer u32. Map
|
Interpret each word as an unsigned little-endian integer u32. Map
|
||||||
each u32 to a float by:
|
each u32 to a float by:
|
||||||
|
|
||||||
x = 2 · (u32 / 2^32) − 1
|
x = 2 · (u32 / 2^32) − 1
|
||||||
|
|
@ -261,15 +270,16 @@ NO_ALIGNMENT and the deployment policy accepts the residual risk.
|
||||||
Once M1 is enabled, § 9.10 above applies in full.
|
Once M1 is enabled, § 9.10 above applies in full.
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note on counter endianness.** §3.4 currently fixes big-endian.
|
**Endianness — RESOLVED 2026-05-11.** v7's canonical integer
|
||||||
Before the §9.10 amendment freezes into the v7 spec, confirm v7's
|
byte-order was confirmed little-endian (`merkle-agi-dag_v7.txt`
|
||||||
canonical integer-byte-order convention: if v7 TLV uses
|
§A1 — every `to_bytes`/`astype` in the TLV encoding is little-
|
||||||
little-endian, flip §3.4 to match; if v7 has no established
|
endian; no big-endian anywhere). The §9.10 text above uses
|
||||||
convention, big-endian as specified here becomes the canonical
|
little-endian throughout to match. The reference implementation
|
||||||
choice for this PRG. The reference implementation
|
(`arborist/substrate/anchor_prg.py`), the 10 regenerated KAT
|
||||||
(`arborist/substrate/anchor_prg.py`) and the 10 KAT vectors are
|
vectors, and `test_anchor_prg.py` are all pinned to little-endian;
|
||||||
pinned to big-endian; a flip would require a `PHI_PRG_VERSION`
|
`PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512-le"`. (The earlier
|
||||||
bump + regenerated KATs.
|
big-endian draft + its KATs are superseded — a re-flip would need
|
||||||
|
another `PHI_PRG_VERSION` bump.)
|
||||||
|
|
||||||
## 4. Out of scope
|
## 4. Out of scope
|
||||||
|
|
||||||
|
|
@ -318,7 +328,7 @@ the moment v7 needs it.
|
||||||
- ``arborist/substrate/anchor_prg.py`` — ``phi_prg(hard_hash_32,
|
- ``arborist/substrate/anchor_prg.py`` — ``phi_prg(hard_hash_32,
|
||||||
dim_h, *, seed)`` per §3.1; HMAC-SHA-512 counter-mode KDF; pure
|
dim_h, *, seed)`` per §3.1; HMAC-SHA-512 counter-mode KDF; pure
|
||||||
stdlib (``hashlib`` + ``hmac``); no third-party dependency. Module
|
stdlib (``hashlib`` + ``hmac``); no third-party dependency. Module
|
||||||
also exports ``PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512"`` so
|
also exports ``PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512-le"`` so
|
||||||
future algorithm rotations can be detected at the call site
|
future algorithm rotations can be detected at the call site
|
||||||
without string-comparing module paths.
|
without string-comparing module paths.
|
||||||
|
|
||||||
|
|
@ -333,7 +343,7 @@ the moment v7 needs it.
|
||||||
from any version number. Acceptance-criteria §5 item 1 reads
|
from any version number. Acceptance-criteria §5 item 1 reads
|
||||||
through to "or equivalent location" so the move doesn't
|
through to "or equivalent location" so the move doesn't
|
||||||
invalidate the original criterion.
|
invalidate the original criterion.
|
||||||
- ``tests/test_anchor_prg.py`` — <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->30<!--/AUTOCOUNT--> tests covering determinism,
|
- ``tests/test_anchor_prg.py`` — <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT--> tests covering determinism,
|
||||||
range invariants, chi² loose-uniformity sanity, dim_h boundary
|
range invariants, chi² loose-uniformity sanity, dim_h boundary
|
||||||
(1, 16384), seed-bit-flip avalanche, hash-bit-flip avalanche,
|
(1, 16384), seed-bit-flip avalanche, hash-bit-flip avalanche,
|
||||||
hand-computed HMAC-SHA-512 first-block formula, prefix-extension
|
hand-computed HMAC-SHA-512 first-block formula, prefix-extension
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ because the prose IS the inside-of-tag content.
|
||||||
### 3.1 `tests`
|
### 3.1 `tests`
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
<!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->30<!--/AUTOCOUNT-->
|
<!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT-->
|
||||||
```
|
```
|
||||||
|
|
||||||
Live value: `pytest --collect-only -q <path>` count, with
|
Live value: `pytest --collect-only -q <path>` count, with
|
||||||
|
|
|
||||||
|
|
@ -589,7 +589,7 @@ than waiting for bench-time STRICT-rate drift to surface it.
|
||||||
|
|
||||||
### Substrate-paper-spec'd primitives (#000012 + #000018 + #000034 + #000037)
|
### Substrate-paper-spec'd primitives (#000012 + #000018 + #000034 + #000037)
|
||||||
|
|
||||||
- `tests/test_anchor_prg.py` — <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->30<!--/AUTOCOUNT--> tests for φ_PRG HMAC-SHA-512
|
- `tests/test_anchor_prg.py` — <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT--> tests for φ_PRG HMAC-SHA-512
|
||||||
expansion (#000035 Phase 1). Covers KAT regression, hand-formula
|
expansion (#000035 Phase 1). Covers KAT regression, hand-formula
|
||||||
(first-block matches direct ``hmac.new``), prefix-extending
|
(first-block matches direct ``hmac.new``), prefix-extending
|
||||||
closure invariant, output-length monotonicity per dim_h.
|
closure invariant, output-length monotonicity per dim_h.
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,21 @@ def test_bytes_to_floats_max_uint32_just_below_one():
|
||||||
|
|
||||||
|
|
||||||
def test_bytes_to_floats_midpoint_maps_to_zero():
|
def test_bytes_to_floats_midpoint_maps_to_zero():
|
||||||
# 0x80000000 → 2 * 0.5 - 1 = 0.0.
|
# u32 = 2^31 → 2 * 0.5 - 1 = 0.0. Words are read little-endian
|
||||||
assert _bytes_to_floats(b"\x80\x00\x00\x00") == [0.0]
|
# (matches v7's TLV convention), so the byte representation of
|
||||||
|
# 2^31 is b"\x00\x00\x00\x80", not b"\x80\x00\x00\x00".
|
||||||
|
assert int.from_bytes(b"\x00\x00\x00\x80", "little") == 1 << 31
|
||||||
|
assert _bytes_to_floats(b"\x00\x00\x00\x80") == [0.0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_bytes_to_floats_reads_little_endian():
|
||||||
|
"""Sanity-pin the endianness: 0x00000001 in little-endian byte
|
||||||
|
layout is b"\x01\x00\x00\x00" → u32=1 → 2*(1/2^32)-1 ≈ -1.0.
|
||||||
|
The big-endian reading (u32 = 0x01000000 = 2^24) would give a
|
||||||
|
very different float — this test catches an accidental flip back."""
|
||||||
|
out = _bytes_to_floats(b"\x01\x00\x00\x00")
|
||||||
|
assert out[0] == pytest.approx(2.0 * (1 / 2**32) - 1.0, abs=1e-12)
|
||||||
|
assert out[0] < -0.999999
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------- chi² sanity
|
# ----------------------------------------------------------- chi² sanity
|
||||||
|
|
@ -215,33 +228,37 @@ def test_phi_prg_output_is_prefix_extending():
|
||||||
|
|
||||||
def test_phi_prg_first_block_matches_direct_hmac():
|
def test_phi_prg_first_block_matches_direct_hmac():
|
||||||
"""First HMAC-SHA-512 block of phi_prg's output should match
|
"""First HMAC-SHA-512 block of phi_prg's output should match
|
||||||
a direct hmac.new(seed, hard_hash + b'\\x00\\x00\\x00\\x00',
|
a direct hmac.new(seed, hard_hash + counter_le_4, sha512).digest()
|
||||||
sha512).digest() invocation. Hand-computed against the
|
invocation. Hand-computed against the function's spec (#000035
|
||||||
function's spec (#000035 §3.1 + module §1).
|
§3.1/§3.4 + module §1). Counter and uint32-word reads are both
|
||||||
|
little-endian (matches v7's TLV convention; dav1d review
|
||||||
|
2026-05-11) — at counter=0 the bytes are b'\\x00\\x00\\x00\\x00'
|
||||||
|
regardless of endianness, but the float-word interpretation is
|
||||||
|
the part this test pins as little-endian.
|
||||||
|
|
||||||
Pattern from fox's `test_b1_exact_formula`: don't rely on
|
Pattern from fox's `test_b1_exact_formula`: don't rely on
|
||||||
KAT regression alone — compute the first-principles math in
|
KAT regression alone — compute the first-principles math in
|
||||||
the test file and assert exact agreement. Catches algorithm
|
the test file and assert exact agreement. Catches algorithm
|
||||||
drift that KAT regenerated against a buggy version would
|
drift that KAT regenerated against a buggy version would miss.
|
||||||
miss.
|
|
||||||
"""
|
"""
|
||||||
import hmac
|
import hmac
|
||||||
|
|
||||||
seed = hashlib.sha256(b"hand-formula-seed").digest()
|
seed = hashlib.sha256(b"hand-formula-seed").digest()
|
||||||
h = hashlib.sha256(b"hand-formula-hash").digest()
|
h = hashlib.sha256(b"hand-formula-hash").digest()
|
||||||
|
|
||||||
# Spec: out = HMAC-SHA-512(seed, hard_hash || counter_be_4) for
|
# Spec: out = HMAC-SHA-512(seed, hard_hash || counter_le_4) for
|
||||||
# counter = 0, 1, 2, ...; concatenated; truncated to dim_h * 4 bytes.
|
# counter = 0, 1, 2, ...; concatenated; truncated to dim_h * 4 bytes.
|
||||||
expected_block_0 = hmac.new(
|
expected_block_0 = hmac.new(
|
||||||
seed, h + (0).to_bytes(4, "big"), hashlib.sha512
|
seed, h + (0).to_bytes(4, "little"), hashlib.sha512
|
||||||
).digest()
|
).digest()
|
||||||
# First block is 64 bytes = 16 uint32s = 16 floats. dim_h=16
|
# First block is 64 bytes = 16 uint32s = 16 floats. dim_h=16
|
||||||
# consumes exactly the first block.
|
# consumes exactly the first block.
|
||||||
floats = phi_prg(h, dim_h=16, seed=seed)
|
floats = phi_prg(h, dim_h=16, seed=seed)
|
||||||
# Convert expected_block_0 to floats per §2.3 spec.
|
# Convert expected_block_0 to floats per §3.4 spec: uint32 read
|
||||||
|
# little-endian, then 2*(u32/2^32)-1.
|
||||||
expected_floats = []
|
expected_floats = []
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
u32 = int.from_bytes(expected_block_0[4 * i : 4 * (i + 1)], "big")
|
u32 = int.from_bytes(expected_block_0[4 * i : 4 * (i + 1)], "little")
|
||||||
expected_floats.append(2.0 * (u32 / 2 ** 32) - 1.0)
|
expected_floats.append(2.0 * (u32 / 2 ** 32) - 1.0)
|
||||||
for j, (got, exp) in enumerate(zip(floats, expected_floats)):
|
for j, (got, exp) in enumerate(zip(floats, expected_floats)):
|
||||||
assert got == pytest.approx(exp, abs=1e-12), (
|
assert got == pytest.approx(exp, abs=1e-12), (
|
||||||
|
|
@ -332,12 +349,16 @@ def test_phi_prg_rejects_dim_h_above_counter_ceiling():
|
||||||
|
|
||||||
|
|
||||||
def test_module_exports_version_string():
|
def test_module_exports_version_string():
|
||||||
assert PHI_PRG_VERSION == "phi-prg-v1-hmac-sha512"
|
# The "-le" suffix records the 2026-05-11 big-endian → little-endian
|
||||||
|
# counter flip (dav1d review; matches v7's TLV convention). A future
|
||||||
|
# endianness or formula change MUST bump this token + regen the KATs.
|
||||||
|
assert PHI_PRG_VERSION == "phi-prg-v1-hmac-sha512-le"
|
||||||
# versioned-default discipline (calculator-test-patterns.md §2):
|
# versioned-default discipline (calculator-test-patterns.md §2):
|
||||||
# "v1" substring present so future major-version rotations
|
# "v1" substring present so future major-version rotations
|
||||||
# (v2-blake3-expansion etc.) are detectable at the call site
|
# (v2-blake3-expansion etc.) are detectable at the call site
|
||||||
# without string-comparing module paths.
|
# without string-comparing module paths.
|
||||||
assert "v1" in PHI_PRG_VERSION
|
assert "v1" in PHI_PRG_VERSION
|
||||||
|
assert PHI_PRG_VERSION.endswith("-le")
|
||||||
|
|
||||||
|
|
||||||
def test_placeholder_seed_is_32_bytes():
|
def test_placeholder_seed_is_32_bytes():
|
||||||
|
|
@ -391,6 +412,14 @@ def test_phi_prg_known_answer_tests():
|
||||||
f"KAT mismatch for label={kat.get('label')!r}: "
|
f"KAT mismatch for label={kat.get('label')!r}: "
|
||||||
f"expected {kat['output_sha256']}, got {digest}"
|
f"expected {kat['output_sha256']}, got {digest}"
|
||||||
)
|
)
|
||||||
|
# Version pin (present in 2026-05-11+ fixtures): a KAT recorded
|
||||||
|
# under a different PHI_PRG_VERSION must not be silently replayed
|
||||||
|
# against the current algorithm.
|
||||||
|
if "version" in kat:
|
||||||
|
assert kat["version"] == PHI_PRG_VERSION, (
|
||||||
|
f"KAT version mismatch for label={kat.get('label')!r}: "
|
||||||
|
f"fixture {kat['version']}, module {PHI_PRG_VERSION}"
|
||||||
|
)
|
||||||
# Also assert the float list has the expected length so a
|
# Also assert the float list has the expected length so a
|
||||||
# bug in _bytes_to_floats truncation is caught.
|
# bug in _bytes_to_floats truncation is caught.
|
||||||
assert len(out_floats) == dim_h
|
assert len(out_floats) == dim_h
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue