uts_core/codec/v1/
timestamp.rs1use crate::{
4 alloc::{Allocator, Global, vec, vec::Vec},
5 codec::v1::{
6 Attestation, FinalizationError, MayHaveInput, PendingAttestation,
7 attestation::RawAttestation, opcode::OpCode,
8 },
9 utils::Hexed,
10};
11use allocator_api2::SliceExt;
12use core::fmt::Debug;
13use std::sync::OnceLock;
14
15pub(crate) mod builder;
16mod decode;
17mod encode;
18mod fmt;
19
20#[derive(Clone, Debug)]
38pub enum Timestamp<A: Allocator = Global> {
39 Step(Step<A>),
40 Attestation(RawAttestation<A>),
41}
42
43#[derive(Clone)]
45pub struct Step<A: Allocator = Global> {
46 op: OpCode,
47 data: Vec<u8, A>,
48 input: OnceLock<Vec<u8, A>>,
49 next: Vec<Timestamp<A>, A>,
50}
51
52impl<A: Allocator> PartialEq for Timestamp<A> {
53 fn eq(&self, other: &Self) -> bool {
54 match (self, other) {
55 (Timestamp::Step(s1), Timestamp::Step(s2)) => s1 == s2,
56 (Timestamp::Attestation(a1), Timestamp::Attestation(a2)) => a1 == a2,
57 _ => false,
58 }
59 }
60}
61impl<A: Allocator> Eq for Timestamp<A> {}
62
63impl<A: Allocator> PartialEq for Step<A> {
64 fn eq(&self, other: &Self) -> bool {
65 self.op == other.op && self.data == other.data && self.next == other.next
66 }
67}
68impl<A: Allocator> Eq for Step<A> {}
69
70impl<A: Allocator + Debug> Debug for Step<A> {
71 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
72 let mut f = f.debug_struct("Step");
73 f.field("op", &self.op);
74 if self.op.has_immediate() {
75 f.field("data", &Hexed(&self.data));
76 }
77 f.field("next", &self.next).finish()
78 }
79}
80
81impl Timestamp {
82 pub fn builder() -> builder::TimestampBuilder<Global> {
84 builder::TimestampBuilder::new_in(Global)
85 }
86
87 pub fn merge(timestamps: Vec<Timestamp, Global>) -> Timestamp {
93 Self::merge_in(timestamps, Global)
94 }
95
96 pub fn try_merge(timestamps: Vec<Timestamp, Global>) -> Result<Timestamp, FinalizationError> {
100 Self::try_merge_in(timestamps, Global)
101 }
102}
103
104impl<A: Allocator> Timestamp<A> {
105 pub fn op(&self) -> OpCode {
107 match self {
108 Timestamp::Step(step) => {
109 debug_assert_ne!(
110 step.op,
111 OpCode::ATTESTATION,
112 "sanity check failed: Step with ATTESTATION opcode"
113 );
114 step.op
115 }
116 Timestamp::Attestation(_) => OpCode::ATTESTATION,
117 }
118 }
119
120 #[inline]
122 pub fn as_step(&self) -> Option<&Step<A>> {
123 match self {
124 Timestamp::Step(step) => Some(step),
125 Timestamp::Attestation(_) => None,
126 }
127 }
128
129 #[inline]
131 pub fn as_attestation(&self) -> Option<&RawAttestation<A>> {
132 match self {
133 Timestamp::Attestation(attestation) => Some(attestation),
134 Timestamp::Step(_) => None,
135 }
136 }
137
138 #[inline]
140 pub fn allocator(&self) -> &A {
141 match self {
142 Self::Attestation(attestation) => attestation.allocator(),
143 Self::Step(step) => step.allocator(),
144 }
145 }
146
147 #[inline]
149 pub fn is_finalized(&self) -> bool {
150 self.input().is_some()
151 }
152
153 #[inline]
155 pub fn attestations(&self) -> AttestationIter<'_, A> {
156 AttestationIter { stack: vec![self] }
157 }
158
159 #[inline]
165 pub fn pending_attestations_mut(&mut self) -> PendingAttestationIterMut<'_, A> {
166 PendingAttestationIterMut { stack: vec![self] }
167 }
168}
169
170impl<A: Allocator + Clone> Timestamp<A> {
171 pub fn builder_in(alloc: A) -> builder::TimestampBuilder<A> {
173 builder::TimestampBuilder::new_in(alloc)
174 }
175
176 #[inline]
182 pub fn finalize(&self, input: &[u8]) {
183 self.try_finalize(input)
184 .expect("conflicting inputs when finalizing timestamp")
185 }
186
187 pub fn try_finalize(&self, input: &[u8]) -> Result<(), FinalizationError> {
191 let init_fn = || SliceExt::to_vec_in(input, self.allocator().clone());
192 match self {
193 Self::Attestation(attestation) => {
194 if let Some(already) = attestation.value.get() {
195 return if input != already {
196 Err(FinalizationError)
197 } else {
198 Ok(())
199 };
200 }
201 let _ = attestation.value.get_or_init(init_fn);
202 }
203 Self::Step(step) => {
204 if let Some(already) = step.input.get() {
205 return if input != already {
206 Err(FinalizationError)
207 } else {
208 Ok(())
209 };
210 }
211 let input = step.input.get_or_init(init_fn);
212
213 match step.op {
214 OpCode::FORK => {
215 debug_assert!(step.next.len() >= 2, "FORK must have at least two children");
216 for child in &step.next {
217 child.try_finalize(input)?;
218 }
219 }
220 OpCode::ATTESTATION => unreachable!("should not happen"),
221 op => {
222 let output = op.execute_in(input, &step.data, step.allocator().clone());
223 debug_assert!(step.next.len() == 1, "non-FORK must have exactly one child");
224 step.next[0].try_finalize(&output)?;
225 }
226 }
227 }
228 }
229 Ok(())
230 }
231
232 pub fn merge_in(timestamps: Vec<Timestamp<A>, A>, alloc: A) -> Timestamp<A> {
238 Self::try_merge_in(timestamps, alloc).expect("conflicting inputs when merging timestamps")
239 }
240
241 pub fn try_merge_in(
247 timestamps: Vec<Timestamp<A>, A>,
248 alloc: A,
249 ) -> Result<Timestamp<A>, FinalizationError> {
250 let finalized_input = timestamps.iter().find_map(|ts| ts.input());
253 if let Some(input) = finalized_input {
254 for ts in timestamps.iter().filter(|ts| !ts.is_finalized()) {
255 ts.try_finalize(input)?;
256 }
257 }
258
259 Ok(Timestamp::Step(Step {
260 op: OpCode::FORK,
261 data: Vec::new_in(alloc.clone()),
262 input: OnceLock::new(),
263 next: timestamps,
264 }))
265 }
266}
267
268impl<A: Allocator> MayHaveInput for Timestamp<A> {
269 #[inline]
270 fn input(&self) -> Option<&[u8]> {
271 match self {
272 Timestamp::Step(step) => step.input(),
273 Timestamp::Attestation(attestation) => attestation.input(),
274 }
275 }
276}
277
278impl<A: Allocator> Step<A> {
279 pub fn op(&self) -> OpCode {
281 self.op
282 }
283
284 pub fn data(&self) -> &[u8] {
286 self.data.as_slice()
287 }
288
289 pub fn next(&self) -> &[Timestamp<A>] {
291 self.next.as_slice()
292 }
293
294 pub fn next_mut(&mut self) -> &mut [Timestamp<A>] {
296 self.next.as_mut_slice()
297 }
298
299 pub fn allocator(&self) -> &A {
301 self.data.allocator()
302 }
303}
304
305impl<A: Allocator> MayHaveInput for Step<A> {
306 #[inline]
307 fn input(&self) -> Option<&[u8]> {
308 self.input.get().map(|v| v.as_slice())
309 }
310}
311
312#[must_use = "AttestationIter is an iterator, it does nothing unless consumed"]
313pub struct AttestationIter<'a, A: Allocator> {
314 stack: Vec<&'a Timestamp<A>>,
315}
316
317impl<'a, A: Allocator> Iterator for AttestationIter<'a, A> {
318 type Item = &'a RawAttestation<A>;
319
320 fn next(&mut self) -> Option<Self::Item> {
321 while let Some(ts) = self.stack.pop() {
322 match ts {
323 Timestamp::Step(step) => {
324 for next in step.next().iter().rev() {
325 self.stack.push(next);
326 }
327 }
328 Timestamp::Attestation(attestation) => return Some(attestation),
329 }
330 }
331 None
332 }
333}
334
335pub struct PendingAttestationIterMut<'a, A: Allocator> {
336 stack: Vec<&'a mut Timestamp<A>>,
337}
338
339impl<'a, A: Allocator> Iterator for PendingAttestationIterMut<'a, A> {
340 type Item = &'a mut Timestamp<A>;
341
342 fn next(&mut self) -> Option<Self::Item> {
343 while let Some(ts) = self.stack.pop() {
344 match ts {
345 Timestamp::Step(step) => {
346 for next in step.next_mut().iter_mut().rev() {
347 self.stack.push(next);
348 }
349 }
350 Timestamp::Attestation(attestation) => {
351 if attestation.tag == PendingAttestation::TAG {
352 return Some(ts);
353 }
354 }
355 }
356 }
357 None
358 }
359}