hexarchproof-sdk 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hexarchproof/__init__.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import uuid
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from canonicaljson import encode_canonical_json
|
|
10
|
+
|
|
11
|
+
JsonLike = dict[str, Any] | list[Any] | str | int | float | bool | None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def canonical_json_bytes(value: JsonLike) -> bytes:
|
|
15
|
+
"""RFC 8785 canonical JSON bytes. Identical to TypeScript canonicalJsonBytes."""
|
|
16
|
+
return encode_canonical_json(value)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def canonical_json_string(value: JsonLike) -> str:
|
|
20
|
+
return canonical_json_bytes(value).decode("utf-8")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def sha256_hex_bytes(raw: bytes) -> str:
|
|
24
|
+
return hashlib.sha256(raw).hexdigest()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def sha256_hex_string(input: str, encoding: str = "utf-8") -> str:
|
|
28
|
+
return sha256_hex_bytes(input.encode(encoding))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def sha256_hex_json(value: JsonLike) -> str:
|
|
32
|
+
"""SHA-256 hex of canonical JSON bytes. Matches TypeScript sha256HexJson exactly."""
|
|
33
|
+
return sha256_hex_bytes(canonical_json_bytes(value))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _proof_payload_for_hash(proof: dict[str, Any]) -> dict[str, Any]:
|
|
37
|
+
return {
|
|
38
|
+
"schema_version": proof["schema_version"],
|
|
39
|
+
"proof_id": proof["proof_id"],
|
|
40
|
+
"input_hash": proof["input_hash"],
|
|
41
|
+
"spec_hash": proof["spec_hash"],
|
|
42
|
+
"output_hash": proof["output_hash"],
|
|
43
|
+
"timestamp": proof["timestamp"],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def recompute_proof_hash(proof: dict[str, Any]) -> str:
|
|
48
|
+
return sha256_hex_bytes(canonical_json_bytes(_proof_payload_for_hash(proof)))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_proof(
|
|
52
|
+
input_payload: JsonLike,
|
|
53
|
+
spec_payload: JsonLike,
|
|
54
|
+
output_payload: JsonLike,
|
|
55
|
+
network: str = "sepolia",
|
|
56
|
+
) -> dict[str, Any]:
|
|
57
|
+
"""Build a fully formed DRPv1Proof. Mirrors buildProof in the TypeScript SDK."""
|
|
58
|
+
timestamp = datetime.now(UTC).isoformat()
|
|
59
|
+
proof: dict[str, Any] = {
|
|
60
|
+
"schema_version": "drp.v1",
|
|
61
|
+
"proof_id": str(uuid.uuid4()),
|
|
62
|
+
"input_hash": sha256_hex_json(input_payload),
|
|
63
|
+
"spec_hash": sha256_hex_json(spec_payload),
|
|
64
|
+
"output_hash": sha256_hex_json(output_payload),
|
|
65
|
+
"anchor": {"network": network, "tx_hash": None, "block_number": None, "anchored_at": None},
|
|
66
|
+
"timestamp": timestamp,
|
|
67
|
+
}
|
|
68
|
+
proof["proof_hash"] = recompute_proof_hash(proof)
|
|
69
|
+
return proof
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class VerificationResult:
|
|
74
|
+
valid: bool
|
|
75
|
+
checks: dict[str, bool]
|
|
76
|
+
errors: list[str]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
_HEX64_LEN = 64
|
|
80
|
+
_HEX_CHARS = frozenset("0123456789abcdefABCDEF")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _is_hex64(value: Any) -> bool:
|
|
84
|
+
return isinstance(value, str) and len(value) == _HEX64_LEN and all(c in _HEX_CHARS for c in value)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _is_valid_drp_shape(candidate: Any) -> bool:
|
|
88
|
+
if not isinstance(candidate, dict):
|
|
89
|
+
return False
|
|
90
|
+
anchor = candidate.get("anchor")
|
|
91
|
+
return (
|
|
92
|
+
candidate.get("schema_version") == "drp.v1"
|
|
93
|
+
and isinstance(candidate.get("proof_id"), str)
|
|
94
|
+
and len(candidate.get("proof_id", "")) > 0
|
|
95
|
+
and _is_hex64(candidate.get("input_hash"))
|
|
96
|
+
and _is_hex64(candidate.get("spec_hash"))
|
|
97
|
+
and _is_hex64(candidate.get("output_hash"))
|
|
98
|
+
and _is_hex64(candidate.get("proof_hash"))
|
|
99
|
+
and isinstance(candidate.get("timestamp"), str)
|
|
100
|
+
and len(candidate.get("timestamp", "")) > 0
|
|
101
|
+
and isinstance(anchor, dict)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def verify_proof(
|
|
106
|
+
proof: Any,
|
|
107
|
+
input_payload: JsonLike,
|
|
108
|
+
spec_payload: JsonLike,
|
|
109
|
+
output_payload: JsonLike,
|
|
110
|
+
) -> VerificationResult:
|
|
111
|
+
"""Verify a DRPv1Proof document against original payloads. Mirrors verifyProof in the TS SDK."""
|
|
112
|
+
checks: dict[str, bool] = {
|
|
113
|
+
"schema_valid": False,
|
|
114
|
+
"input_hash_matches": False,
|
|
115
|
+
"spec_hash_matches": False,
|
|
116
|
+
"output_hash_matches": False,
|
|
117
|
+
"proof_hash_matches": False,
|
|
118
|
+
}
|
|
119
|
+
errors: list[str] = []
|
|
120
|
+
if not _is_valid_drp_shape(proof):
|
|
121
|
+
errors.append("schema_invalid:proof does not match DRPv1Proof shape")
|
|
122
|
+
return VerificationResult(valid=False, checks=checks, errors=errors)
|
|
123
|
+
checks["schema_valid"] = True
|
|
124
|
+
checks["input_hash_matches"] = proof["input_hash"] == sha256_hex_json(input_payload)
|
|
125
|
+
if not checks["input_hash_matches"]: errors.append("input_hash_mismatch")
|
|
126
|
+
checks["spec_hash_matches"] = proof["spec_hash"] == sha256_hex_json(spec_payload)
|
|
127
|
+
if not checks["spec_hash_matches"]: errors.append("spec_hash_mismatch")
|
|
128
|
+
checks["output_hash_matches"] = proof["output_hash"] == sha256_hex_json(output_payload)
|
|
129
|
+
if not checks["output_hash_matches"]: errors.append("output_hash_mismatch")
|
|
130
|
+
checks["proof_hash_matches"] = proof["proof_hash"] == recompute_proof_hash(proof)
|
|
131
|
+
if not checks["proof_hash_matches"]: errors.append("proof_hash_mismatch")
|
|
132
|
+
return VerificationResult(valid=all(checks.values()), checks=checks, errors=errors)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = [
|
|
136
|
+
"JsonLike", "VerificationResult",
|
|
137
|
+
"canonical_json_bytes", "canonical_json_string",
|
|
138
|
+
"sha256_hex_bytes", "sha256_hex_string", "sha256_hex_json",
|
|
139
|
+
"build_proof", "recompute_proof_hash", "verify_proof",
|
|
140
|
+
]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hexarchproof-sdk
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Python SDK for Hexarch Deterministic Reproduction Proofs (DRP). Same input → same hash → same proof, anchored for independent verification.
|
|
5
|
+
Author-email: Noir Stack LLC <info@noirstack.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://hexarch.systems/
|
|
8
|
+
Project-URL: Repository, https://github.com/no1rstack/hexarchproof-sdk
|
|
9
|
+
Project-URL: Bug Tracker, https://noirstack.com/support
|
|
10
|
+
Keywords: hexarch,drp,deterministic,reproducibility,verifiable-compute,proof,auditability,noirstack,canonical-json,sha256
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Security :: Cryptography
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: canonicaljson>=2.0.0
|
|
22
|
+
|
|
23
|
+
# hexarchproof-sdk (Python)
|
|
24
|
+
|
|
25
|
+
Python SDK for **Hexarch Deterministic Reproduction Proofs (DRP)**.
|
|
26
|
+
|
|
27
|
+
> Same input → same hash → same proof — anchored for independent verification.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install hexarchproof-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from hexarchproof import build_proof, verify_proof, sha256_hex_string, sha256_hex_json
|
|
39
|
+
|
|
40
|
+
hash_val = sha256_hex_string('my payload')
|
|
41
|
+
json_hash = sha256_hex_json({'b': 2, 'a': 1})
|
|
42
|
+
|
|
43
|
+
proof = build_proof(
|
|
44
|
+
{'input': 'my deterministic payload'},
|
|
45
|
+
{'type': 'hexarch.text-proof', 'version': '1'},
|
|
46
|
+
{'output': 'my deterministic payload'},
|
|
47
|
+
)
|
|
48
|
+
# proof['schema_version'] == 'drp.v1'
|
|
49
|
+
|
|
50
|
+
result = verify_proof(
|
|
51
|
+
proof,
|
|
52
|
+
{'input': 'my deterministic payload'},
|
|
53
|
+
{'type': 'hexarch.text-proof', 'version': '1'},
|
|
54
|
+
{'output': 'my deterministic payload'},
|
|
55
|
+
)
|
|
56
|
+
# result.valid == True
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Links
|
|
60
|
+
|
|
61
|
+
- Hexarch Domain: https://hexarch.systems/
|
|
62
|
+
- Discord: https://discord.gg/DZysBQJQ
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
hexarchproof/__init__.py,sha256=RV5WMImeBodd1Q9aPWlzHKajbQtGmOKaQfk9GcikYGs,4923
|
|
2
|
+
hexarchproof_sdk-0.0.2.dist-info/METADATA,sha256=nBFHIWlhep1UZbsP3fVvaow-RSfQ3VcbI4FcP-L_-jk,2014
|
|
3
|
+
hexarchproof_sdk-0.0.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
4
|
+
hexarchproof_sdk-0.0.2.dist-info/top_level.txt,sha256=l_-ix__gTu6ZNuXoWrlhBZXzhRN-3k9dWJP400ScMVg,13
|
|
5
|
+
hexarchproof_sdk-0.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hexarchproof
|