mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-06-13 09:37:53 +02:00
122 lines
4.6 KiB
Python
122 lines
4.6 KiB
Python
"""Sprint 6 — sacrifice burn-on-entry mechanic.
|
|
|
|
Maps to IMPLEMENTATION_PLAN §7.1 Sprint 6 row:
|
|
"Sacrifice burns rep on entry (not refundable)."
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from services.infonet.gates import can_enter, compute_member_set, is_member
|
|
from services.infonet.tests._chain_factory import make_market_chain
|
|
from services.infonet.tests._gate_factory import (
|
|
make_gate_create,
|
|
make_gate_enter,
|
|
make_gate_exit,
|
|
)
|
|
|
|
|
|
def test_unknown_gate_cannot_be_entered():
|
|
decision = can_enter("alice", "no-such-gate", [])
|
|
assert not decision.accepted
|
|
assert decision.refusals[0].kind == "gate_not_found"
|
|
|
|
|
|
def test_can_enter_with_sufficient_rep():
|
|
base = 1_000_000.0
|
|
chain = []
|
|
# Give alice enough common rep through an uprep from an oracle holder.
|
|
chain += make_market_chain(
|
|
"m1", "creator", outcome="yes",
|
|
predictions=[
|
|
{"node_id": "ora", "side": "yes", "stake_amount": 50.0},
|
|
{"node_id": "loser", "side": "no", "stake_amount": 50.0},
|
|
],
|
|
base_ts=base, participants=5, total_stake=100.0,
|
|
)
|
|
# Many upreps from ora to alice → alice has substantial common rep.
|
|
from services.infonet.tests._chain_factory import make_event
|
|
for i in range(3):
|
|
chain.append(make_event(
|
|
"uprep", "ora",
|
|
{"target_node_id": "alice", "target_event_id": f"e{i}"},
|
|
timestamp=base + 10_000 + i * 1000, sequence=100 + i,
|
|
))
|
|
chain.append(make_gate_create("g1", "creator", ts=base + 20_000, seq=200,
|
|
entry_sacrifice=5, min_overall_rep=0))
|
|
decision = can_enter("alice", "g1", chain)
|
|
assert decision.accepted
|
|
assert decision.cost == 5
|
|
|
|
|
|
def test_insufficient_rep_refused_with_diagnostic():
|
|
base = 1_000_000.0
|
|
chain = [make_gate_create("g1", "creator", ts=base, seq=1,
|
|
entry_sacrifice=10, min_overall_rep=0)]
|
|
decision = can_enter("alice", "g1", chain)
|
|
assert not decision.accepted
|
|
refusal_kinds = {r.kind for r in decision.refusals}
|
|
assert "insufficient_common_rep" in refusal_kinds
|
|
|
|
|
|
def test_member_set_after_enter_and_exit():
|
|
base = 1_000_000.0
|
|
chain = [
|
|
make_gate_create("g1", "creator", ts=base, seq=1),
|
|
make_gate_enter("g1", "alice", ts=base + 100, seq=2),
|
|
make_gate_enter("g1", "bob", ts=base + 200, seq=3),
|
|
make_gate_exit("g1", "alice", ts=base + 300, seq=4),
|
|
]
|
|
members = compute_member_set("g1", chain)
|
|
assert members == {"bob"}
|
|
|
|
|
|
def test_already_member_cannot_re_enter():
|
|
base = 1_000_000.0
|
|
chain = [
|
|
make_gate_create("g1", "creator", ts=base, seq=1, entry_sacrifice=0),
|
|
make_gate_enter("g1", "alice", ts=base + 100, seq=2),
|
|
]
|
|
decision = can_enter("alice", "g1", chain)
|
|
assert not decision.accepted
|
|
assert decision.refusals[0].kind == "already_member"
|
|
|
|
|
|
def test_voluntary_exit_does_not_refund_sacrifice():
|
|
"""Sacrifice is BURNED on entry. Voluntary exit removes member
|
|
status but does NOT credit sacrifice back. The chain has no
|
|
refund event — that's the structural enforcement.
|
|
"""
|
|
base = 1_000_000.0
|
|
chain = [
|
|
make_gate_create("g1", "creator", ts=base, seq=1, entry_sacrifice=10),
|
|
make_gate_enter("g1", "alice", ts=base + 100, seq=2, sacrifice=10),
|
|
make_gate_exit("g1", "alice", ts=base + 200, seq=3),
|
|
]
|
|
# alice is no longer a member.
|
|
assert not is_member("alice", "g1", chain)
|
|
# No "gate_refund" event exists in the schema. The sacrifice is
|
|
# gone from the system permanently — common_rep view never gets
|
|
# it back. (This is a structural / definitional invariant: the
|
|
# protocol doesn't have an event type that could refund a
|
|
# sacrifice. Asserting that here as a marker for future AIs.)
|
|
from services.infonet.schema import INFONET_ECONOMY_EVENT_TYPES
|
|
refund_event_types = {t for t in INFONET_ECONOMY_EVENT_TYPES if "refund" in t}
|
|
assert refund_event_types == set()
|
|
|
|
|
|
def test_shutdown_voids_member_set():
|
|
"""When a gate is shutdown, the member set zeroes out — members
|
|
are released but lose access. (Sprint 6: sacrifice is already
|
|
burned by then.)"""
|
|
base = 1_000_000.0
|
|
chain = [
|
|
make_gate_create("g1", "creator", ts=base, seq=1),
|
|
make_gate_enter("g1", "alice", ts=base + 100, seq=2),
|
|
make_gate_enter("g1", "bob", ts=base + 200, seq=3),
|
|
# Synthesize a shutdown_execute event.
|
|
{"event_type": "gate_shutdown_execute",
|
|
"node_id": "creator", "timestamp": base + 1000, "sequence": 99,
|
|
"payload": {"petition_id": "p1", "gate_id": "g1"}},
|
|
]
|
|
assert compute_member_set("g1", chain) == set()
|