Explorer
/opt/struktur/buzz/docs/formal/nip-pl/mutation_test.py
← Zurück ↓ Download
"""Mutation test: prove the acceptance model has TEETH.

We inject the two most tempting spec-weakenings and confirm the model CATCHES
each one. A model that stays green under a real weakening is worthless.

  M1: accept on generation ALONE (drop NIP-01 ordering, the (a) clause).
      => the poison event e3 (gen9, created150 < tombstone's 200) is accepted,
         resurrecting the lease and poisoning the watermark. Must trip I1 & I3.

  M2: accept on NIP-01 ordering ALONE (drop the generation watermark, clause (b)).
      => a high-created_at REPLAY with a stale generation wins; watermark is no
         longer the resurrection backstop. Must trip a resurrection under replay.
"""
from itertools import permutations as P
from acceptance import Ev, nip01_beats

def run(mode):
    universe = [
        Ev("e1", 1, 100, True),
        Ev("e2", 2, 200, False),   # legit revoke
        Ev("e3", 9, 150, True),    # poison: high gen, loses NIP-01
        Ev("e4", 3, 250, True),
        Ev("e5", 1, 100, True),
        Ev("a1", 5, 200, True),
        # WITNESS for clause (b): an event with the HIGHEST created_at but a STALE
        # generation. NIP-01 alone accepts it (created 300 > all); only the
        # generation watermark rejects it. This is the "malicious high-created_at
        # replay with stale gen" the watermark exists to stop.
        Ev("z1", 0, 300, True),
    ]
    caught = 0
    for perm in P(universe):
        stored = None; effective = False; watermark = -1
        for ev in perm:
            wins_nip01 = nip01_beats(ev, stored)
            wins_gen = ev.gen > watermark
            if mode == "M1":      # gen only
                accept = wins_gen
            elif mode == "M2":    # nip01 only
                accept = wins_nip01
            else:                 # spec: both
                accept = wins_nip01 and wins_gen
            eff_before = effective
            wm_before = watermark
            if accept:
                stored = ev; effective = ev.active; watermark = ev.gen
            # resurrection check: tombstone stored, then flipped active by an event
            # that does NOT beat both orderings
            if effective and not eff_before and stored is ev:
                if not (nip01_beats(ev, None) and ev.gen > wm_before):
                    pass
            # poison check: nip01-loser raised watermark
            if not nip01_beats(ev, stored if stored is not ev else None):
                pass
        # simpler post-hoc: did e3 (the poison) ever end up as the effective active
        # state after e2's tombstone appeared earlier in the order?
        # replay to detect
        st=None; ef=False; wm=-1; tomb=False; bug=False
        for ev in perm:
            wn = nip01_beats(ev, st); wg = ev.gen > wm
            acc = wg if mode=="M1" else (wn if mode=="M2" else (wn and wg))
            if acc:
                st=ev; ef=ev.active; wm=ev.gen
            if st is not None and not st.active and not ef:
                tomb=True
            if tomb and ef and st is ev and ev in (universe[2], universe[4], universe[6]):
                # e3, e5, or z1 -- none should EVER be the effective active state
                # after e2's tombstone. e3/e5 lose NIP-01; z1 loses only on gen
                # (stale generation) -- so z1 is the pure clause-(b) witness.
                bug=True
        if bug:
            caught += 1
    return caught

for m, desc in [("M1","gen-only (drop NIP-01)"), ("M2","nip01-only (drop watermark)"), ("SPEC","dual-ordering (spec)")]:
    c = run(m)
    print(f"{m:5} {desc:28} -> bug orderings detected: {c}")