Skip to main content

holos_tda/distributed/store/
verification.rs

1use crate::{CertificateLimits, RelativeInterfaceCertificate};
2
3use super::super::compose::{
4    check_fold_id, combined_vertices, compose_certificates, decode_certificate, encode_certificate,
5    require_compatible, require_separator,
6};
7use super::super::model::{
8    ArtifactId, DistributedInterfaceError, DistributedInterfaceManifest, DurableInterfaceStore,
9};
10
11impl DurableInterfaceStore {
12    /// Recompute every fold and verify a committed manifest from stored shards.
13    ///
14    /// Ignores durable progress. Checks that every recorded fold and the
15    /// final result follow from the ordered shard artifacts.
16    pub fn verify_manifest(
17        &self,
18        manifest: &DistributedInterfaceManifest,
19        limits: CertificateLimits,
20    ) -> Result<RelativeInterfaceCertificate, DistributedInterfaceError> {
21        let mut accumulator = self.load_manifest_start(manifest, limits)?;
22        self.replay_manifest_folds(manifest, limits, &mut accumulator)?;
23        if accumulator.protected_vertices() != manifest.output_protected_vertices {
24            accumulator =
25                compose_certificates(&[&accumulator], &manifest.output_protected_vertices, limits)?;
26        }
27        self.check_manifest_result(manifest, &accumulator, limits)?;
28        Ok(accumulator)
29    }
30
31    fn load_manifest_start(
32        &self,
33        manifest: &DistributedInterfaceManifest,
34        limits: CertificateLimits,
35    ) -> Result<RelativeInterfaceCertificate, DistributedInterfaceError> {
36        let bytes = self.get(manifest.shards[0], limits.max_bytes)?;
37        let accumulator = decode_certificate(&bytes, limits)?;
38        require_separator(&accumulator, &manifest.separator_vertices)?;
39        check_fold_id(&bytes, manifest.folds[0])?;
40        Ok(accumulator)
41    }
42
43    fn replay_manifest_folds(
44        &self,
45        manifest: &DistributedInterfaceManifest,
46        limits: CertificateLimits,
47        accumulator: &mut RelativeInterfaceCertificate,
48    ) -> Result<(), DistributedInterfaceError> {
49        let intermediate = combined_vertices(
50            &manifest.separator_vertices,
51            &manifest.output_protected_vertices,
52        );
53        for (position, shard) in manifest.shards.iter().enumerate().skip(1) {
54            self.replay_manifest_fold(
55                manifest,
56                limits,
57                &intermediate,
58                accumulator,
59                position,
60                *shard,
61            )?;
62        }
63        Ok(())
64    }
65
66    fn replay_manifest_fold(
67        &self,
68        manifest: &DistributedInterfaceManifest,
69        limits: CertificateLimits,
70        intermediate: &[usize],
71        accumulator: &mut RelativeInterfaceCertificate,
72        position: usize,
73        shard: ArtifactId,
74    ) -> Result<(), DistributedInterfaceError> {
75        let bytes = self.get(shard, limits.max_bytes)?;
76        let child = decode_certificate(&bytes, limits)?;
77        require_compatible(accumulator, &child)?;
78        require_separator(&child, &manifest.separator_vertices)?;
79        *accumulator = compose_certificates(&[accumulator, &child], intermediate, limits)?;
80        check_fold_id(
81            &encode_certificate(accumulator, limits)?,
82            manifest.folds[position],
83        )
84    }
85
86    fn check_manifest_result(
87        &self,
88        manifest: &DistributedInterfaceManifest,
89        accumulator: &RelativeInterfaceCertificate,
90        limits: CertificateLimits,
91    ) -> Result<(), DistributedInterfaceError> {
92        let encoded = encode_certificate(accumulator, limits)?;
93        if ArtifactId::for_bytes(&encoded) != manifest.result {
94            return Err(DistributedInterfaceError::new(
95                "manifest result differs from recomputation",
96            ));
97        }
98        let stored = self.get(manifest.result, limits.max_bytes)?;
99        if stored != encoded {
100            return Err(DistributedInterfaceError::new(
101                "stored result differs from recomputed result",
102            ));
103        }
104        Ok(())
105    }
106}