20 new tests: thread-safety, tripwire invariants (no cumulative drift), poison, compat seam (reconcile_ok==tripwire_ok), mutation litmus (grep-based K-field ban), Hypothesis fuzz (500 random streams), vendor drift gate. Fix: apply_balance_update now resets tripwire_ok=True (non-fill events default healthy). 26 total (20+6).
92 lines
3.4 KiB
Python
92 lines
3.4 KiB
Python
"""ASExAccountPublisher — single-writer account to ZincPlane.
|
|
|
|
Wraps ASEXAccountV2 (or a test double) so that every account event
|
|
(fill, funding, balance_update) first applies through ASEx, then
|
|
builds an E-only AccountStateSnapshot and publishes it to the
|
|
ZincPlane account region. Edge-triggered: no timer writes, no
|
|
periodic re-stamp. Publishes RAW E, never K.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, List, Optional
|
|
|
|
from .contracts import AccountStateSnapshot
|
|
from .zinc_plane import InMemoryZincPlane
|
|
|
|
|
|
class AccountPublisher:
|
|
"""Applies account events and publishes E snapshots to a ZincPlane.
|
|
|
|
Rev-4-FINAL: E sole authority. tripwire_ok = stateless per-event check.
|
|
reconcile_ok kept as compat alias. No KBlock fields cross the boundary.
|
|
"""
|
|
|
|
def __init__(self, plane: InMemoryZincPlane) -> None:
|
|
self._plane = plane
|
|
self._event_seq: int = 0
|
|
# Internal state tracking E values (simplified: mirrors EBlock fields)
|
|
self._wallet_balance: float = 0.0
|
|
self._available_margin: float = 0.0
|
|
self._used_margin: float = 0.0
|
|
self._e_live: bool = False
|
|
# Stateless tripwire: set by each event, never accumulated
|
|
self._tripwire_ok: bool = True
|
|
self._tripwire_last_event_id: str = ""
|
|
|
|
def apply_fill(self, wallet_balance: float, available_margin: float,
|
|
used_margin: float, reconcile_ok: bool = True,
|
|
tripwire_ok: bool = True, tripwire_last_event_id: str = "") -> None:
|
|
"""Apply a fill event: update E fields, publish snapshot with tripwire."""
|
|
self._wallet_balance = wallet_balance
|
|
self._available_margin = available_margin
|
|
self._used_margin = used_margin
|
|
self._e_live = True
|
|
self._tripwire_ok = tripwire_ok
|
|
self._tripwire_last_event_id = tripwire_last_event_id
|
|
self._publish()
|
|
|
|
def apply_funding(self, amount: float) -> None:
|
|
"""Apply a funding event: funding changes wallet_balance."""
|
|
self._wallet_balance += amount
|
|
self._available_margin += amount
|
|
self._e_live = True
|
|
self._publish()
|
|
|
|
def apply_balance_update(self, wallet_balance: float, available_margin: float,
|
|
used_margin: float) -> None:
|
|
"""Apply a balance update: overwrite all E fields. Resets tripwire to
|
|
True (no fill event = no tripwire assessment needed; defaults to healthy)."""
|
|
self._wallet_balance = wallet_balance
|
|
self._available_margin = available_margin
|
|
self._used_margin = used_margin
|
|
self._e_live = True
|
|
self._tripwire_ok = True
|
|
self._tripwire_last_event_id = ''
|
|
self._publish()
|
|
|
|
def _publish(self) -> None:
|
|
self._event_seq += 1
|
|
import time
|
|
snap = AccountStateSnapshot(
|
|
wallet_balance=self._wallet_balance,
|
|
available_margin=self._available_margin,
|
|
used_margin=self._used_margin,
|
|
event_seq=self._event_seq,
|
|
mono_ns=int(time.monotonic_ns()),
|
|
e_live=self._e_live,
|
|
reconcile_ok=self._tripwire_ok, # compat alias
|
|
tripwire_ok=self._tripwire_ok,
|
|
tripwire_last_event_id=self._tripwire_last_event_id,
|
|
)
|
|
# Publish RAW E — NEVER K
|
|
self._plane.publish_account(snap)
|
|
|
|
@property
|
|
def plane(self) -> InMemoryZincPlane:
|
|
return self._plane
|
|
|
|
@property
|
|
def event_seq(self) -> int:
|
|
return self._event_seq
|