아바타 저장소 승격 계약을 완성
This commit is contained in:
parent
ac9b702688
commit
ccdcfcd2f5
36 changed files with 14734 additions and 222 deletions
183
scripts/validate-public-runtime-offline-quiescence.py
Normal file
183
scripts/validate-public-runtime-offline-quiescence.py
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Validate a privacy-safe legacy bootstrap quiescence receipt."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
INITIALIZER = SCRIPT_DIR / "initialize-public-runtime-upload-root.py"
|
||||
|
||||
|
||||
async def _current_database_target_sha256() -> str:
|
||||
import asyncpg
|
||||
|
||||
from app.config import settings
|
||||
from public_runtime_database_identity import connected_database_target_sha256
|
||||
|
||||
connection = await asyncpg.connect(
|
||||
settings.database_url, command_timeout=settings.db_command_timeout
|
||||
)
|
||||
try:
|
||||
async with connection.transaction(isolation="repeatable_read", readonly=True):
|
||||
return await connected_database_target_sha256(connection)
|
||||
finally:
|
||||
await connection.close()
|
||||
|
||||
|
||||
def _load_initializer():
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"vignette_upload_initializer_contract", INITIALIZER
|
||||
)
|
||||
if spec is None or spec.loader is None:
|
||||
raise RuntimeError("offline_quiescence_validator_unavailable")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--receipt-path", required=True)
|
||||
parser.add_argument("--expected-receipt-sha256", required=True)
|
||||
parser.add_argument("--manifest-path", required=True)
|
||||
parser.add_argument("--expected-manifest-sha256", required=True)
|
||||
parser.add_argument("--stable-source-root", required=True)
|
||||
parser.add_argument("--upload-root", required=True)
|
||||
parser.add_argument("--expected-legacy-source-commit", required=True)
|
||||
parser.add_argument("--expected-legacy-source-tree", required=True)
|
||||
args = parser.parse_args()
|
||||
module = _load_initializer()
|
||||
try:
|
||||
manifest_path = module._validated_regular_file(Path(args.manifest_path))
|
||||
if module.sha256_file(manifest_path) != args.expected_manifest_sha256:
|
||||
raise module.InitializationError("manifest_hash_drift")
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
offline = manifest.get("offline_quiescence") if isinstance(manifest, dict) else None
|
||||
if not isinstance(offline, dict) or set(offline) != {
|
||||
"receipt_sha256",
|
||||
"database_target_sha256",
|
||||
"source_root_sha256s",
|
||||
"source_root_set_sha256",
|
||||
"preserved_object_count",
|
||||
"preserved_total_size_bytes",
|
||||
"preserved_inventory_sha256",
|
||||
"preserved_decode_valid_count",
|
||||
"preserved_decode_invalid_count",
|
||||
"required_decode_invalid_object_count",
|
||||
"required_decode_invalid_reference_count",
|
||||
"reference_count",
|
||||
"unique_object_count",
|
||||
"reference_set_sha256",
|
||||
}:
|
||||
raise module.InitializationError("manifest_offline_quiescence_missing")
|
||||
if offline.get("receipt_sha256") != args.expected_receipt_sha256:
|
||||
raise module.InitializationError("manifest_quiescence_receipt_drift")
|
||||
proof = module.validate_offline_quiescence_receipt(
|
||||
receipt_path=Path(args.receipt_path),
|
||||
expected_receipt_sha256=args.expected_receipt_sha256,
|
||||
stable_source_root=Path(args.stable_source_root),
|
||||
upload_root=Path(args.upload_root),
|
||||
expected_source_commit=args.expected_legacy_source_commit,
|
||||
expected_source_tree=args.expected_legacy_source_tree,
|
||||
expected_source_roots=None,
|
||||
expected_source_root_sha256s=offline.get("source_root_sha256s"),
|
||||
expected_source_root_set_sha256=offline.get(
|
||||
"source_root_set_sha256"
|
||||
),
|
||||
expected_database_target_sha256=asyncio.run(
|
||||
_current_database_target_sha256()
|
||||
),
|
||||
expected_preserved_object_count=offline.get(
|
||||
"preserved_object_count"
|
||||
),
|
||||
expected_preserved_total_size_bytes=offline.get(
|
||||
"preserved_total_size_bytes"
|
||||
),
|
||||
expected_preserved_inventory_sha256=offline.get(
|
||||
"preserved_inventory_sha256"
|
||||
),
|
||||
expected_preserved_decode_valid_count=offline.get(
|
||||
"preserved_decode_valid_count"
|
||||
),
|
||||
expected_preserved_decode_invalid_count=offline.get(
|
||||
"preserved_decode_invalid_count"
|
||||
),
|
||||
expected_required_decode_invalid_object_count=offline.get(
|
||||
"required_decode_invalid_object_count"
|
||||
),
|
||||
expected_required_decode_invalid_reference_count=offline.get(
|
||||
"required_decode_invalid_reference_count"
|
||||
),
|
||||
expected_reference_count=offline.get("reference_count"),
|
||||
expected_unique_object_count=offline.get("unique_object_count"),
|
||||
expected_reference_set_sha256=offline.get("reference_set_sha256"),
|
||||
)
|
||||
if list(proof.source_root_sha256s) != offline.get(
|
||||
"source_root_sha256s"
|
||||
) or proof.source_root_set_sha256 != offline.get(
|
||||
"source_root_set_sha256"
|
||||
):
|
||||
raise module.InitializationError("manifest_legacy_source_root_drift")
|
||||
except (OSError, ValueError, module.InitializationError):
|
||||
print(
|
||||
json.dumps(
|
||||
{"status": "failed", "reason": "offline_quiescence_contract_failed"},
|
||||
ensure_ascii=True,
|
||||
separators=(",", ":"),
|
||||
)
|
||||
)
|
||||
return 1
|
||||
except Exception:
|
||||
print(
|
||||
json.dumps(
|
||||
{"status": "failed", "reason": "offline_quiescence_validation_unavailable"},
|
||||
ensure_ascii=True,
|
||||
separators=(",", ":"),
|
||||
)
|
||||
)
|
||||
return 1
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"status": "passed",
|
||||
"receipt_sha256": proof.receipt_sha256,
|
||||
"database_target_sha256": proof.database_target_sha256,
|
||||
"source_root_set_sha256": proof.source_root_set_sha256,
|
||||
"preserved_object_count": proof.preserved_object_count,
|
||||
"preserved_total_size_bytes": proof.preserved_total_size_bytes,
|
||||
"preserved_inventory_sha256": proof.preserved_inventory_sha256,
|
||||
"preserved_decode_valid_count": (
|
||||
proof.preserved_decode_valid_count
|
||||
),
|
||||
"preserved_decode_invalid_count": (
|
||||
proof.preserved_decode_invalid_count
|
||||
),
|
||||
"required_decode_invalid_object_count": (
|
||||
proof.required_decode_invalid_object_count
|
||||
),
|
||||
"required_decode_invalid_reference_count": (
|
||||
proof.required_decode_invalid_reference_count
|
||||
),
|
||||
"reference_count": proof.reference_count,
|
||||
"unique_object_count": proof.unique_object_count,
|
||||
"reference_set_sha256": proof.reference_set_sha256,
|
||||
"listener_absent": True,
|
||||
"tunnel_absent": True,
|
||||
},
|
||||
ensure_ascii=True,
|
||||
separators=(",", ":"),
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue