Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/pyrxd/cli/glyph_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,15 @@ def _render_txid_human(payload: dict) -> str:
lines.append(f" bound_ref={row.get('bound_ref_outpoint', '')}")
lines.append(f" owner_pkh={row.get('owner_pkh', '')}")
lines.append(f" variant={row.get('variant', '')} (non-transferable at consensus)")
# The classifier attaches a `note` saying what this verdict does NOT
# establish, and this row used to drop it while the standalone script
# card printed it in full. This is the path most people meet the tool
# on, and "non-transferable at consensus" is exactly the sentence a
# credential or swap gate would over-trust. The sibling
# container-legacy branch already points the reader onward; soulbound
# did not.
lines.append(" does NOT verify the singleton is held here — see")
lines.append(" `pyrxd glyph inspect <script>` for the full qualifier")
elif type_ == "self-replicating-covenant":
lines.append(f" bound_ref={row.get('bound_ref_outpoint', '(multiple)')}")
lines.append(" markers only — NOT proof of soulbound")
Expand Down Expand Up @@ -549,7 +558,24 @@ def _render_script_human(payload: dict) -> str:
body.append(f" self-replication branch: {payload['has_self_replication']}")
body.append(f" burn branch: {payload['has_burn_branch']}")
body.append(" NON-TRANSFERABLE AT CONSENSUS — the only spends this lock permits are")
body.append(" a byte-identical self-clone or a burn. There is no transfer path.")
# The two variants pin DIFFERENT things, and this said "byte-identical" for
# both. The fixed-index builder compares whole scripts
# (OP_OUTPUTBYTECODE / OP_UTXOBYTECODE); the composable one compares
# CODE-SCRIPT HASHES, and its own docstring says "code-identical clone".
#
# Those coincide only because neither builder emits OP_STATESEPARATOR, so the
# code script IS the whole script. That is not a detail to paper over: code-
# script equality plus a state prefix lets the OWNER change between hops, and
# `classify_soulbound` returns MUTABLE_STATE_COVENANT for exactly that shape.
# Naming the weaker constraint accurately is what makes the distinction
# visible to whoever reads this next.
if payload.get("variant") == "composable":
body.append(" a CODE-identical self-clone or a burn. There is no transfer path.")
body.append(" (this variant pins its code-script hash, not the whole script; the two")
body.append(" coincide here because the builder emits no OP_STATESEPARATOR, so there")
body.append(" is no mutable state for a clone to change)")
else:
body.append(" a byte-identical self-clone or a burn. There is no transfer path.")
body.append(" (exact match against pyrxd's soulbound covenant builder. It does NOT")
body.append(" verify the bound ref names a live Glyph singleton, that the singleton")
body.append(" is actually held here, or that the covenant is defect-free — the")
Expand Down
80 changes: 80 additions & 0 deletions tests/test_confusables_check_is_actually_wired.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,83 @@ def test_the_browser_bridge_does_not_OVERWRITE_the_classifier(self) -> None:
assert 'warnings = dict(metadata.get("display_warnings") or {})' in inspect_source, (
"the bridge must SEED from the classifier's warnings, not replace them"
)


class TestTheSoulboundVERDICTIsPreciseOnBothSurfaces:
"""Two claims the CLI made that the code does not support.

1. The tx-listing row printed `variant=… (non-transferable at consensus)` and
DROPPED the classifier's own `note` — "does NOT verify that ref names a live
Glyph singleton, that the singleton is actually held here, or that the
covenant is free of defects". The standalone script card printed it in full.
The tx listing is the path most people meet the tool on, and this is exactly
the sentence a credential or swap gate would over-trust.

2. Both variants were described as permitting "a byte-identical self-clone".
The fixed-index builder does compare whole scripts; the COMPOSABLE one
compares CODE-SCRIPT HASHES, and its own docstring says "code-identical
clone". They coincide only because neither builder emits OP_STATESEPARATOR —
and code-script equality WITH a state prefix is precisely the shape that lets
the owner change, which `classify_soulbound` now reports as
MUTABLE_STATE_COVENANT.
"""

@staticmethod
def _script_card(variant: str) -> str:
from pyrxd.cli.glyph_inspect import _render_script_human

return _render_script_human(
{
"type": "soulbound-covenant",
"length": 100,
"variant": variant,
"bound_ref_outpoint": "ab" * 32 + ":0",
"owner_pkh": "cd" * 20,
"has_self_replication": True,
"has_burn_branch": True,
"transferability": "soulbound_covenant",
}
)

def test_the_composable_variant_is_not_called_byte_identical(self) -> None:
text = self._script_card("composable")
assert "CODE-identical" in text
assert "byte-identical" not in text, "composable pins a code-script hash, not the whole script"

def test_the_fixed_index_variant_still_says_byte_identical(self) -> None:
"""It really does compare whole scripts — weakening this would be its own
inaccuracy, in the safe-sounding direction."""
assert "byte-identical" in self._script_card("fixed-index")

def test_the_composable_card_says_WHY_the_two_coincide(self) -> None:
"""Without the state-separator reasoning a reader cannot tell whether the
weaker constraint matters here. It does not — and that is a fact about the
builder, not about the opcodes."""
assert "OP_STATESEPARATOR" in self._script_card("composable")

def test_the_tx_listing_row_carries_a_qualifier(self) -> None:
from pyrxd.cli.glyph_inspect import _render_txid_human

text = _render_txid_human(
{
"txid": "aa" * 32,
"version": 2,
"locktime": 0,
"byte_length": 300,
"input_count": 1,
"output_count": 1,
"inputs": [],
"outputs": [
{
"vout": 0,
"satoshis": 1000,
"type": "soulbound-covenant",
"variant": "fixed-index",
"bound_ref_outpoint": "ab" * 32 + ":0",
"owner_pkh": "cd" * 20,
}
],
}
)
assert "non-transferable at consensus" in text
assert "does NOT verify" in text, "the bare verdict must not travel alone on this path"
Loading