mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
`amy sno parse|work|verify` — validate a DECK-0003 payload against §1.9, price the proof of work an avatar owes for it, and check whether a kind:11333 avatar has paid. Local, accountless, thin over quartz: the verbs exist so the reader can be diffed against the implementations the spec points at, without a device or a relay. cli/tests/sno/ is that diff. The reference's own fixtures drive it — _rejections() out of decks/sno-reference.py and the golden vectors out of cyberspace-cli — so the cases are theirs, not ours. 26 §1.9 verdicts agree on both the answer and the rule number; all 8 avatar vectors agree. The four places the two differ are asserted as divergences rather than skipped, because a reader that quietly stopped diverging would be as interesting as one that started: - we read the v:2-with-literal-triples cohort, the arbiter refuses it (§5 permits the generosity; three of seven live objects need it); - we refuse a vertex past 64 units, the arbiter accepts it (§8's own open question, and whole*120 overflows an Int); - the Python reference still calls 33331 the avatar kind, a week behind §8.10's move to 11333, so it reads a conformant avatar as not-an-avatar and would price a standalone object as one; - empty content is §8.10's default avatar to us and not-an-avatar to it. Same outcome — the client draws its default — different word. SnoAvatarEvent gains payment(), which reports required/committed/zeros and why, mirroring verify_avatar_work's shape so the two are directly comparable; isPaid() is now that result's ok. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwXApJjoZYtkD3sPRWbPNa
107 lines
3.4 KiB
Python
Executable File
107 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Adapter over the cyberspace project's own reference implementations.
|
|
|
|
Speaks one JSON object per line on stdout so `sno-conformance.sh` can diff it
|
|
against `amy sno … --json` with jq. Nothing here reimplements anything: it
|
|
imports `decks/sno-reference.py` from the cyberspace spec repo (the §1.9
|
|
arbiter) and `cyberspace_core/avatar.py` from cyberspace-cli (the §8.10 work
|
|
and payment reference), and just re-emits what they answer.
|
|
|
|
refdriver.py cases the deck's own rejection table, + Appendix A
|
|
refdriver.py vectors cyberspace-cli's avatar-work golden vectors
|
|
refdriver.py verdict < payload {"valid":…, "rule":…}
|
|
refdriver.py work < payload {"required":…}
|
|
refdriver.py verify < event {"ok":…, "required":…, "committed":…, "zeros":…, "reason":…}
|
|
|
|
Paths come from $CYBERSPACE_DIR and $CYBERSPACE_CLI_DIR.
|
|
"""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import signal
|
|
import sys
|
|
|
|
# Used in pipelines (`| head`), where dying on SIGPIPE is the right answer
|
|
# rather than a traceback.
|
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
|
|
|
|
|
def _load(path, name):
|
|
spec = importlib.util.spec_from_file_location(name, path)
|
|
if spec is None:
|
|
raise SystemExit(f"cannot load {path}")
|
|
mod = importlib.util.module_from_spec(spec)
|
|
sys.modules[name] = mod
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def _sno():
|
|
root = os.environ.get("CYBERSPACE_DIR")
|
|
if not root:
|
|
raise SystemExit("CYBERSPACE_DIR is not set")
|
|
return _load(os.path.join(root, "decks", "sno-reference.py"), "sno_reference")
|
|
|
|
|
|
def _avatar():
|
|
root = os.environ.get("CYBERSPACE_CLI_DIR")
|
|
if not root:
|
|
raise SystemExit("CYBERSPACE_CLI_DIR is not set")
|
|
sys.path.insert(0, os.path.join(root, "src"))
|
|
return _load(os.path.join(root, "src", "cyberspace_core", "avatar.py"), "ref_avatar")
|
|
|
|
|
|
def emit(obj):
|
|
print(json.dumps(obj, separators=(",", ":"), sort_keys=True))
|
|
|
|
|
|
def cmd_cases():
|
|
sno = _sno()
|
|
emit({"name": "appendix A", "rule": None, "payload": dict(sno.APPENDIX_A)})
|
|
for label, payload in sno._rejections():
|
|
emit({"name": label, "rule": label.replace("rule ", ""), "payload": payload})
|
|
|
|
|
|
def cmd_vectors():
|
|
root = os.environ.get("CYBERSPACE_CLI_DIR")
|
|
with open(os.path.join(root, "tests", "fixtures", "avatar_work.json")) as fh:
|
|
for case in json.load(fh):
|
|
emit({"name": case["name"], "required": case["required"], "payload": case["payload"]})
|
|
|
|
|
|
def cmd_verdict():
|
|
sno = _sno()
|
|
payload = json.load(sys.stdin)
|
|
try:
|
|
sno.validate(dict(payload))
|
|
emit({"valid": True, "rule": None})
|
|
except sno.SnoError as err:
|
|
text = str(err)
|
|
rule = text.split(":")[0].replace("rule ", "").strip() if text.startswith("rule ") else "?"
|
|
emit({"valid": False, "rule": rule})
|
|
|
|
|
|
def cmd_work():
|
|
avatar = _avatar()
|
|
emit({"required": avatar.avatar_work(json.load(sys.stdin))})
|
|
|
|
|
|
def cmd_verify():
|
|
avatar = _avatar()
|
|
result = avatar.verify_avatar_work(json.load(sys.stdin))
|
|
emit({k: result[k] for k in ("ok", "required", "committed", "zeros", "reason")})
|
|
|
|
|
|
COMMANDS = {
|
|
"cases": cmd_cases,
|
|
"vectors": cmd_vectors,
|
|
"verdict": cmd_verdict,
|
|
"work": cmd_work,
|
|
"verify": cmd_verify,
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
|
|
raise SystemExit(f"usage: refdriver.py <{'|'.join(COMMANDS)}>")
|
|
COMMANDS[sys.argv[1]]()
|