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:
parent
0714fff7fa
commit
b1219c64bc
2 changed files with 43 additions and 8 deletions
|
|
@ -8,6 +8,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -42,7 +43,7 @@ class UrllibHttp:
|
||||||
def request(
|
def request(
|
||||||
self, method: str, url: str, payload: dict[str, Any], headers: dict[str, str]
|
self, method: str, url: str, payload: dict[str, Any], headers: dict[str, str]
|
||||||
) -> _UrllibResp:
|
) -> _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)
|
req = urllib.request.Request(url, data=data, method=method)
|
||||||
for key, value in headers.items():
|
for key, value in headers.items():
|
||||||
req.add_header(key, value)
|
req.add_header(key, value)
|
||||||
|
|
@ -106,7 +107,7 @@ class GlobalClient:
|
||||||
res = self.http.request(
|
res = self.http.request(
|
||||||
"POST",
|
"POST",
|
||||||
url,
|
url,
|
||||||
payload={"hash": digest, "sync": True},
|
payload={"data": digest, "hashAlg": "SHA256"},
|
||||||
headers=self._headers(),
|
headers=self._headers(),
|
||||||
)
|
)
|
||||||
if res.status_code >= 300:
|
if res.status_code >= 300:
|
||||||
|
|
@ -115,13 +116,37 @@ class GlobalClient:
|
||||||
job_id = parsed.get("jobId") or parsed.get("blockHash") or ""
|
job_id = parsed.get("jobId") or parsed.get("blockHash") or ""
|
||||||
if not job_id:
|
if not job_id:
|
||||||
raise ValueError("global timestamp response missing jobId")
|
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(
|
return Receipt(
|
||||||
receipt_id=str(job_id),
|
receipt_id=receipt_id,
|
||||||
hash=digest,
|
hash=digest,
|
||||||
scope="global",
|
scope="global",
|
||||||
cube_id=cube_id,
|
cube_id=cube_id,
|
||||||
created_at=str(parsed.get("timestamp") or ""),
|
created_at=created,
|
||||||
merkle=str(parsed.get("blockHash") or ""),
|
merkle=merkle,
|
||||||
)
|
)
|
||||||
|
|
||||||
def verify_certificate(self, receipt_id: str) -> VerifyResult:
|
def verify_certificate(self, receipt_id: str) -> VerifyResult:
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class FakeHttp:
|
||||||
200,
|
200,
|
||||||
{
|
{
|
||||||
"jobId": "job-1",
|
"jobId": "job-1",
|
||||||
"hash": payload["hash"],
|
"hash": payload.get("data") or payload.get("hash"),
|
||||||
"timestamp": "2026-08-15T00:00:00Z",
|
"timestamp": "2026-08-15T00:00:00Z",
|
||||||
"blockHash": "0xdead",
|
"blockHash": "0xdead",
|
||||||
},
|
},
|
||||||
|
|
@ -38,6 +38,16 @@ class FakeHttp:
|
||||||
if payload.get("certificate") == "job-1":
|
if payload.get("certificate") == "job-1":
|
||||||
return _Resp(200, {"valid": True, "hash": SHA256_EMPTY})
|
return _Resp(200, {"valid": True, "hash": SHA256_EMPTY})
|
||||||
return _Resp(200, {"valid": False})
|
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"})
|
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]
|
method, url, payload, headers = http.calls[0]
|
||||||
assert method == "POST"
|
assert method == "POST"
|
||||||
assert url == "https://api.veraetime.net/api/timestamp"
|
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 headers["Authorization"] == "Bearer secret"
|
||||||
assert rec.scope == "global"
|
assert rec.scope == "global"
|
||||||
assert rec.receipt_id == "job-1"
|
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")
|
[g] = await routed.stamp(SHA256_EMPTY, scope="global", cube_id="cube-1")
|
||||||
assert g.scope == "global"
|
assert g.scope == "global"
|
||||||
assert g.receipt_id == "job-1"
|
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")
|
cached = await routed.hash_report(SHA256_EMPTY, want_scope="global")
|
||||||
assert cached[0].receipt_id == "job-1"
|
assert cached[0].receipt_id == "job-1"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue