fix: post data+SHA256 to VeraeTime and poll job for CID certificate

Live /api/timestamp rejects {hash,sync}; it wants {data,hashAlg:SHA256}
and returns a job whose result is a multihash certificate.
This commit is contained in:
George Lambert 2026-09-20 10:18:24 -04:00
commit b1219c64bc
2 changed files with 43 additions and 8 deletions

View file

@ -8,6 +8,7 @@ from __future__ import annotations
import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path
@ -42,7 +43,7 @@ class UrllibHttp:
def request(
self, method: str, url: str, payload: dict[str, Any], headers: dict[str, str]
) -> _UrllibResp:
data = json.dumps(payload).encode()
data = None if method == "GET" else json.dumps(payload).encode()
req = urllib.request.Request(url, data=data, method=method)
for key, value in headers.items():
req.add_header(key, value)
@ -106,7 +107,7 @@ class GlobalClient:
res = self.http.request(
"POST",
url,
payload={"hash": digest, "sync": True},
payload={"data": digest, "hashAlg": "SHA256"},
headers=self._headers(),
)
if res.status_code >= 300:
@ -115,13 +116,37 @@ class GlobalClient:
job_id = parsed.get("jobId") or parsed.get("blockHash") or ""
if not job_id:
raise ValueError("global timestamp response missing jobId")
created = str(parsed.get("timestamp") or "")
merkle = str(parsed.get("blockHash") or "")
receipt_id = str(job_id)
status_url = f"{self.base_url}/api/status/{job_id}"
for _ in range(20):
st = self.http.request("GET", status_url, payload={}, headers=self._headers())
if st.status_code >= 300:
break
body = st.json() if hasattr(st, "json") else {}
if not isinstance(body, dict):
break
state = str(body.get("status") or "")
if state == "failed":
raise ValueError(str(body.get("error") or "global timestamp job failed"))
if state == "completed":
receipt_id = str(body.get("result") or body.get("id") or job_id)
meta = body.get("metadata") or {}
created = str(meta.get("timestamp") or body.get("completedAt") or created)
merkle = str(meta.get("blockIndex") or meta.get("blockHash") or merkle)
break
if state in ("pending", "running", ""):
time.sleep(0.25)
continue
break
return Receipt(
receipt_id=str(job_id),
receipt_id=receipt_id,
hash=digest,
scope="global",
cube_id=cube_id,
created_at=str(parsed.get("timestamp") or ""),
merkle=str(parsed.get("blockHash") or ""),
created_at=created,
merkle=merkle,
)
def verify_certificate(self, receipt_id: str) -> VerifyResult:

View file

@ -29,7 +29,7 @@ class FakeHttp:
200,
{
"jobId": "job-1",
"hash": payload["hash"],
"hash": payload.get("data") or payload.get("hash"),
"timestamp": "2026-08-15T00:00:00Z",
"blockHash": "0xdead",
},
@ -38,6 +38,16 @@ class FakeHttp:
if payload.get("certificate") == "job-1":
return _Resp(200, {"valid": True, "hash": SHA256_EMPTY})
return _Resp(200, {"valid": False})
if "/api/status/" in url:
return _Resp(
200,
{
"id": "job-1",
"status": "completed",
"result": "job-1",
"metadata": {"timestamp": "2026-08-15T00:00:00Z", "blockHash": "0xdead"},
},
)
return _Resp(404, {"error": "nope"})
@ -49,7 +59,7 @@ async def test_global_stamp_posts_hash_sync_and_maps_receipt():
method, url, payload, headers = http.calls[0]
assert method == "POST"
assert url == "https://api.veraetime.net/api/timestamp"
assert payload == {"hash": SHA256_EMPTY, "sync": True}
assert payload == {"data": SHA256_EMPTY, "hashAlg": "SHA256"}
assert headers["Authorization"] == "Bearer secret"
assert rec.scope == "global"
assert rec.receipt_id == "job-1"
@ -84,7 +94,7 @@ async def test_routing_sends_only_global_scope_to_central():
[g] = await routed.stamp(SHA256_EMPTY, scope="global", cube_id="cube-1")
assert g.scope == "global"
assert g.receipt_id == "job-1"
assert len(http.calls) == 1
assert http.calls[0][1].endswith("/api/timestamp")
cached = await routed.hash_report(SHA256_EMPTY, want_scope="global")
assert cached[0].receipt_id == "job-1"