Files
StarPilot/openpilot/starpilot/flm/feedback_replay.py
T
2026-10-21 23:28:39 -05:00

137 lines
7.7 KiB
Python

"""NEW event source feedback proof, separate from legacy event admission."""
from dataclasses import dataclass
from openpilot.starpilot.flm.candidate_feedback import LEGACY_POLICY,CLOSED_POLICY,PROOF_VERSION,policy as validate_candidate_policy
PROTOCOL='chronological-source-feedback-mean-fit-v1'
EVENT_VERSION=7
_SEAL=object()
@dataclass(frozen=True)
class FeedbackReplayEvidence:
binding: str
original: object
windows: tuple
engine_input_digest: str
_seal: object
candidate_policy: str = LEGACY_POLICY
def _inputs(engine, *, candidate_policy=LEGACY_POLICY):
# Hash trusted in-process objects ONLY; no pickle data is ever decoded here.
# Keep the same original CP/factory/controller/native/checkpoint/body coverage
# as the existing causal owned-input key, without recursively hashing this seal.
import hashlib
import pickle
from openpilot.starpilot.flm.controller_replay import SelectedControllerFactory
from openpilot.starpilot.flm.pid_controller_replay import PidControllerFactory
factory=engine.factory
if type(factory) not in (SelectedControllerFactory,PidControllerFactory):
raise ValueError('registered original feedback factory required')
policy=('torque',str(factory.policy)) if type(factory) is SelectedControllerFactory else ('stock-pid',None)
selected=(type(factory).__module__,type(factory).__qualname__,factory.cp.as_builder().to_bytes(),
str(factory.mode),policy,factory.turn_assist,factory.manual_baseline,factory.manual_source_epoch,
factory.interface_class.__module__,factory.interface_class.__qualname__)
native=(engine.emissions.cp.as_builder().to_bytes(),engine.emissions.identity,engine.emissions.baseline_revision,
tuple(engine.emissions.events.items()),tuple(engine.emissions.carriers.items()))
body=(engine.identity,engine.baseline_revision,engine.engine.model_revision,engine.engine.failed,
engine.engine.raw_mode,selected,engine.engine.objective,engine.objective,engine.reference,
engine.prior_full,engine.original_anchor,engine.activation_plan,engine.restore_envelope,
tuple(engine.records),tuple(engine.checkpoints),tuple(engine.replay_windows),native)
from openpilot.starpilot.flm.normalized_trace_archive import archive_key
if archive_key(engine) is not None:
body = (*body,archive_key(engine))
validate_candidate_policy(candidate_policy)
if candidate_policy != LEGACY_POLICY:
body = (*body,candidate_policy,PROOF_VERSION)
return hashlib.sha256(pickle.dumps(body,protocol=5)).hexdigest()
def verify_archive(packet, original):
from openpilot.starpilot.flm.feedback_archive import archive_binding
from openpilot.starpilot.flm.feedback_observation import verify_original
from openpilot.starpilot.flm.feedback_archive_codec import expand_observations
from openpilot.starpilot.flm.torque_raw_fit import output_from_dict
from openpilot.starpilot.flm.normalized_trace_archive import normalized_packet,archive_of
if normalized_packet(packet):
import pickle
owner_archive = archive_of(original)
if owner_archive is None or pickle.dumps(owner_archive.literal,protocol=5) != pickle.dumps(packet['trace_archive'],protocol=5):
raise ValueError('normalized feedback original literal source differs')
binding=archive_binding(packet)
publications,consumptions=expand_observations(packet['feedback_observations'],binding_digest=binding)
frames=tuple(original.records)
outputs={value.apply_ns:value for value in (output_from_dict(row) for row in packet['context_receipts']['outputs'])}
return verify_original(publications,consumptions,original.original_context_controls,outputs,original,binding,
expected_control_ids=tuple(frame.control_ns for frame in frames),context_packet=packet['context_receipts'],
frames=frames,allow_initial_context_only=True)
def attach_feedback_engine(engine, tape, *, candidate_policy=LEGACY_POLICY):
from openpilot.starpilot.flm.event_controller_replay import EventControllerReplay
from openpilot.starpilot.flm.feedback_observation import subset_verified
if type(engine) is not EventControllerReplay or engine.activation_plan is None:
raise ValueError('new feedback requires actual declared stage engine')
windows=[]
for bounds in engine.replay_windows:
controls=tuple(frame.control_ns for frame in engine.records if bounds[0]<=frame.control_ns<=bounds[1])
if not controls or controls[0]!=bounds[0] or controls[-1]!=bounds[1]:
raise ValueError('complete feedback engine window required')
context=tuple(row.control_ns for row in tape.context_only)
if context:
# The sealed global first q may precede a later stage-only replay.
if len(context) != 1 or context[0] > engine.records[0].control_ns:
raise ValueError('only original leading feedback context allowed')
controls=tuple(stamp for stamp in controls if stamp not in context)
subset=subset_verified(tape,controls,binding_digest=tape.binding_digest)
activation=engine.activation_plan.activation_for(*bounds)
if activation is None:
raise ValueError('original feedback activation clock required')
# An original context-only window has no treatment. Requiring every request
# to equal original through its end proves the unchanged prefix instead.
flag_activation=activation[0] or bounds[1]+1
windows.append((bounds,subset,flag_activation))
validate_candidate_policy(candidate_policy)
evidence=FeedbackReplayEvidence(tape.binding_digest,tape,tuple(windows),
_inputs(engine,candidate_policy=candidate_policy),_SEAL,candidate_policy)
engine.verified_feedback_tape=evidence
return evidence
def require_feedback_engine(engine):
from openpilot.starpilot.flm.feedback_observation import validate_subset_verified
evidence=getattr(engine,'verified_feedback_tape',None)
if (type(evidence) is not FeedbackReplayEvidence or evidence._seal is not _SEAL or
type(evidence.candidate_policy) is not str or evidence.candidate_policy not in (LEGACY_POLICY,CLOSED_POLICY) or
evidence.engine_input_digest!=_inputs(engine,candidate_policy=evidence.candidate_policy) or
tuple(bounds for bounds,_,_ in evidence.windows)!=tuple(engine.replay_windows)):
raise ValueError('owned original feedback engine evidence required')
for bounds,subset,activation in evidence.windows:
controls=tuple(frame.control_ns for frame in engine.records if bounds[0]<=frame.control_ns<=bounds[1] and
frame.control_ns not in tuple(row.control_ns for row in evidence.original.context_only))
# Validate the owner seal/digest and exact full window again, never trust an
# instance flag or wire-created VerifiedFeedbackTape-shaped object.
validate_subset_verified(subset,controls,binding_digest=evidence.binding)
if (engine.activation_plan.activation_for(*bounds)[0] or bounds[1]+1)!=activation:
raise ValueError('feedback activation changed')
return evidence
def verify_candidate_engine(engine,requests,reported):
from openpilot.starpilot.flm.feedback_observation import verify_candidate_flags
evidence=require_feedback_engine(engine)
if evidence.candidate_policy != LEGACY_POLICY:
raise ValueError('closed-loop candidate requires its own native chronology proof')
for _bounds,subset,activation in evidence.windows:
selected={row.consumption.control_ns:requests[row.consumption.control_ns] for row in subset.rows}
verify_candidate_flags(subset,selected,reported,activation_ns=activation,binding_digest=evidence.binding)
return True
def owned_feedback_key(engine):
"""New feedback evidence is part of every strict/FD/prefix owned-input key."""
evidence=require_feedback_engine(engine)
key = (PROTOCOL,evidence.binding,evidence.original.digest,
tuple((bounds,subset.digest,activation) for bounds,subset,activation in evidence.windows))
return key if evidence.candidate_policy == LEGACY_POLICY else (*key,evidence.candidate_policy,PROOF_VERSION)