diff --git a/README.md b/README.md index 292e056..107c249 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ The selected architecture-neutral set is exactly twenty raw assets `bls_midnight K1–K19 use `srsGeneration=midnight-trusted-setup@3ea610263b228af24840f7b00661ee22360db6d8`; official names `midnight-srs-2p{k}` map explicitly to cache names `bls_midnight_2p{k}`. K0 has no ceremony catalog row and truthfully uses provider compatibility provenance `midnight-ledger-provider-compat@7a89f45d29792be7e09ca5eb246f1e69f0b2a179/sha256:59b30b…`. All raw files install `0644` under their literal cache names. -The Ledger archive restores only twelve `zswap/9/*` and `dust/9/*` files at `0644`. Its identity is `ledgerStaticSemver=9.0.0`, `cacheNamespace=9`, and full `memberManifestSha256`; it is not versioned by proof-server RC. Exact rc.5 source plus the two pinned OCI digests accept static-9. Exact source `cd652d7…`/static-10 and its architecture-specific images reject static-9 while reusing the unchanged SRS. +The Ledger archive restores only twelve `zswap/9/*` and `dust/9/*` files at `0644`. Its semantic identity is the canonical, file-only, path-sorted `ledger-static-member-manifest-v1` projection (`path`, `bytes`, `sha256`, `mode`) and full `memberManifestSha256`; deterministic ZIP directory/type/order evidence is retained separately as the ZIP-layout manifest and never changes the semantic digest algorithm. Its version identity is `ledgerStaticSemver=9.0.0` plus `cacheNamespace=9`, not a proof-server RC. Exact rc.5 source plus the two pinned OCI digests accept static-9. Exact source `cd652d7…`/static-10 and its architecture-specific images reject static-9 while reusing the unchanged SRS. Append-only correction rules: diff --git a/metadata/contracts/ledger-static-9-member-manifest-v1.json b/metadata/contracts/ledger-static-9-member-manifest-v1.json new file mode 100644 index 0000000..d440e81 --- /dev/null +++ b/metadata/contracts/ledger-static-9-member-manifest-v1.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": "ledger-static-member-manifest-v1", + "members": [ + {"path": "dust/9/spend.bzkir", "bytes": 2555, "sha256": "904181287e75b0fb596ba5fcc116c882ee5d28e3115304c93ebd913722ce5841", "mode": "0644"}, + {"path": "dust/9/spend.prover", "bytes": 2175671, "sha256": "996602da7ca386284e656c78ea03e55bffdba29475e6a67965c50de05e13efc2", "mode": "0644"}, + {"path": "dust/9/spend.verifier", "bytes": 1351, "sha256": "3f1569ebcab0655c5c145b28947c74edc4e3f5c6b276e4404b661cf0905b49d3", "mode": "0644"}, + {"path": "zswap/9/output.bzkir", "bytes": 494, "sha256": "91dc8b401dd8385e8d29eaac018c70b578505f48c7952452ef319bc397fa1f1b", "mode": "0644"}, + {"path": "zswap/9/output.prover", "bytes": 5730182, "sha256": "d992b04f13c3fd432f55fb8bfe6466d87bc181f1a2acf233ec228030bbdd4ed8", "mode": "0644"}, + {"path": "zswap/9/output.verifier", "bytes": 2311, "sha256": "72e8074856f2f5c504ade25a86a2b8902c64aeb9497c4c8e6b26dea842a0ab08", "mode": "0644"}, + {"path": "zswap/9/sign.bzkir", "bytes": 114, "sha256": "37ea2094516e145a738126307cf92bd293f7cb524b1ccd49fa6f3225a9ec3a50", "mode": "0644"}, + {"path": "zswap/9/sign.prover", "bytes": 2814823, "sha256": "fe7268dd2bdd107f862f881ac3c5bc71a6df77ce80bfed51cf71514648e660e0", "mode": "0644"}, + {"path": "zswap/9/sign.verifier", "bytes": 2023, "sha256": "e39a727caa0de167e6dd6122a9e3b758fecf48f9093ab77c0309648de8ce07e1", "mode": "0644"}, + {"path": "zswap/9/spend.bzkir", "bytes": 1294, "sha256": "7cb5bbcf67cb212a3336fb439a77e8f32f0aa8a56185c8e1247d6cbfc7300205", "mode": "0644"}, + {"path": "zswap/9/spend.prover", "bytes": 11020001, "sha256": "19d234b5c68b7212ad6b0ec9334a95594748154128f3704eb576bcc843cc5c45", "mode": "0644"}, + {"path": "zswap/9/spend.verifier", "bytes": 2311, "sha256": "544554effd7ae9fb9063be52a9ec2a986756301071fcd97bb4598fb45a335658", "mode": "0644"} + ] +} diff --git a/metadata/contracts/proof-data-q8b-v1.json b/metadata/contracts/proof-data-q8b-v1.json index b2ed681..723a448 100644 --- a/metadata/contracts/proof-data-q8b-v1.json +++ b/metadata/contracts/proof-data-q8b-v1.json @@ -37,8 +37,12 @@ "assetName": "midnight-ledger-static-noarch-9.0.0.zip", "ledgerStaticSemver": "9.0.0", "cacheNamespace": "9", - "memberManifestSha256": null, - "memberManifestStatus": "pending-phase-3p-deterministic-assembly", + "memberManifestSha256": "9ba79d1d49d10465f46db247ffe5e4ae3f779ad06f07d1869169a427a907ac0c", + "memberManifestStatus": "reviewed-phase-3p-file-only-semantic-projection", + "memberManifestPath": "metadata/contracts/ledger-static-9-member-manifest-v1.json", + "zipLayoutManifestSha256": "c234479391542c2440e9f4ded1bb7c601365ef433fecb161d80311bc2bcfa2d7", + "archiveBytes": 21601265, + "archiveSha256": "d7e8ccfdbc55a2b7139aadd4797d665f888a4502b63ebae24d23314eeee341b2", "mode": "0644", "members": [ {"path": "zswap/9/spend.prover", "bytes": 11020001, "sha256": "19d234b5c68b7212ad6b0ec9334a95594748154128f3704eb576bcc843cc5c45"}, diff --git a/scripts/warehouse_lib.py b/scripts/warehouse_lib.py index 2c83441..4942ed0 100755 --- a/scripts/warehouse_lib.py +++ b/scripts/warehouse_lib.py @@ -697,6 +697,25 @@ def ledger_member_manifest(members: list[dict[str, Any]]) -> str: return canonical_sha256({"schemaVersion": "ledger-static-member-manifest-v1", "members": rows}) +def validate_q8b_contract_identity(contract: dict[str, Any]) -> None: + ledger = contract["ledgerStatic"] + path = ROOT / ledger["memberManifestPath"] + expect(path.is_file() and path.resolve().is_relative_to(ROOT.resolve()), "Q8B semantic member manifest path is missing or escapes the repository") + semantic = load_json(path) + expected = { + "schemaVersion": "ledger-static-member-manifest-v1", + "members": [ + {"path": row["path"], "bytes": row["bytes"], "sha256": row["sha256"], "mode": ledger["mode"]} + for row in sorted(ledger["members"], key=lambda item: item["path"]) + ], + } + expect(semantic == expected, "Q8B semantic member manifest differs from the exact twelve contract rows") + expect(canonical_sha256(semantic) == ledger["memberManifestSha256"], "Q8B semantic member-manifest digest drift") + expect(ledger["memberManifestStatus"] == "reviewed-phase-3p-file-only-semantic-projection", "Q8B member manifest is not in the reviewed Phase-3p state") + expect(re.fullmatch(r"[0-9a-f]{64}", ledger["zipLayoutManifestSha256"]) is not None, "Q8B ZIP-layout manifest digest is invalid") + expect(isinstance(ledger["archiveBytes"], int) and ledger["archiveBytes"] > 0 and re.fullmatch(r"[0-9a-f]{64}", ledger["archiveSha256"]) is not None, "Q8B deterministic archive identity is invalid") + + def validate_archive_invariants(entry: dict[str, Any]) -> None: asset = entry["asset"] archive = entry["archive"] @@ -1014,6 +1033,7 @@ def validate_catalog( expect(catalog.get("repository") == {"fullName": REPOSITORY, "id": REPOSITORY_ID, "nodeId": REPOSITORY_NODE_ID}, "catalog repository identity mismatch") expect(catalog.get("release") == {"tag": RELEASE_TAG, "id": RELEASE_ID, "nodeId": RELEASE_NODE_ID, "url": RELEASE_URL, "mutable": True}, "catalog release identity mismatch") proof_contract = proof_contract_override or load_json(ROOT / "metadata/contracts/proof-data-q8b-v1.json") + validate_q8b_contract_identity(proof_contract) family_contracts = family_contracts_override or load_json(ROOT / "metadata/contracts/families-v1.json") software_families = {row["family"]: row for row in family_contracts["softwareFamilies"]} seen_ids: set[str] = set() diff --git a/tests/test_proof_contract.py b/tests/test_proof_contract.py index 097c122..9914a4d 100644 --- a/tests/test_proof_contract.py +++ b/tests/test_proof_contract.py @@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) -from warehouse_lib import WarehouseError, ledger_member_manifest, load_json, resolve_catalog, validate_catalog # noqa: E402 +from warehouse_lib import WarehouseError, canonical_sha256, ledger_member_manifest, load_json, resolve_catalog, validate_catalog # noqa: E402 CONTRACT = load_json(ROOT / "metadata/contracts/proof-data-q8b-v1.json") @@ -60,8 +60,8 @@ def proof_entry(*, k: int | None = None, semver: str | None = None, name: str | "evidenceSha256": "f" * 64, } semantic = f"ledger-static/{semver}/{manifest}" - digest = (correction_seed or "c") * 64 - size = 1 + digest = correction_seed * 64 if correction_seed else CONTRACT["ledgerStatic"]["archiveSha256"] + size = 1 if correction_seed else CONTRACT["ledgerStatic"]["archiveBytes"] source_repo = "midnightntwrk/midnight-ledger" source_commit = CONTRACT["srs"]["providerCommit"] return { @@ -113,6 +113,15 @@ def test_exact_q8b_inventory(self) -> None: self.assertIsNone(objects[0]["officialAlias"]) self.assertTrue(all(row["officialAlias"] == f"midnight-srs-2p{row['k']}" for row in objects[1:])) self.assertFalse(contract["exactCompatibility"]["static10Negative"]["static9Accepted"]) + semantic = load_json(ROOT / contract["ledgerStatic"]["memberManifestPath"]) + self.assertEqual(canonical_sha256(semantic), contract["ledgerStatic"]["memberManifestSha256"]) + projected_members = [ + {"path": row["path"], "size": row["bytes"], "sha256": row["sha256"], "installMode": contract["ledgerStatic"]["mode"]} + for row in contract["ledgerStatic"]["members"] + ] + self.assertEqual(ledger_member_manifest(projected_members), contract["ledgerStatic"]["memberManifestSha256"]) + self.assertEqual(contract["ledgerStatic"]["archiveSha256"], "d7e8ccfdbc55a2b7139aadd4797d665f888a4502b63ebae24d23314eeee341b2") + self.assertEqual(contract["ledgerStatic"]["archiveBytes"], 21601265) def test_generation_and_static_revision_resolution(self) -> None: base = load_json(ROOT / "metadata/releases/0.3.120.json") @@ -130,11 +139,12 @@ def test_generation_and_static_revision_resolution(self) -> None: result = resolve_catalog(base, family=None, version=None, os_name=None, arch=None, variant=None, k=5, srs_generation="sha256:" + "d" * 64, ledger_static=None, member_manifest=None) self.assertEqual(result["assetName"], generation_2["asset"]["name"]) - blocked_normal = proof_entry(semver="9.0.0") - blocked_catalog = load_json(ROOT / "metadata/releases/0.3.120.json") - blocked_catalog["entries"].append(blocked_normal) - with self.assertRaisesRegex(WarehouseError, "blocked until Phase-3p"): - validate_catalog(blocked_catalog, ROOT / "metadata/schema/artifact-catalog-v1.schema.json") + normal = proof_entry(semver="9.0.0") + normal_catalog = load_json(ROOT / "metadata/releases/0.3.120.json") + normal_catalog["entries"].append(normal) + validate_catalog(normal_catalog, ROOT / "metadata/schema/artifact-catalog-v1.schema.json") + self.assertEqual(normal["proofData"]["memberManifestSha256"], CONTRACT["ledgerStatic"]["memberManifestSha256"]) + self.assertEqual(normal["asset"]["sha256"], CONTRACT["ledgerStatic"]["archiveSha256"]) first = proof_entry(semver="9.0.0", correction_seed="a") second = proof_entry(semver="9.0.0", correction_seed="b")