TL;DR
Enterprises can verify millions of Marturia receipts without checking every signature. Use Merkle-root verification for O(log N) proofs on any receipt, run full-chain replay only when required, and rely on statistical sampling for continuous assurance. A simple Python script plus a CI step keeps the entire tenant chain honest at production scale.
The verification problem at scale¶
A busy tenant can emit tens of thousands of receipts per day. Each receipt carries an Ed25519 signature, a SHA-256 hash link to its predecessor, and (when published) a Merkle root anchored on a public witness. Verifying one receipt is cheap; verifying every receipt for every tenant every day is not. The goal is to choose the right verification strategy for the risk tolerance and latency budget of the workload.
The cheap path: Merkle anchor verification¶
Once a Merkle root is published and witnessed, any receipt inside that batch can be proven with a short inclusion proof. The verifier only needs:
- The published root (already signed by the witness network).
- The receipt itself.
- The O(log N) sibling hashes that reconstruct the root.
This reduces the cost of proving any single receipt from O(N) to O(log N) and removes the need to re-verify every preceding receipt.
The thorough path: full-chain replay¶
Full replay walks the hash chain from the first receipt to the latest, checking:
- Ed25519 signature on every receipt
- Correct
prev_hashlinkage - Monotonic sequence numbers
Replay gives the strongest guarantee but scales linearly with the number of receipts. It is normally reserved for initial onboarding, regulatory audits, or when a sampling check flags an anomaly.
Sampling-based audit¶
Random spot-checks combined with Merkle-root verification deliver cryptographic assurance at low cost. Because every receipt is committed to a Merkle tree, an auditor can request a small random subset (for example 0.1 %) and still obtain a high probability of detecting any tampering that affects more than a few receipts. The remaining receipts are covered by the single root signature.
Python script for bulk verification¶
The example below uses the Marturia search API to fetch every receipt for a tenant, verifies each signature and hash link, and prints a concise report.
#!/usr/bin/env python3
import requests
import ed25519
import hashlib
import json
from datetime import datetime
TENANT = "acme-prod"
API = "https://api.marturia.dev/v1"
VERIFY_KEY = ed25519.VerifyingKey(open("tenant-acme.pub").read())
def fetch_all_receipts(tenant):
receipts = []
cursor = None
while True:
params = {"tenant": tenant, "limit": 1000}
if cursor:
params["cursor"] = cursor
r = requests.get(f"{API}/receipts/search", params=params, timeout=30)
r.raise_for_status()
batch = r.json()["receipts"]
receipts.extend(batch)
cursor = r.json().get("next_cursor")
if not cursor:
break
return receipts
def verify_receipt(r):
sig = bytes.fromhex(r["signature"])
msg = json.dumps(r["payload"], sort_keys=True).encode()
try:
VERIFY_KEY.verify(sig, msg)
except ed25519.BadSignatureError:
return False
if r.get("prev_hash"):
expected = hashlib.sha256(
json.dumps(r["prev_payload"], sort_keys=True).encode()
).hexdigest()
if expected != r["prev_hash"]:
return False
return True
def main():
receipts = fetch_all_receipts(TENANT)
failures = []
for r in receipts:
if not verify_receipt(r):
failures.append(r["id"])
print(f"Checked {len(receipts)} receipts at {datetime.utcnow().isoformat()}Z")
if failures:
print("FAILURES:", failures)
exit(1)
print("All receipts verified successfully")
if __name__ == "__main__":
main()
Run the script from a scheduled job or CI pipeline; it exits non-zero on any failure so downstream steps can be blocked.
CI integration¶
Add the verification step to your audit-trail release pipeline:
# .github/workflows/audit.yml
- name: Verify tenant chain
run: |
python3 verify_tenant.py --tenant acme-prod --threshold 0.001
env:
MARTURIA_API_KEY: ${{ secrets.MARTURIA_API_KEY }}
Fail the build if the script reports any invalid receipt or if the sampled Merkle proofs do not reconstruct the published root.
Performance characteristics¶
Ed25519 verification runs at roughly 10 000 signatures per second on a single modern core. Verifying one million receipts therefore takes about 100 seconds of CPU time. Parallelizing across cores or machines reduces wall-clock time linearly. Merkle-proof verification is orders of magnitude faster because only the root signature and a logarithmic number of hashes are checked.
Trade-off diagram¶
Related Marturia resources¶
- /docs/verify.html
- /learn/lesson_06_the_verifier.html