Skip to main content

structured_asic/archive/
check.rs

1//! Exact existing timestamp receipts, independent of historical trust/material.
2
3use super::{Error, Graph, OwnedVec, RangeSource, ResultOf, Work, resource};
4use crate::{references::DigestReading, timestamp};
5use structured_xades::certificate::Agreement;
6
7/// Original timestamp occurrence has no successful-subset projection.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TimestampState {
10    /// This original occurrence has no exact completed receipt.
11    Unchecked,
12    /// Completed common checking did not establish mathematical evidence.
13    Failed,
14    /// Actual common mathematics covers this exact original manifest input.
15    Proven,
16}
17/// Declared source hashes are independent of timestamp mathematics.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ObjectsState {
20    /// Original object instructions were not executed.
21    Unchecked,
22    /// A declared hash or exact whole-input association was negative/incomplete.
23    Failed,
24    /// Every original declared object hash matched its actual source pass.
25    Matches,
26}
27/// One original manifest/TST participant, preserving its complete common result.
28pub struct TimestampCheck<'r, 'd, D, G> {
29    node: usize,
30    receipt: Option<&'r timestamp::Checked<'d, D, G>>,
31    timestamp: TimestampState,
32    objects: ObjectsState,
33}
34impl<'r, 'd, D: Copy, G: Copy> TimestampCheck<'r, 'd, D, G> {
35    /// Original graph node index, never success/date order.
36    #[must_use]
37    pub fn node(&self) -> usize {
38        self.node
39    }
40    /// Actual exact-input result, including all negative common facts.
41    #[must_use]
42    pub fn receipt(&self) -> Option<&'r timestamp::Checked<'d, D, G>> {
43        self.receipt
44    }
45    /// Completed timestamp mathematics only, independent of historical trust.
46    #[must_use]
47    pub fn timestamp(&self) -> TimestampState {
48        self.timestamp
49    }
50    /// Every original XML object declaration, independently of the TST result.
51    #[must_use]
52    pub fn objects(&self) -> ObjectsState {
53        self.objects
54    }
55}
56
57/// Whole original archive census and exact mathematical/input associations.
58/// This is NOT validation-material completeness, historical `PoE`, trusted TSA
59/// status, algorithm longevity, or an attained container/signature B level.
60pub struct Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S: RangeSource> {
61    graph: &'g Graph<'v, 's, N, D, G, S>,
62    checks: OwnedVec<TimestampCheck<'r, 'd, S::Document, S::Generation>>,
63    all_math: bool,
64    all_objects: bool,
65}
66impl<'g, 'r, 'd, 'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource>
67    Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S>
68{
69    /// Exact immutable graph used for this operation.
70    #[must_use]
71    pub fn graph(&self) -> &'g Graph<'v, 's, N, D, G, S> {
72        self.graph
73    }
74    /// EVERY original node, including uninspected/missing/failed participants.
75    #[must_use]
76    pub fn checks(&self) -> &[TimestampCheck<'r, 'd, S::Document, S::Generation>] {
77        &self.checks
78    }
79    /// All actual original TSTs established mathematics on their exact input.
80    #[must_use]
81    pub fn all_timestamp_math(&self) -> bool {
82        self.all_math
83    }
84    /// All original XML declarations were checked and matched actual objects.
85    #[must_use]
86    pub fn all_object_digests(&self) -> bool {
87        self.all_objects
88    }
89}
90
91impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Graph<'v, 's, N, D, G, S> {
92    /// Joins only actual closed common TST receipts, requiring BOTH token and
93    /// consumed-input identities. A separately checked arbitrary manifest or a
94    /// later successful subset cannot fill another original occurrence.
95    pub fn bind_timestamps<'g, 'r, 'd, W: Work>(
96        &'g self,
97        supplied: &[&'r timestamp::Checked<'d, S::Document, S::Generation>],
98        work: &W,
99    ) -> ResultOf<Mathematics<'g, 'r, 'd, 'v, 's, N, D, G, S>, S> {
100        let execution = self.archive.session().query(work);
101        self.archive
102            .admit(&execution, self.archive.directory_span())
103            .map_err(Error::Archive)?;
104        let refusal = |cause| Error::Archive(resource(self.archive.directory_span(), cause));
105        // Validate every supplied occurrence before allocation or selection.
106        for check in supplied {
107            self.archive
108                .original(check.token(), &execution)
109                .map_err(Error::Archive)?;
110            self.archive
111                .original(check.input(), &execution)
112                .map_err(Error::Archive)?;
113        }
114        let mut by_input = execution.map();
115        for (index, check) in supplied.iter().enumerate() {
116            if by_input
117                .try_insert((check.token().ordinal(), check.input().ordinal()), index)
118                .map_err(refusal)?
119                .is_some()
120            {
121                return Err(Error::DuplicateTimestamp(check.token()));
122            }
123        }
124        let mut checks = execution.vector();
125        checks
126            .try_reserve_exact(self.nodes.len())
127            .map_err(refusal)?;
128        let mut all_math = !self.nodes.is_empty();
129        let mut all_objects = !self.nodes.is_empty();
130        for (index, node) in self.nodes.iter().enumerate() {
131            let receipt = match node.timestamp {
132                Some(token) => by_input
133                    .get(&(token.ordinal(), node.member.ordinal()), &execution)
134                    .map_err(refusal)?
135                    .map(|index| supplied[*index]),
136                None => None,
137            };
138            execution.charge_work(1).map_err(refusal)?;
139            let timestamp = match receipt {
140                None => TimestampState::Unchecked,
141                Some(check) => {
142                    if check.common().evidence().is_some() {
143                        TimestampState::Proven
144                    } else {
145                        TimestampState::Failed
146                    }
147                }
148            };
149            let objects = match node
150                .facts
151                .and_then(|index| self.facts[index].object_digests())
152            {
153                None => ObjectsState::Unchecked,
154                Some(answers) => {
155                    let mut matched = !answers.is_empty();
156                    for answer in answers {
157                        execution.charge_work(1).map_err(refusal)?;
158                        if *answer != DigestReading::Checked(Agreement::Matches) {
159                            matched = false;
160                        }
161                    }
162                    if matched {
163                        ObjectsState::Matches
164                    } else {
165                        ObjectsState::Failed
166                    }
167                }
168            };
169            if timestamp != TimestampState::Proven {
170                all_math = false;
171            }
172            if objects != ObjectsState::Matches {
173                all_objects = false;
174            }
175            checks
176                .try_push(TimestampCheck {
177                    node: index,
178                    receipt,
179                    timestamp,
180                    objects,
181                })
182                .map_err(refusal)?;
183        }
184        self.archive
185            .admit(&execution, self.archive.directory_span())
186            .map_err(Error::Archive)?;
187        Ok(Mathematics {
188            graph: self,
189            checks: checks.into_owned(),
190            all_math,
191            all_objects,
192        })
193    }
194}