1use super::{Error, Graph, OwnedVec, RangeSource, ResultOf, Work, resource};
4use crate::{references::DigestReading, timestamp};
5use structured_xades::certificate::Agreement;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TimestampState {
10 Unchecked,
12 Failed,
14 Proven,
16}
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ObjectsState {
20 Unchecked,
22 Failed,
24 Matches,
26}
27pub 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 #[must_use]
37 pub fn node(&self) -> usize {
38 self.node
39 }
40 #[must_use]
42 pub fn receipt(&self) -> Option<&'r timestamp::Checked<'d, D, G>> {
43 self.receipt
44 }
45 #[must_use]
47 pub fn timestamp(&self) -> TimestampState {
48 self.timestamp
49 }
50 #[must_use]
52 pub fn objects(&self) -> ObjectsState {
53 self.objects
54 }
55}
56
57pub 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 #[must_use]
71 pub fn graph(&self) -> &'g Graph<'v, 's, N, D, G, S> {
72 self.graph
73 }
74 #[must_use]
76 pub fn checks(&self) -> &[TimestampCheck<'r, 'd, S::Document, S::Generation>] {
77 &self.checks
78 }
79 #[must_use]
81 pub fn all_timestamp_math(&self) -> bool {
82 self.all_math
83 }
84 #[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 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 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}