Spaces:
Running
Running
Download scripts/validate_hosted_proof_receipt.py from noqt/eggcracker: direct link, hf CLI and curl.
- Browser
- Download file 12.4 kB
-
https://huggingface.co/spaces/noqt/eggcracker/resolve/main/scripts/validate_hosted_proof_receipt.py
- Command line
-
hf download hf://spaces/noqt/eggcracker/scripts/validate_hosted_proof_receipt.py
-
curl -L -o validate_hosted_proof_receipt.py https://huggingface.co/spaces/noqt/eggcracker/resolve/main/scripts/validate_hosted_proof_receipt.py
12.4 kB
| #!/usr/bin/env python3 | |
| """Validate one bounded hosted-proof receipt against the public v1 contract.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import math | |
| import re | |
| import stat | |
| import sys | |
| from collections.abc import Mapping, Sequence | |
| from pathlib import Path | |
| from typing import Any | |
| ROOT = Path(__file__).resolve().parents[1] | |
| SCHEMA_PATH = ROOT / "schemas" / "hosted-proof-receipt-v1.schema.json" | |
| MAX_RECEIPT_BYTES = 16_384 | |
| MAX_SCHEMA_BYTES = 65_536 | |
| SUPPORTED_DIALECT = "https://json-schema.org/draft/2020-12/schema" | |
| ANNOTATION_KEYWORDS = {"$id", "$schema", "description", "title"} | |
| VALIDATION_KEYWORDS = { | |
| "additionalProperties", | |
| "const", | |
| "enum", | |
| "maximum", | |
| "minimum", | |
| "oneOf", | |
| "pattern", | |
| "properties", | |
| "required", | |
| "type", | |
| } | |
| class ReceiptValidationError(ValueError): | |
| """A bounded validation failure that never includes receipt values.""" | |
| class SchemaContractError(ReceiptValidationError): | |
| """The checked-in schema uses a contract this validator cannot enforce.""" | |
| def _unique_object(pairs: list[tuple[str, object]]) -> dict[str, object]: | |
| value: dict[str, object] = {} | |
| for name, item in pairs: | |
| if name in value: | |
| raise ReceiptValidationError("JSON contains a duplicate object member") | |
| value[name] = item | |
| return value | |
| def _reject_nonstandard_number(_value: str) -> None: | |
| raise ReceiptValidationError("JSON contains a non-standard number") | |
| def _read_json(path: Path, *, maximum_bytes: int, label: str) -> object: | |
| try: | |
| metadata = path.lstat() | |
| if not stat.S_ISREG(metadata.st_mode): | |
| raise ReceiptValidationError(f"{label} must be a regular file") | |
| with path.open("rb") as handle: | |
| encoded = handle.read(maximum_bytes + 1) | |
| except ReceiptValidationError: | |
| raise | |
| except OSError as error: | |
| raise ReceiptValidationError(f"{label} could not be read") from error | |
| if len(encoded) > maximum_bytes: | |
| raise ReceiptValidationError(f"{label} exceeds its safe size bound") | |
| try: | |
| return json.loads( | |
| encoded.decode("utf-8"), | |
| object_pairs_hook=_unique_object, | |
| parse_constant=_reject_nonstandard_number, | |
| ) | |
| except ReceiptValidationError: | |
| raise | |
| except (UnicodeError, json.JSONDecodeError, RecursionError) as error: | |
| raise ReceiptValidationError(f"{label} is not bounded UTF-8 JSON") from error | |
| def _json_equal(left: object, right: object) -> bool: | |
| if isinstance(left, bool) or isinstance(right, bool): | |
| return type(left) is type(right) and left == right | |
| if isinstance(left, (int, float)) and isinstance(right, (int, float)): | |
| return left == right | |
| return type(left) is type(right) and left == right | |
| def _matches_type(value: object, expected: str) -> bool: | |
| if expected == "object": | |
| return isinstance(value, dict) | |
| if expected == "string": | |
| return isinstance(value, str) | |
| if expected == "integer": | |
| return ( | |
| isinstance(value, (int, float)) | |
| and not isinstance(value, bool) | |
| and math.isfinite(value) | |
| and (isinstance(value, int) or value.is_integer()) | |
| ) | |
| if expected == "number": | |
| return ( | |
| isinstance(value, (int, float)) | |
| and not isinstance(value, bool) | |
| and math.isfinite(value) | |
| ) | |
| raise SchemaContractError("schema uses an unsupported type") | |
| def _preflight_schema(schema: Mapping[str, Any], *, root: bool = False) -> None: | |
| """Reject schema drift anywhere before a receipt can select a branch.""" | |
| unknown = set(schema) - ANNOTATION_KEYWORDS - VALIDATION_KEYWORDS | |
| if unknown: | |
| raise SchemaContractError("schema uses an unsupported validation keyword") | |
| for keyword in ANNOTATION_KEYWORDS & set(schema): | |
| if not isinstance(schema[keyword], str): | |
| raise SchemaContractError("schema has an invalid annotation") | |
| if root and schema.get("$schema") != SUPPORTED_DIALECT: | |
| raise SchemaContractError("schema uses an unsupported dialect") | |
| if not root and "$schema" in schema: | |
| raise SchemaContractError("schema has a nested dialect declaration") | |
| expected_type = schema.get("type") | |
| if expected_type is not None and expected_type not in { | |
| "integer", | |
| "number", | |
| "object", | |
| "string", | |
| }: | |
| raise SchemaContractError("schema uses an unsupported type") | |
| branches = schema.get("oneOf") | |
| if branches is not None: | |
| if not isinstance(branches, list) or not branches: | |
| raise SchemaContractError("schema has an invalid oneOf contract") | |
| for branch in branches: | |
| if not isinstance(branch, dict): | |
| raise SchemaContractError("schema has an invalid oneOf branch") | |
| _preflight_schema(branch) | |
| properties = schema.get("properties") | |
| required = schema.get("required") | |
| additional = schema.get("additionalProperties") | |
| has_object_contract = ( | |
| properties is not None | |
| or required is not None | |
| or "additionalProperties" in schema | |
| ) | |
| if has_object_contract: | |
| if ( | |
| expected_type != "object" | |
| or not isinstance(properties, dict) | |
| or not isinstance(required, list) | |
| or additional is not False | |
| or not all(isinstance(item, str) for item in required) | |
| or len(required) != len(set(required)) | |
| ): | |
| raise SchemaContractError("schema has an invalid object contract") | |
| for name, item_schema in properties.items(): | |
| if not isinstance(name, str) or not isinstance(item_schema, dict): | |
| raise SchemaContractError("schema has an invalid property contract") | |
| _preflight_schema(item_schema) | |
| allowed = schema.get("enum") | |
| if allowed is not None and (not isinstance(allowed, list) or not allowed): | |
| raise SchemaContractError("schema has an invalid enum contract") | |
| pattern = schema.get("pattern") | |
| if pattern is not None: | |
| if not isinstance(pattern, str) or expected_type != "string": | |
| raise SchemaContractError("schema has an invalid string pattern") | |
| try: | |
| re.compile(pattern) | |
| except re.error as error: | |
| raise SchemaContractError("schema has an invalid string pattern") from error | |
| minimum = schema.get("minimum") | |
| maximum = schema.get("maximum") | |
| if minimum is not None or maximum is not None: | |
| if expected_type not in {"integer", "number"}: | |
| raise SchemaContractError("schema has an invalid numeric bound") | |
| for bound in (minimum, maximum): | |
| if bound is not None and not _matches_type(bound, "number"): | |
| raise SchemaContractError("schema has an invalid numeric bound") | |
| if minimum is not None and maximum is not None and minimum > maximum: | |
| raise SchemaContractError("schema has inconsistent numeric bounds") | |
| def _validate(value: object, schema: Mapping[str, Any], path: str = "receipt") -> None: | |
| unknown = set(schema) - ANNOTATION_KEYWORDS - VALIDATION_KEYWORDS | |
| if unknown: | |
| raise SchemaContractError("schema uses an unsupported validation keyword") | |
| branches = schema.get("oneOf") | |
| if branches is not None: | |
| if not isinstance(branches, list) or not branches: | |
| raise SchemaContractError("schema has an invalid oneOf contract") | |
| matches = 0 | |
| for branch in branches: | |
| if not isinstance(branch, dict): | |
| raise SchemaContractError("schema has an invalid oneOf branch") | |
| try: | |
| _validate(value, branch, path) | |
| except SchemaContractError: | |
| raise | |
| except ReceiptValidationError: | |
| continue | |
| matches += 1 | |
| if matches != 1: | |
| raise ReceiptValidationError("receipt does not match exactly one v1 shape") | |
| expected_type = schema.get("type") | |
| if expected_type is not None and not isinstance(expected_type, str): | |
| raise SchemaContractError("schema has an invalid type contract") | |
| if isinstance(expected_type, str) and not _matches_type(value, expected_type): | |
| raise ReceiptValidationError(f"{path} has the wrong JSON type") | |
| if "const" in schema and not _json_equal(value, schema["const"]): | |
| raise ReceiptValidationError(f"{path} does not match its required value") | |
| allowed = schema.get("enum") | |
| if allowed is not None and not isinstance(allowed, list): | |
| raise SchemaContractError("schema has an invalid enum contract") | |
| if isinstance(allowed, list) and not any(_json_equal(value, item) for item in allowed): | |
| raise ReceiptValidationError(f"{path} is not an allowed value") | |
| pattern = schema.get("pattern") | |
| if pattern is not None: | |
| if not isinstance(pattern, str) or not isinstance(value, str): | |
| raise SchemaContractError("schema has an invalid string pattern") | |
| try: | |
| matches_pattern = re.fullmatch(pattern, value) is not None | |
| except re.error as error: | |
| raise SchemaContractError("schema has an invalid string pattern") from error | |
| if not matches_pattern: | |
| raise ReceiptValidationError(f"{path} does not match its required format") | |
| if "minimum" in schema or "maximum" in schema: | |
| if not _matches_type(value, "number"): | |
| raise ReceiptValidationError(f"{path} has the wrong JSON type") | |
| minimum = schema.get("minimum") | |
| maximum = schema.get("maximum") | |
| if minimum is not None and not _matches_type(minimum, "number"): | |
| raise SchemaContractError("schema has an invalid minimum") | |
| if maximum is not None and not _matches_type(maximum, "number"): | |
| raise SchemaContractError("schema has an invalid maximum") | |
| if minimum is not None and value < minimum: | |
| raise ReceiptValidationError(f"{path} is below its allowed bound") | |
| if maximum is not None and value > maximum: | |
| raise ReceiptValidationError(f"{path} is above its allowed bound") | |
| properties = schema.get("properties") | |
| required = schema.get("required") | |
| if properties is not None or required is not None or schema.get("additionalProperties") is False: | |
| if not isinstance(value, dict) or not isinstance(properties, dict): | |
| raise SchemaContractError("schema has an invalid object contract") | |
| if not isinstance(required, list) or not all(isinstance(item, str) for item in required): | |
| raise SchemaContractError("schema has an invalid required-field contract") | |
| missing = set(required) - set(value) | |
| if missing: | |
| raise ReceiptValidationError("receipt is missing a required field") | |
| if schema.get("additionalProperties") is False and set(value) - set(properties): | |
| raise ReceiptValidationError("receipt contains an unexpected field") | |
| for name, item_schema in properties.items(): | |
| if not isinstance(name, str) or not isinstance(item_schema, dict): | |
| raise SchemaContractError("schema has an invalid property contract") | |
| if name in value: | |
| _validate(value[name], item_schema, f"{path}.{name}") | |
| def validate_receipt(receipt: object, schema: object) -> None: | |
| if not isinstance(schema, dict): | |
| raise SchemaContractError("schema root must be an object") | |
| _preflight_schema(schema, root=True) | |
| _validate(receipt, schema) | |
| def main(argv: Sequence[str] | None = None) -> int: | |
| parser = argparse.ArgumentParser( | |
| description="validate one local Lumi Eggcracker hosted-proof receipt against v1" | |
| ) | |
| parser.add_argument("receipt", type=Path, help="path to one JSON receipt object") | |
| arguments = parser.parse_args(argv) | |
| try: | |
| schema = _read_json(SCHEMA_PATH, maximum_bytes=MAX_SCHEMA_BYTES, label="schema") | |
| receipt = _read_json( | |
| arguments.receipt, | |
| maximum_bytes=MAX_RECEIPT_BYTES, | |
| label="receipt input", | |
| ) | |
| validate_receipt(receipt, schema) | |
| except ReceiptValidationError as error: | |
| print(f"INVALID hosted-proof receipt v1: {error}", file=sys.stderr) | |
| return 1 | |
| print("VALID hosted-proof receipt v1") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |