Skip to main content

uor_addr/
outcome.rs

1//! Pipeline-output carrier — the shape every realization's `address()`
2//! entry point returns.
3//!
4//! All realizations produce the same triple: the ASCII κ-label
5//! ([`crate::KappaLabel`]) plus a replayable TC-05 witness. The κ-label
6//! width `N` is the selected σ-axis's [`AddrHash::LABEL_BYTES`](crate::hash::AddrHash::LABEL_BYTES)
7//! (71 sha256/blake3, 73 sha3-256, 74 keccak256, 135 sha512) and the
8//! fingerprint width `FP` is the axis's `FINGERPRINT_MAX_BYTES` (32, or 64
9//! for sha512) — both carried in the type. Under ADR-060 the model's
10//! `forward()` yields a `Grounded<'a, S, NIN, FP>` whose `'a` borrows the
11//! input carrier; [`AddressOutcome`] extracts the **owned** witness data
12//! (the κ-label, the replay [`Trace`], and the σ-projection fingerprint)
13//! from it so the outcome carries no borrow and the input may be dropped.
14
15use prism::replay::{certify_from_trace, Trace};
16use prism::seal::Grounded;
17
18use crate::label::{KappaLabel, LabelDecodeError};
19
20/// Trace event capacity for the address realizations. The κ-derivation's
21/// canonical k-invariants branch emits a short, bounded trace; 256 is the
22/// foundation's default `Trace` width (`HostBounds::TRACE_MAX_EVENTS`).
23pub const ADDRESS_TRACE_EVENTS: usize = 256;
24
25/// **The result of a successful `address()` invocation.** Generic over the
26/// κ-label byte width `N` and the fingerprint byte width `FP` (the selected
27/// σ-axis's `LABEL_BYTES` / `FINGERPRINT_MAX_BYTES`; `FP` defaults to 32).
28#[derive(Debug)]
29pub struct AddressOutcome<const N: usize, const FP: usize = 32> {
30    /// The replayable TC-05 witness (owns its trace + fingerprint).
31    pub witness: AddressWitness<N, FP>,
32    /// The ASCII wire-format κ-label, `<algorithm>:<lowercase-hex>`.
33    pub address: KappaLabel<N>,
34}
35
36impl<const N: usize, const FP: usize> AddressOutcome<N, FP> {
37    /// Extract the owned outcome from a model's `forward()` result. Reads
38    /// the κ-label output bytes, replays the derivation into an owned
39    /// [`Trace`], and snapshots the σ-projection fingerprint — none of
40    /// which borrow the (about-to-be-dropped) input carrier.
41    ///
42    /// `N` must equal the grounded output shape's `SITE_COUNT` (the κ-label
43    /// byte width); the per-axis entry points supply the matching literal.
44    ///
45    /// # Errors
46    ///
47    /// [`LabelDecodeError`] if the grounded output is not a well-formed
48    /// `N`-byte ASCII κ-label (unreachable for the address realizations'
49    /// ψ₉ output; defensive against substrate corruption).
50    pub fn from_grounded<S, const NIN: usize>(
51        grounded: &Grounded<'_, S, NIN, FP>,
52    ) -> Result<Self, LabelDecodeError>
53    where
54        S: prism::std_types::GroundedShape,
55    {
56        let address = KappaLabel::<N>::from_bytes(grounded.output_bytes())?;
57        let trace: Trace<ADDRESS_TRACE_EVENTS, FP> = grounded.derivation().replay();
58        let mut fingerprint = [0u8; FP];
59        let fp = grounded.content_fingerprint();
60        let fp_bytes = fp.as_bytes();
61        let n = fp_bytes.len().min(FP);
62        fingerprint[..n].copy_from_slice(&fp_bytes[..n]);
63        Ok(Self {
64            witness: AddressWitness {
65                address,
66                trace,
67                fingerprint,
68            },
69            address,
70        })
71    }
72}
73
74/// A replayable TC-05 witness. Holds the owned replay [`Trace`], the
75/// σ-projection fingerprint, and the κ-label. [`verify`](Self::verify)
76/// re-certifies the trace through `prism::replay::certify_from_trace`
77/// without re-invoking the σ-axis, and confirms the re-derived
78/// fingerprint equals the source's (QS-05 replay equivalence).
79pub struct AddressWitness<const N: usize, const FP: usize = 32> {
80    address: KappaLabel<N>,
81    trace: Trace<ADDRESS_TRACE_EVENTS, FP>,
82    fingerprint: [u8; FP],
83}
84
85impl<const N: usize, const FP: usize> AddressWitness<N, FP> {
86    /// The κ-label this witness attests.
87    #[must_use]
88    pub fn kappa_label(&self) -> KappaLabel<N> {
89        self.address
90    }
91
92    /// The `FP`-byte σ-projection content fingerprint (32 for the
93    /// `Hasher<32>` axes, 64 for sha512).
94    #[must_use]
95    pub fn content_fingerprint(&self) -> &[u8; FP] {
96        &self.fingerprint
97    }
98
99    /// Replay the derivation through `certify_from_trace` (no σ-axis
100    /// re-invocation) and confirm the re-derived fingerprint matches.
101    /// Returns the attested κ-label on success.
102    ///
103    /// # Errors
104    ///
105    /// [`VerifyError`] if the trace is malformed or the re-derived
106    /// fingerprint diverges from the source (QS-05 violation).
107    pub fn verify(&self) -> Result<KappaLabel<N>, VerifyError> {
108        let certified = certify_from_trace(&self.trace).map_err(|_| VerifyError::ReplayFailed)?;
109        if certified.certificate().content_fingerprint().as_bytes()[..] != self.fingerprint[..] {
110            return Err(VerifyError::FingerprintMismatch);
111        }
112        Ok(self.address)
113    }
114}
115
116impl<const N: usize, const FP: usize> core::fmt::Debug for AddressWitness<N, FP> {
117    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        f.debug_struct("AddressWitness")
119            .field("address", &self.address)
120            .finish_non_exhaustive()
121    }
122}
123
124/// TC-05 replay-verification failures from [`AddressWitness::verify`].
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum VerifyError {
127    /// `certify_from_trace` rejected the trace (malformed / out-of-order).
128    ReplayFailed,
129    /// The re-derived fingerprint diverged from the source (QS-05).
130    FingerprintMismatch,
131}