mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Kind 33330 is §7.6's bag: one addressable event holding everything an author hid in one region, addressed by a hash of a hash so the relay can hand it to anyone and it buys them nothing. CyberspaceBagEvent reads it — the §8.6 tags, the AES-256-GCM payload, and the two plaintext shapes §7.6 allows, with an item that fails its signature check dropped alone because "one corrupt or forged item says nothing about the others". A wrong key returns null rather than throwing: §7.6 is explicit that a failed decryption "MUST NOT be treated as an error in the bag". RegionSweep is §7.7's position-free search. The axes are swept independently up to the combine, which is what makes it affordable: §4.7 builds region_n from three per-axis Cantor roots, so a box costs 2^(Hx-h) + 2^(Hy-h) + 2^(Hz-h) trees rather than a whole three-tree key per candidate. It is a cold Sequence on purpose — a hint is a stranger's choice of difficulty, and the caller's budget is the only defence. amy gains `cyberspace open` and `cyberspace sweep`, and the conformance harness gains the two sections that matter most here: cyberspace-cli seals a plaintext with its own key derivation, its own cipher and its own event builder, and amy has to arrive at the same 32 bytes from the coordinate alone — then find the same bag again from nothing but its hint box. 15/15 sections pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwXApJjoZYtkD3sPRWbPNa
230 lines
8.4 KiB
Python
Executable File
230 lines
8.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":…}
|
|
refdriver.py region COORD HEIGHT {"key":…, "lookup_id":…, "x":…, "y":…, "z":…, "plane":…}
|
|
refdriver.py hints §7.7's golden hint vectors, one per line
|
|
refdriver.py encrypt COORD HEIGHT seal stdin into a kind-33330 bag at that region
|
|
refdriver.py box COORD HX HY HZ the §7.7 hint tag for the box around that coord
|
|
|
|
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")
|
|
if not root:
|
|
raise SystemExit("CYBERSPACE_CLI_DIR is not set")
|
|
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")})
|
|
|
|
|
|
def cmd_region():
|
|
"""§2.2 decode plus §7.2 derivation, from cyberspace-cli's own modules.
|
|
|
|
`location_encryption.py` imports AESGCM at module scope and that binding is
|
|
not always present; `cantor` and `movement` are the whole of what a region
|
|
key needs, and going through them keeps this an adapter rather than a
|
|
reimplementation.
|
|
"""
|
|
sys.path.insert(0, os.path.join(os.environ["CYBERSPACE_CLI_DIR"], "src"))
|
|
from cyberspace_core.coords import coord_to_xyz
|
|
from cyberspace_core.cantor import cantor_pair, int_to_bytes_be_min, sha256
|
|
from cyberspace_core.movement import compute_subtree_cantor
|
|
|
|
coord_hex, height = sys.argv[2], int(sys.argv[3])
|
|
x, y, z, plane = coord_to_xyz(int(coord_hex, 16))
|
|
base = lambda v: (v >> height) << height if height > 0 else v
|
|
region_n = cantor_pair(
|
|
cantor_pair(
|
|
compute_subtree_cantor(base(x), height),
|
|
compute_subtree_cantor(base(y), height),
|
|
),
|
|
compute_subtree_cantor(base(z), height),
|
|
)
|
|
key = sha256(int_to_bytes_be_min(region_n))
|
|
emit({
|
|
"key": key.hex(),
|
|
"lookup_id": sha256(key).hex(),
|
|
"x": str(x), "y": str(y), "z": str(z),
|
|
"plane": "ideaspace" if plane else "dataspace",
|
|
})
|
|
|
|
|
|
def cmd_hints():
|
|
"""§7.7's golden vectors, straight out of the spec's own hint-reference.py."""
|
|
root = os.environ["CYBERSPACE_DIR"]
|
|
# It sits at the repo root, beside CYBERSPACE_V2.md, rather than under
|
|
# decks/ where the DECK references live; accept either in case it moves.
|
|
path = next(
|
|
(p for p in (os.path.join(root, "hint-reference.py"), os.path.join(root, "decks", "hint-reference.py")) if os.path.exists(p)),
|
|
None,
|
|
)
|
|
if path is None:
|
|
raise SystemExit("hint-reference.py not found under $CYBERSPACE_DIR")
|
|
hint_ref = _load(path, "hint_reference")
|
|
for name, vector in hint_ref.vectors().items():
|
|
emit({"name": name, **vector})
|
|
|
|
|
|
def cmd_encrypt():
|
|
"""Seal stdin into a §8.6 bag, using cyberspace-cli's own cipher and event builder.
|
|
|
|
The point of the round trip is that nothing on this side is ours: the key
|
|
comes from `location_encryption`, the sealing from `encrypt_with_location_key`
|
|
and the tags from `make_encrypted_content_event`. What `amy cyberspace open`
|
|
then has to do is derive the same key from the same coordinate and read a
|
|
ciphertext it never saw made.
|
|
|
|
A fixed nonce keeps the fixture reproducible; §7.6 wants a fresh one per
|
|
bag, which matters for a hider and not for a test that seals once.
|
|
"""
|
|
sys.path.insert(0, os.path.join(os.environ["CYBERSPACE_CLI_DIR"], "src"))
|
|
import base64
|
|
|
|
from cyberspace_core.coords import coord_to_xyz
|
|
from cyberspace_cli.nostr_event import make_encrypted_content_event
|
|
from cyberspace_core.location_encryption import (
|
|
derive_region_key_material_for_height,
|
|
encrypt_with_location_key,
|
|
)
|
|
|
|
coord_hex, height = sys.argv[2], int(sys.argv[3])
|
|
x, y, z, _plane = coord_to_xyz(int(coord_hex, 16))
|
|
material = derive_region_key_material_for_height(x=x, y=y, z=z, height=height)
|
|
payload = encrypt_with_location_key(
|
|
sys.stdin.buffer.read(),
|
|
location_decryption_key=material.location_decryption_key,
|
|
nonce=bytes(range(12)),
|
|
)
|
|
event = make_encrypted_content_event(
|
|
pubkey_hex="b" * 64,
|
|
created_at=1,
|
|
lookup_id_hex=material.lookup_id_hex,
|
|
algorithm="aes-256-gcm",
|
|
ciphertext_b64=base64.b64encode(payload).decode("ascii"),
|
|
height_hint=height,
|
|
content="",
|
|
kind=33330,
|
|
)
|
|
emit({
|
|
"event": event,
|
|
"key": material.location_decryption_key.hex(),
|
|
"lookup_id": material.lookup_id_hex,
|
|
})
|
|
|
|
|
|
def cmd_box():
|
|
"""The §7.7 `hint` tag naming the aligned box around a coordinate.
|
|
|
|
Built from the reference's own interleave so a sweep test is not marking
|
|
its own homework: the box comes from their `coord_to_xyz`/`xyz_to_coord`,
|
|
and if our alignment disagreed the bag would simply not be in the box we
|
|
were handed.
|
|
"""
|
|
sys.path.insert(0, os.path.join(os.environ["CYBERSPACE_CLI_DIR"], "src"))
|
|
from cyberspace_core.coords import coord_to_xyz, xyz_to_coord
|
|
|
|
coord_hex = sys.argv[2]
|
|
hx, hy, hz = (int(v) for v in sys.argv[3:6])
|
|
x, y, z, plane = coord_to_xyz(int(coord_hex, 16))
|
|
base = xyz_to_coord((x >> hx) << hx, (y >> hy) << hy, (z >> hz) << hz, plane)
|
|
emit({"tag": ["hint", f"{base:064x}", str(hx), str(hy), str(hz)]})
|
|
|
|
|
|
COMMANDS = {
|
|
"box": cmd_box,
|
|
"cases": cmd_cases,
|
|
"encrypt": cmd_encrypt,
|
|
"region": cmd_region,
|
|
"hints": cmd_hints,
|
|
"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]]()
|