Pyramid web app for inspecting arborist Merkle trees over sharded SQLite: document roots, inclusion proofs, an interactive 3D Merkle lattice (six.js / WebGL with graceful fallback), audit-chain linkage, and a live SSE event stream. Generated artifacts (env/, vendored static/six/ bundles, demo databases) are gitignored and rebuilt via the Makefile (venv, six, init-db, serve).
51 lines
1.8 KiB
Python
51 lines
1.8 KiB
Python
"""Merkle inclusion proof endpoint (ticket §6.1, §13).
|
|
|
|
Hard rule (DoD #10): inclusion is integrity, not semantic truth.
|
|
Server-side proof computation lives behind ``arborist.read.Shards.proof``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pyramid.response import Response
|
|
from pyramid.view import view_config
|
|
|
|
|
|
def _json(body, status=200) -> Response:
|
|
return Response(json_body=body, status=status)
|
|
|
|
|
|
@view_config(route_name="api_proof", request_method="GET")
|
|
def get_proof(request):
|
|
if request.shards is None:
|
|
return _json(
|
|
{"error": "no_shard", "note": "configure arborist.shards"}, status=503
|
|
)
|
|
root_hash = request.matchdict["root_hash"]
|
|
try:
|
|
leaf_index = int(request.matchdict["leaf_index"])
|
|
except ValueError:
|
|
return _json({"error": "leaf_index must be an integer"}, status=400)
|
|
try:
|
|
p = request.shards.proof(root_hash, leaf_index)
|
|
if p is None:
|
|
# Fall through to a context root's inclusion proof — same
|
|
# builder, sorted source document_roots as leaves.
|
|
p = request.shards.context_proof(root_hash, leaf_index)
|
|
except IndexError as exc:
|
|
return _json({"error": str(exc)}, status=400)
|
|
if p is None:
|
|
return _json({"error": "not_found", "root_hash": root_hash, "leaf_index": leaf_index}, status=404)
|
|
return _json({
|
|
"root_hash": p.document_root,
|
|
"leaf_index": p.leaf_index,
|
|
"leaf_hash": p.leaf_hash,
|
|
"sibling_hashes": p.sibling_hashes,
|
|
"left_right_flags": p.left_right_flags,
|
|
"computed_root": p.computed_root,
|
|
"expected_root": p.expected_root,
|
|
"passed": p.passed,
|
|
"domain_separator": "leaf=0x00 / node=0x03",
|
|
"shard": p.shard_path,
|
|
"raw_proof": p.raw,
|
|
"source_roots": [p.document_root],
|
|
})
|