349 lines
13 KiB
Python
349 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Dormant Phase-1.0AC adapter driven only by exact built-in fake syscalls.
|
|
|
|
The module has no socket, selector, address, DNS, CLI, real clock or file
|
|
output. It exercises one nonblocking lifecycle with synthetic outcomes and
|
|
the existing passive batch/result contract.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import math
|
|
|
|
from phase10z_passive_batch_contract import (
|
|
PassiveBatch,
|
|
PassiveContractError,
|
|
PassiveResultAccumulator,
|
|
)
|
|
|
|
|
|
MAX_FAKE_STEPS = 1024
|
|
MAX_STEP_ADVANCE_SECONDS = 60.0
|
|
MAX_RECEIVE_BYTES = 65536
|
|
|
|
CREATE_STREAM = "CREATE_STREAM"
|
|
SET_NONBLOCKING = "SET_NONBLOCKING"
|
|
START_CONNECT = "START_CONNECT"
|
|
WAIT_WRITE = "WAIT_WRITE"
|
|
GET_SO_ERROR = "GET_SO_ERROR"
|
|
WRITE_BYTES = "WRITE_BYTES"
|
|
WAIT_READ = "WAIT_READ"
|
|
READ_BYTES = "READ_BYTES"
|
|
|
|
OK = "OK"
|
|
IMMEDIATE = "IMMEDIATE"
|
|
PENDING = "PENDING"
|
|
READY = "READY"
|
|
INTERRUPTED = "INTERRUPTED"
|
|
TIMEOUT = "TIMEOUT"
|
|
ZERO = "ZERO"
|
|
NONZERO = "NONZERO"
|
|
PROGRESS = "PROGRESS"
|
|
EOF = "EOF"
|
|
ERROR = "ERROR"
|
|
|
|
ALLOWED_RESULTS = {
|
|
CREATE_STREAM: {OK, ERROR},
|
|
SET_NONBLOCKING: {OK, ERROR},
|
|
START_CONNECT: {IMMEDIATE, PENDING, ERROR},
|
|
WAIT_WRITE: {READY, INTERRUPTED, TIMEOUT, ERROR},
|
|
GET_SO_ERROR: {ZERO, NONZERO, ERROR},
|
|
WRITE_BYTES: {PROGRESS, ZERO, ERROR},
|
|
WAIT_READ: {READY, INTERRUPTED, TIMEOUT, ERROR},
|
|
READ_BYTES: {PROGRESS, EOF, ERROR},
|
|
}
|
|
|
|
|
|
class DormantAdapterError(RuntimeError):
|
|
"""Normalized synthetic adapter failure without supplied bytes."""
|
|
|
|
|
|
class FakeSyscallError(RuntimeError):
|
|
"""Exact fake-facade state or script failure."""
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FakeSyscallStep:
|
|
"""One synthetic syscall outcome; never an operating-system call."""
|
|
|
|
operation: str
|
|
result: str
|
|
value: int = 0
|
|
data: bytes = b""
|
|
advance_seconds: float = 0.0
|
|
|
|
def __post_init__(self) -> None:
|
|
if self.operation not in ALLOWED_RESULTS or \
|
|
self.result not in ALLOWED_RESULTS[self.operation]:
|
|
raise FakeSyscallError("fake syscall operation/result is invalid")
|
|
if not isinstance(self.value, int) or isinstance(self.value, bool) or \
|
|
self.value < 0:
|
|
raise FakeSyscallError("fake syscall value is invalid")
|
|
if not isinstance(self.data, bytes):
|
|
raise FakeSyscallError("fake syscall data is invalid")
|
|
advance = self.advance_seconds
|
|
if not isinstance(advance, (int, float)) or isinstance(advance, bool) or \
|
|
not math.isfinite(advance) or not 0.0 <= advance <= \
|
|
MAX_STEP_ADVANCE_SECONDS:
|
|
raise FakeSyscallError("fake syscall time advance is invalid")
|
|
if self.operation == WRITE_BYTES and self.result == PROGRESS:
|
|
if self.value <= 0 or self.data:
|
|
raise FakeSyscallError("fake write progress is invalid")
|
|
elif self.operation == READ_BYTES and self.result == PROGRESS:
|
|
if not self.data or self.value != len(self.data):
|
|
raise FakeSyscallError("fake read progress is invalid")
|
|
elif self.value != 0 or self.data:
|
|
raise FakeSyscallError("fake syscall carries unexpected data")
|
|
|
|
|
|
class OfflineFakeClock:
|
|
"""Explicit synthetic monotonic value; never reads the host clock."""
|
|
|
|
def __init__(self, initial: float = 0.0) -> None:
|
|
if not isinstance(initial, (int, float)) or isinstance(initial, bool) or \
|
|
not math.isfinite(initial):
|
|
raise FakeSyscallError("fake clock initial value is invalid")
|
|
self._value = float(initial)
|
|
|
|
def monotonic(self) -> float:
|
|
return self._value
|
|
|
|
def advance(self, seconds: float) -> None:
|
|
if not isinstance(seconds, (int, float)) or isinstance(seconds, bool) or \
|
|
not math.isfinite(seconds) or not 0.0 <= seconds <= \
|
|
MAX_STEP_ADVANCE_SECONDS:
|
|
raise FakeSyscallError("fake clock advance is invalid")
|
|
self._value += float(seconds)
|
|
|
|
|
|
class OfflineFakeSyscallFacade:
|
|
"""Closed fake facade; it cannot retain a target or create a capability."""
|
|
|
|
def __init__(
|
|
self, clock: OfflineFakeClock, steps: tuple[FakeSyscallStep, ...],
|
|
close_result: str = OK,
|
|
) -> None:
|
|
if type(clock) is not OfflineFakeClock:
|
|
raise FakeSyscallError("only the exact fake clock is accepted")
|
|
if not isinstance(steps, tuple) or not 1 <= len(steps) <= MAX_FAKE_STEPS \
|
|
or any(type(step) is not FakeSyscallStep for step in steps):
|
|
raise FakeSyscallError("fake syscall script is invalid")
|
|
if close_result not in {OK, ERROR}:
|
|
raise FakeSyscallError("fake close result is invalid")
|
|
self.clock = clock
|
|
self._steps = list(steps)
|
|
self._close_result = close_result
|
|
self.state = "NEW"
|
|
self.invoke_count = 0
|
|
self.close_count = 0
|
|
self.discarded_steps = 0
|
|
self.trace: list[str] = []
|
|
|
|
def invoke(self, operation: str) -> FakeSyscallStep:
|
|
if self.state == "CLOSED" or not self._steps:
|
|
raise FakeSyscallError("fake syscall script is exhausted")
|
|
step = self._steps.pop(0)
|
|
if step.operation != operation:
|
|
raise FakeSyscallError("fake syscall ordering is invalid")
|
|
self.clock.advance(step.advance_seconds)
|
|
self.invoke_count += 1
|
|
self.trace.append(f"{operation}:{step.result}")
|
|
self.state = "ACTIVE"
|
|
return step
|
|
|
|
def close_once(self) -> None:
|
|
if self.state == "CLOSED" or self.close_count != 0:
|
|
raise FakeSyscallError("fake facade cannot close")
|
|
self.close_count = 1
|
|
self.discarded_steps = len(self._steps)
|
|
self._steps.clear()
|
|
self.trace.append(f"LOCAL_CLOSE:{self._close_result}")
|
|
self.state = "CLOSED"
|
|
if self._close_result != OK:
|
|
raise FakeSyscallError("synthetic local close failed")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DormantAdapterOutcome:
|
|
classification: str
|
|
result_classification: str
|
|
exact_identity: bool
|
|
batch_bytes_sent: int
|
|
received_bytes: int
|
|
write_calls: int
|
|
read_calls: int
|
|
interrupted_waits: int
|
|
timed_out_waits: int
|
|
discarded_steps_after_close: int
|
|
trace: tuple[str, ...]
|
|
target_retained: bool = False
|
|
live_transport_present: bool = False
|
|
device_behavior_proven: bool = False
|
|
|
|
|
|
def _before_deadline(clock: OfflineFakeClock, deadline: float) -> None:
|
|
if clock.monotonic() >= deadline:
|
|
raise DormantAdapterError("synthetic deadline reached")
|
|
|
|
|
|
def _wait_until_ready(
|
|
facade: OfflineFakeSyscallFacade,
|
|
clock: OfflineFakeClock,
|
|
deadline: float,
|
|
operation: str,
|
|
) -> tuple[bool, int, int]:
|
|
interrupted = 0
|
|
timed_out = 0
|
|
while True:
|
|
_before_deadline(clock, deadline)
|
|
step = facade.invoke(operation)
|
|
if clock.monotonic() >= deadline:
|
|
if step.result == READY:
|
|
raise DormantAdapterError(
|
|
"synthetic deadline won readiness race")
|
|
if step.result == INTERRUPTED:
|
|
return False, interrupted + 1, timed_out
|
|
if step.result == TIMEOUT:
|
|
return False, interrupted, timed_out + 1
|
|
raise DormantAdapterError("synthetic wait failed at deadline")
|
|
if step.result == READY:
|
|
return True, interrupted, timed_out
|
|
if step.result == INTERRUPTED:
|
|
interrupted += 1
|
|
continue
|
|
if step.result == TIMEOUT:
|
|
timed_out += 1
|
|
continue
|
|
raise DormantAdapterError("synthetic readiness failed")
|
|
|
|
|
|
def run_dormant_adapter(
|
|
batch: PassiveBatch,
|
|
facade: OfflineFakeSyscallFacade,
|
|
clock: OfflineFakeClock,
|
|
receipt_precommitted: bool,
|
|
) -> DormantAdapterOutcome:
|
|
"""Run one target-free fake-syscall lifecycle and normalize all failure."""
|
|
if type(batch) is not PassiveBatch or \
|
|
type(facade) is not OfflineFakeSyscallFacade or \
|
|
type(clock) is not OfflineFakeClock or facade.clock is not clock or \
|
|
receipt_precommitted is not True:
|
|
raise DormantAdapterError("dormant adapter boundary is invalid")
|
|
|
|
start = clock.monotonic()
|
|
deadline = start + batch.deadline_seconds
|
|
accumulator: PassiveResultAccumulator
|
|
try:
|
|
accumulator = PassiveResultAccumulator(batch)
|
|
except PassiveContractError as error:
|
|
raise DormantAdapterError("passive batch is invalid") from error
|
|
|
|
sent = 0
|
|
received = 0
|
|
write_calls = 0
|
|
read_calls = 0
|
|
interrupted_waits = 0
|
|
timed_out_waits = 0
|
|
opened = False
|
|
sanitized = None
|
|
primary_failure: DormantAdapterError | None = None
|
|
|
|
try:
|
|
_before_deadline(clock, deadline)
|
|
if facade.invoke(CREATE_STREAM).result != OK:
|
|
raise DormantAdapterError("synthetic stream creation failed")
|
|
opened = True
|
|
_before_deadline(clock, deadline)
|
|
if facade.invoke(SET_NONBLOCKING).result != OK:
|
|
raise DormantAdapterError("synthetic nonblocking setup failed")
|
|
_before_deadline(clock, deadline)
|
|
connect_result = facade.invoke(START_CONNECT).result
|
|
if connect_result == PENDING:
|
|
ready, interrupted, timed_out = _wait_until_ready(
|
|
facade, clock, deadline, WAIT_WRITE)
|
|
interrupted_waits += interrupted
|
|
timed_out_waits += timed_out
|
|
if not ready:
|
|
raise DormantAdapterError(
|
|
"synthetic connect reached deadline")
|
|
if facade.invoke(GET_SO_ERROR).result != ZERO:
|
|
raise DormantAdapterError("synthetic pending connect failed")
|
|
_before_deadline(clock, deadline)
|
|
elif connect_result != IMMEDIATE:
|
|
raise DormantAdapterError("synthetic immediate connect failed")
|
|
|
|
while sent < len(batch.payload):
|
|
ready, interrupted, timed_out = _wait_until_ready(
|
|
facade, clock, deadline, WAIT_WRITE)
|
|
interrupted_waits += interrupted
|
|
timed_out_waits += timed_out
|
|
if not ready:
|
|
raise DormantAdapterError("synthetic write reached deadline")
|
|
step = facade.invoke(WRITE_BYTES)
|
|
write_calls += 1
|
|
if step.result != PROGRESS or step.value > len(batch.payload) - sent:
|
|
raise DormantAdapterError("synthetic write made invalid progress")
|
|
sent += step.value
|
|
_before_deadline(clock, deadline)
|
|
|
|
while clock.monotonic() < deadline:
|
|
ready, interrupted, timed_out = _wait_until_ready(
|
|
facade, clock, deadline, WAIT_READ)
|
|
interrupted_waits += interrupted
|
|
timed_out_waits += timed_out
|
|
if not ready:
|
|
break
|
|
step = facade.invoke(READ_BYTES)
|
|
read_calls += 1
|
|
if step.result == EOF:
|
|
raise DormantAdapterError("synthetic EOF is not completion")
|
|
if step.result != PROGRESS or received + len(step.data) > \
|
|
MAX_RECEIVE_BYTES:
|
|
raise DormantAdapterError("synthetic read is invalid")
|
|
if clock.monotonic() >= deadline:
|
|
raise DormantAdapterError("synthetic data reached deadline")
|
|
try:
|
|
accumulator.feed_supplied_chunk(step.data)
|
|
except PassiveContractError as error:
|
|
raise DormantAdapterError("passive result rejected input") from error
|
|
received += len(step.data)
|
|
|
|
if clock.monotonic() < deadline or received == 0:
|
|
raise DormantAdapterError("synthetic deadline result is incomplete")
|
|
try:
|
|
sanitized = accumulator.seal_at_hard_deadline(True)
|
|
except PassiveContractError as error:
|
|
raise DormantAdapterError("passive deadline result is invalid") from error
|
|
except Exception as error: # noqa: BLE001 - exact fake boundary normalization
|
|
primary_failure = DormantAdapterError("dormant fake-syscall run failed")
|
|
primary_failure.__cause__ = error
|
|
finally:
|
|
if opened:
|
|
try:
|
|
facade.close_once()
|
|
except FakeSyscallError as error:
|
|
if primary_failure is None:
|
|
primary_failure = DormantAdapterError(
|
|
"dormant fake-syscall cleanup failed")
|
|
primary_failure.__cause__ = error
|
|
|
|
if primary_failure is not None:
|
|
raise primary_failure
|
|
if sanitized is None or facade.close_count != 1:
|
|
raise DormantAdapterError("dormant fake-syscall result is incomplete")
|
|
return DormantAdapterOutcome(
|
|
classification="OFFLINE_DORMANT_FAKE_SYSCALL_ADAPTER_COMPLETE",
|
|
result_classification=str(sanitized["classification"]),
|
|
exact_identity=bool(sanitized["exact_identity"]),
|
|
batch_bytes_sent=sent,
|
|
received_bytes=received,
|
|
write_calls=write_calls,
|
|
read_calls=read_calls,
|
|
interrupted_waits=interrupted_waits,
|
|
timed_out_waits=timed_out_waits,
|
|
discarded_steps_after_close=facade.discarded_steps,
|
|
trace=("CONSUMED_RECEIPT_PREEXISTS", *facade.trace),
|
|
)
|