Skip to main content

holos_tda/program_trace/
artifact.rs

1use crate::{
2    CertificateLimits, ProgramArtifact, ProgramUpdateMode, RipsParams, SparseDistanceMatrix,
3};
4
5use super::codec::{Reader, program_artifact_error};
6use super::envelope::{
7    STEP_MINIMUM_BYTES, check_count_bytes, check_envelope_size, check_no_trailing_bytes,
8    decode_program_artifact, decode_trace_header, decode_trace_steps, encode_checkpoints,
9    encode_program_artifact, encode_trace_header, encode_trace_step,
10};
11use super::model::{
12    ProgramTraceArtifact, ProgramTraceDecodeLimits, ProgramTraceError, ProgramTraceStep,
13    TraceTotals, VerifiedProgramTrace,
14};
15use super::records::{decode_graph, encode_graph};
16use super::replay::{check_replayed_step, check_step_shape, replay_step, verify_program_artifact};
17
18impl ProgramTraceArtifact {
19    /// Produce a trace from an initial graph and updated graphs.
20    pub fn build(
21        initial: &SparseDistanceMatrix,
22        updates: &[SparseDistanceMatrix],
23        params: &RipsParams,
24        certificate_limits: CertificateLimits,
25    ) -> std::result::Result<Self, ProgramTraceError> {
26        let (initial_program, mut program) =
27            ProgramArtifact::compile(initial, params, certificate_limits)
28                .map_err(program_artifact_error)?;
29        let mut steps = Vec::with_capacity(updates.len());
30        for graph in updates {
31            let update = program
32                .advance(graph)
33                .map_err(|error| ProgramTraceError::new(error.to_string()))?;
34            let checkpoint = (update.mode != ProgramUpdateMode::Reused)
35                .then(|| ProgramArtifact::capture(graph, &program))
36                .transpose()
37                .map_err(program_artifact_error)?;
38            steps.push(ProgramTraceStep {
39                graph: graph.clone(),
40                mode: update.mode,
41                work: update.work,
42                events: update.events,
43                continuation: update.continuation,
44                correspondence: update.correspondence,
45                diagram: update.result.diagram,
46                checkpoint,
47            });
48        }
49        Ok(Self {
50            initial_graph: initial.clone(),
51            initial_program,
52            steps,
53        })
54    }
55
56    /// Initial graph embedded in the trace.
57    pub fn initial_graph(&self) -> &SparseDistanceMatrix {
58        &self.initial_graph
59    }
60
61    /// Initial program artifact.
62    pub fn initial_program(&self) -> &ProgramArtifact {
63        &self.initial_program
64    }
65
66    /// Ordered update steps.
67    pub fn steps(&self) -> &[ProgramTraceStep] {
68        &self.steps
69    }
70
71    /// Encode the canonical `HOLOSDLT` version 2 envelope.
72    pub fn encode(&self) -> std::result::Result<Vec<u8>, ProgramTraceError> {
73        let initial_program = encode_program_artifact(&self.initial_program)?;
74        let checkpoints = encode_checkpoints(&self.steps)?;
75        let mut out = Vec::new();
76        encode_trace_header(&mut out, self.steps.len(), initial_program.len())?;
77        encode_graph(&mut out, &self.initial_graph)?;
78        out.extend_from_slice(&initial_program);
79        for (step, checkpoint) in self.steps.iter().zip(checkpoints) {
80            encode_trace_step(&mut out, step, checkpoint.as_deref())?;
81        }
82        Ok(out)
83    }
84
85    /// Decode and structurally validate a bounded program trace.
86    pub fn decode(
87        bytes: &[u8],
88        limits: ProgramTraceDecodeLimits,
89        certificate_limits: CertificateLimits,
90    ) -> std::result::Result<Self, ProgramTraceError> {
91        check_envelope_size(bytes, limits.max_bytes)?;
92        let mut reader = Reader::new(bytes);
93        let header = decode_trace_header(&mut reader, limits)?;
94        let mut total_edges = 0usize;
95        let initial_graph = decode_graph(&mut reader, limits, &mut total_edges)?;
96        let initial_program = decode_program_artifact(
97            &mut reader,
98            header.initial_program_bytes,
99            limits,
100            certificate_limits,
101        )?;
102        let mut totals = TraceTotals {
103            checkpoint_bytes: header.initial_program_bytes,
104            ..TraceTotals::default()
105        };
106        check_count_bytes(
107            &reader,
108            header.step_count,
109            STEP_MINIMUM_BYTES,
110            "trace step records",
111        )?;
112        let steps = decode_trace_steps(
113            &mut reader,
114            header.step_count,
115            limits,
116            certificate_limits,
117            initial_program.modulus(),
118            &mut totals,
119            &mut total_edges,
120        )?;
121        check_no_trailing_bytes(&reader)?;
122        Ok(Self {
123            initial_graph,
124            initial_program,
125            steps,
126        })
127    }
128
129    /// Verify every reused step and every checkpoint.
130    pub fn verify(
131        &self,
132        certificate_limits: CertificateLimits,
133    ) -> std::result::Result<VerifiedProgramTrace, ProgramTraceError> {
134        let mut program = verify_program_artifact(
135            &self.initial_program,
136            &self.initial_graph,
137            certificate_limits,
138        )?;
139        let initial_result = program.result().clone();
140        let mut verified_steps = Vec::with_capacity(self.steps.len());
141        for (index, step) in self.steps.iter().enumerate() {
142            check_step_shape(step)?;
143            let checked = replay_step(&mut program, step, index, certificate_limits)?;
144            check_replayed_step(&checked, step, index)?;
145            verified_steps.push(checked.into());
146        }
147        Ok(VerifiedProgramTrace {
148            initial_result,
149            steps: verified_steps,
150            final_program: program,
151        })
152    }
153}