Files
neuron-tai/tests/test_runtime_pin_identity.py
Dobromir Popov 03e97ca31a fix: bind recipe identity to certified artifact bytes (DGR-025)
Append +artifact.<sha256> to the llama.cpp runtime axis, computed from the
exact bytes read by attest_loaded_runtime, so a differently-built shared
object with copied lock values can no longer forge a certified runtime
identity. Node/tracker parsers require the suffix; new test proves a
byte-identical-lock but different-binary artifact produces a different
recipe fingerprint. Regenerates conformance vectors accordingly.

105 passed in tests/test_native_identity_emission.py,
tests/test_runtime_pin_identity.py, tests/test_runtime_recipe_identity.py.
2026-07-21 13:22:02 +03:00

385 lines
13 KiB
Python

"""DGR-025: the recipe's runtime axis commits to the exact pin and patch stack.
The fingerprint (DGR-003 lineage) digests a ``runtime_version`` string, but a
string an operator typed is a label, not a pin: two workers could run different
patch stacks under the same label and still agree on the digest. These tests
pin the axis to the DGR-027 lock manifest — the exact upstream commit plus a
digest over the ordered patch-stack bytes — and prove both identity
implementations reject a moving runtime reference.
"""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
import pytest
from meshnet_node.runtime_pin import (
DEFAULT_LOCK_DIR,
RuntimePinError,
load_runtime_pin,
)
from meshnet_node.runtime_recipe import (
RecipeIdentityError,
RuntimeRecipe,
tokenizer_identity,
)
from meshnet_tracker.recipe import (
RecipeIdentityError as TrackerRecipeIdentityError,
parse_identity,
tokenizer_identity as tracker_tokenizer_identity,
)
REPO_LOCK_DIR = (
Path(__file__).resolve().parent.parent / "packages" / "node" / "native" / "llama"
)
TOKENIZER = tokenizer_identity({"tokenizer.json": b'{"vocab":{"a":0}}\n'})
def _recipe(**changes: object) -> RuntimeRecipe:
fields: dict[str, object] = {
"weight_quantization": "Q4_K_M",
"activation_dtype": "bfloat16",
"compute_dtype": "float32",
"kv_dtype": "q8_0",
"kv_layout": "paged-v1",
"tokenizer_revision": TOKENIZER,
"architecture_adapter": "llama/range-v1",
"backend_id": "llama.cpp",
"runtime_version": (
"llama.cpp@" + "d" * 40 + "+patchstack." + "e" * 64
+ "+build." + "f" * 64
+ "+artifact." + "a" * 64
),
"recipe_id": "example-gguf",
"recipe_version": "1",
"catalogue_version": "2026.07.1",
}
fields.update(changes)
return RuntimeRecipe(**fields) # type: ignore[arg-type]
def _write_fixture_workspace(
root: Path,
*,
commit: str = "e" * 40,
patches: dict[str, bytes] | None = None,
lock_series: list[str] | None = None,
series_lines: list[str] | None = None,
sums_lines: list[str] | None = None,
schema_version: int = 1,
upstream: str = "https://github.com/ggml-org/llama.cpp.git",
upstream_commit_file: str | None = None,
patched_tree: str = "a" * 40,
build: dict[str, object] | None = None,
) -> Path:
"""A minimal DGR-027-shaped lock workspace; overrides create disagreement."""
if patches is None:
patches = {
"0001-first.patch": b"--- a\n+++ b\n",
"0002-second.patch": b"--- c\n+++ d\n",
}
names = list(patches)
digests = {
name: hashlib.sha256(body).hexdigest() for name, body in patches.items()
}
patch_dir = root / "patches"
patch_dir.mkdir(parents=True)
for name, body in patches.items():
(patch_dir / name).write_bytes(body)
(root / "UPSTREAM_LOCK.json").write_text(
json.dumps(
{
"schema_version": schema_version,
"upstream": upstream,
"commit": commit,
"patched_tree": patched_tree,
"build": build or {"configure_flags": ["-DTEST=ON"]},
"patch_series": names if lock_series is None else lock_series,
}
),
encoding="utf-8",
)
(root / "UPSTREAM_COMMIT").write_text(
(commit if upstream_commit_file is None else upstream_commit_file) + "\n",
encoding="utf-8",
)
(patch_dir / "series").write_text(
"\n".join(names if series_lines is None else series_lines) + "\n",
encoding="utf-8",
)
if sums_lines is None:
sums_lines = ["# ordered digests"] + [
f"{digests[name]} {name}" for name in names
]
(patch_dir / "SHA256SUMS").write_text(
"\n".join(sums_lines) + "\n", encoding="utf-8"
)
return root
# --- the committed manifest is the identity source -------------------------
def test_default_lock_dir_is_the_committed_manifest():
assert DEFAULT_LOCK_DIR == REPO_LOCK_DIR
def test_committed_manifest_derives_a_deterministic_runtime_pin():
pin = load_runtime_pin(REPO_LOCK_DIR)
again = load_runtime_pin(REPO_LOCK_DIR)
assert pin == again
lock = json.loads(
(REPO_LOCK_DIR / "UPSTREAM_LOCK.json").read_text(encoding="utf-8")
)
assert pin.upstream_commit == lock["commit"]
assert list(pin.patch_series) == lock["patch_series"]
# The axis value names the runtime, exact commit, stack, and build recipe.
assert pin.runtime_version == (
f"llama.cpp@{lock['commit']}+patchstack.{pin.patch_stack_digest}"
f"+build.{pin.build_recipe_digest}"
)
assert len(pin.patch_stack_digest) == 64
def test_derived_axis_value_is_a_valid_recipe_pin():
pin = load_runtime_pin(REPO_LOCK_DIR)
runtime_version = pin.runtime_version + "+artifact." + "a" * 64
recipe = _recipe(runtime_version=runtime_version)
assert recipe.runtime_version == runtime_version
assert len(recipe.runtime_recipe_digest) == 64
def test_patch_byte_change_changes_the_runtime_identity(tmp_path):
baseline = load_runtime_pin(_write_fixture_workspace(tmp_path / "a"))
changed = load_runtime_pin(
_write_fixture_workspace(
tmp_path / "b",
patches={
"0001-first.patch": b"--- a\n+++ b\n",
"0002-second.patch": b"--- c\n+++ DIFFERENT\n",
},
)
)
assert baseline.patch_stack_digest != changed.patch_stack_digest
assert baseline.runtime_version != changed.runtime_version
def test_patch_order_is_part_of_the_stack_identity(tmp_path):
patches = {
"0001-first.patch": b"--- a\n+++ b\n",
"0002-second.patch": b"--- c\n+++ d\n",
}
forward = load_runtime_pin(
_write_fixture_workspace(tmp_path / "a", patches=patches)
)
names = list(patches)
reversed_names = list(reversed(names))
digests = {
name: hashlib.sha256(body).hexdigest() for name, body in patches.items()
}
swapped = load_runtime_pin(
_write_fixture_workspace(
tmp_path / "b",
patches=patches,
lock_series=reversed_names,
series_lines=reversed_names,
sums_lines=[f"{digests[name]} {name}" for name in reversed_names],
)
)
assert forward.patch_stack_digest != swapped.patch_stack_digest
def test_build_recipe_change_changes_runtime_identity(tmp_path):
baseline = load_runtime_pin(_write_fixture_workspace(tmp_path / "a"))
changed = load_runtime_pin(
_write_fixture_workspace(
tmp_path / "b", build={"configure_flags": ["-DTEST=OFF"]}
)
)
assert baseline.runtime_version != changed.runtime_version
# --- every manifest disagreement fails closed ------------------------------
def test_missing_lock_file_fails_closed(tmp_path):
with pytest.raises(RuntimePinError, match="UPSTREAM_LOCK.json"):
load_runtime_pin(tmp_path)
def test_malformed_lock_json_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path)
(tmp_path / "UPSTREAM_LOCK.json").write_text("{not json", encoding="utf-8")
with pytest.raises(RuntimePinError, match="not valid JSON"):
load_runtime_pin(tmp_path)
def test_unknown_lock_schema_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path, schema_version=2)
with pytest.raises(RuntimePinError, match="schema"):
load_runtime_pin(tmp_path)
def test_moving_commit_reference_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path, commit="master")
with pytest.raises(RuntimePinError, match="exact 40"):
load_runtime_pin(tmp_path)
def test_upstream_commit_file_disagreement_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path, upstream_commit_file="f" * 40)
with pytest.raises(RuntimePinError, match="UPSTREAM_COMMIT"):
load_runtime_pin(tmp_path)
def test_series_file_disagreement_fails_closed(tmp_path):
_write_fixture_workspace(
tmp_path, series_lines=["0002-second.patch", "0001-first.patch"]
)
with pytest.raises(RuntimePinError, match="series"):
load_runtime_pin(tmp_path)
def test_missing_patch_file_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path)
(tmp_path / "patches" / "0002-second.patch").unlink()
with pytest.raises(RuntimePinError, match="0002-second.patch"):
load_runtime_pin(tmp_path)
def test_checksum_disagreement_fails_closed(tmp_path):
_write_fixture_workspace(tmp_path)
patch = tmp_path / "patches" / "0002-second.patch"
patch.write_bytes(patch.read_bytes() + b"tampered\n")
with pytest.raises(RuntimePinError, match="SHA256SUMS"):
load_runtime_pin(tmp_path)
def test_sums_entry_missing_fails_closed(tmp_path):
patches = {
"0001-first.patch": b"--- a\n+++ b\n",
"0002-second.patch": b"--- c\n+++ d\n",
}
digest = hashlib.sha256(patches["0001-first.patch"]).hexdigest()
_write_fixture_workspace(
tmp_path,
patches=patches,
sums_lines=[f"{digest} 0001-first.patch"],
)
with pytest.raises(RuntimePinError, match="SHA256SUMS"):
load_runtime_pin(tmp_path)
def test_empty_patch_series_requires_empty_series_files(tmp_path):
# An unpatched runtime is a legal pin; a lock that *hides* patches is not.
_write_fixture_workspace(tmp_path, lock_series=[])
with pytest.raises(RuntimePinError, match="series"):
load_runtime_pin(tmp_path)
# --- tokenizer identities are bytes, never labels -------------------------
def _vector_identity() -> dict[str, object]:
vectors = json.loads(
(Path(__file__).parent / "data" / "recipe_fingerprint_vectors.json").read_text(
encoding="utf-8"
)
)
return json.loads(json.dumps(vectors["vectors"][0]["identity"]))
@pytest.mark.parametrize("moving", ["origin/main", "stable", "release", "v1", "HEAD"])
def test_node_rejects_every_label_as_a_tokenizer_identity(moving):
with pytest.raises(RecipeIdentityError, match="content-addressed tokenizer"):
_recipe(tokenizer_revision=moving)
@pytest.mark.parametrize("moving", ["origin/main", "stable", "release", "v1", "HEAD"])
def test_tracker_rejects_every_label_as_a_tokenizer_identity(moving):
doc = _vector_identity()
doc["recipe"]["tokenizer_revision"] = moving # type: ignore[index]
doc.pop("fingerprint", None)
with pytest.raises(TrackerRecipeIdentityError, match="content-addressed"):
parse_identity(doc)
def test_node_and_tracker_independently_derive_identical_tokenizer_bytes_identity():
files = {
"tokenizer.json": b'{"vocab":{"a":0}}\n',
"tokenizer_config.json": b'{"bos_token":"<s>"}\n',
}
assert tokenizer_identity(files) == tracker_tokenizer_identity(files)
def test_tokenizer_bytes_under_one_label_and_one_byte_change_get_new_recipe_fingerprints():
label = "stable"
first = {"tokenizer.json": b'{"vocab":{"a":0}}\n'}
second = {"tokenizer.json": b'{"vocab":{"a":1}}\n'}
first_id = tokenizer_identity(first)
second_id = tokenizer_identity(second)
assert first_id != second_id, label
assert _recipe(tokenizer_revision=first_id).runtime_recipe_digest != _recipe(
tokenizer_revision=second_id
).runtime_recipe_digest
# --- both identity implementations reject a moving runtime -----------------
def test_node_recipe_rejects_a_moving_runtime_version():
with pytest.raises(RecipeIdentityError, match="moving reference"):
_recipe(runtime_version="latest")
def test_tracker_rejects_a_moving_runtime_version():
vectors = json.loads(
(Path(__file__).parent / "data" / "recipe_fingerprint_vectors.json").read_text(
encoding="utf-8"
)
)
doc = json.loads(json.dumps(vectors["vectors"][0]["identity"]))
doc["recipe"]["runtime_version"] = "latest"
doc.pop("fingerprint", None)
with pytest.raises(TrackerRecipeIdentityError, match="moving reference"):
parse_identity(doc)
@pytest.mark.parametrize(
"forged",
[
"llama.cpp@master+patchstack.not-a-digest",
"llama.cpp@e920c523+patchstack.forged",
"release-that-operator-typed",
],
)
def test_node_recipe_rejects_noncanonical_llama_runtime_pins(forged):
with pytest.raises(RecipeIdentityError, match="40-hex commit"):
_recipe(runtime_version=forged)
@pytest.mark.parametrize(
"forged",
[
"llama.cpp@master+patchstack.not-a-digest",
"llama.cpp@e920c523+patchstack.forged",
"release-that-operator-typed",
],
)
def test_tracker_rejects_noncanonical_llama_runtime_pins(forged):
vectors = json.loads(
(Path(__file__).parent / "data" / "recipe_fingerprint_vectors.json").read_text(
encoding="utf-8"
)
)
doc = json.loads(json.dumps(vectors["vectors"][0]["identity"]))
doc["recipe"]["runtime_version"] = forged
doc.pop("fingerprint", None)
with pytest.raises(TrackerRecipeIdentityError, match="40-hex commit"):
parse_identity(doc)