Skip to main content

aven_protocol/artifact/
staging.rs

1//! Structural staging views and component-bound retained-byte verification.
2//! These checks do not establish current membership, expiry or server acceptance.
3
4use super::catalog::{Artifact, Images};
5use super::{Descriptor, Error, Result, catalog, decode_state_catalog};
6use crate::context::LocalSharedStatePackageContext;
7use crate::wire::bootstrap::Component;
8
9pub struct DeclarationView(Descriptor);
10
11pub struct Binding {
12    pub vault: [u8; 32],
13    pub generation: [u8; 32],
14    pub bootstrap: [u8; 32],
15    pub stream: [u8; 32],
16    pub membership: [u8; 32],
17}
18
19impl DeclarationView {
20    pub fn decode(bytes: &[u8]) -> Result<Self> {
21        Descriptor::decode(bytes).map(Self)
22    }
23
24    pub fn prefix_count(&self) -> u64 {
25        self.0.prefix
26    }
27
28    pub fn prefix_rows(&self, bytes: &[u8]) -> Result<Vec<(u64, String)>> {
29        self.catalog(1, bytes)?;
30        catalog::prefix_decode(bytes, self.0.prefix)
31    }
32
33    pub fn image_rows(&self, bytes: &[u8]) -> Result<Images> {
34        self.catalog(2, bytes)?;
35        Images::decode(bytes)
36    }
37
38    pub fn binding(&self) -> Binding {
39        let d = &self.0;
40        Binding {
41            vault: d.vault,
42            generation: d.generation,
43            bootstrap: d.bootstrap,
44            stream: d.stream,
45            membership: d.membership,
46        }
47    }
48
49    pub fn catalog_lengths(&self, class: usize) -> Result<Vec<u64>> {
50        Ok(self
51            .0
52            .catalogs
53            .get(class)
54            .ok_or(Error::Invalid)?
55            .slice_lengths())
56    }
57
58    pub fn verify_slice(&self, class: usize, index: usize, bytes: &[u8]) -> Result<()> {
59        self.0
60            .catalogs
61            .get(class)
62            .ok_or(Error::Invalid)?
63            .verify_slice(index, bytes)
64    }
65
66    pub fn catalog(&self, class: usize, bytes: &[u8]) -> Result<CatalogView> {
67        self.0
68            .catalogs
69            .get(class)
70            .ok_or(Error::Invalid)?
71            .verify(bytes, class as u8 + 1)?;
72        let catalog = match class {
73            0 => Ok(Catalog::State(decode_state_catalog(bytes)?)),
74            1 => {
75                catalog::prefix_decode(bytes, self.0.prefix)?;
76                Ok(Catalog::Prefix)
77            }
78            2 => Ok(Catalog::Images(Images::decode(bytes)?)),
79            _ => Err(Error::Invalid),
80        }?;
81        Ok(CatalogView(catalog))
82    }
83
84    pub fn artifacts(&self, catalog: Option<&CatalogView>) -> Vec<ArtifactView> {
85        let d = &self.0;
86        let wrap = |component, artifact: Artifact| ArtifactView {
87            component,
88            artifact,
89            context: d.context(),
90            stream: d.stream,
91            bootstrap: d.bootstrap,
92        };
93        match catalog.map(|c| &c.0) {
94            None => vec![wrap(Component::Manifest, d.manifest.clone())],
95            Some(Catalog::State(a)) => vec![wrap(Component::State, a.clone())],
96            Some(Catalog::Images(images)) => images
97                .objects
98                .iter()
99                .map(|i| wrap(Component::Image(i.id), i.artifact.clone()))
100                .collect(),
101            Some(Catalog::Prefix) => Vec::new(),
102        }
103    }
104}
105
106pub struct CatalogView(Catalog);
107
108enum Catalog {
109    State(Artifact),
110    Prefix,
111    Images(Images),
112}
113
114pub struct ArtifactView {
115    /// Structural routing selector, not proof of server admission.
116    pub component: Component,
117    artifact: Artifact,
118    context: LocalSharedStatePackageContext,
119    stream: [u8; 32],
120    bootstrap: [u8; 32],
121}
122
123impl ArtifactView {
124    pub fn lengths(&self) -> Vec<u64> {
125        self.artifact.chunks.iter().map(|c| c.length).collect()
126    }
127
128    /// Exact descriptor recipe; callers must bind it to current stored inputs.
129    pub fn artifact(&self) -> &Artifact {
130        &self.artifact
131    }
132
133    pub fn context(&self) -> LocalSharedStatePackageContext {
134        self.context
135    }
136
137    pub fn stream(&self) -> [u8; 32] {
138        self.stream
139    }
140
141    /// Record identity, family and class used by the chunk-header verifier.
142    pub fn identity(&self) -> Result<([u8; 32], u8, u8)> {
143        match self.component {
144            Component::Image(id) => Ok((id, 1, 0)),
145            Component::State => Ok((self.bootstrap, 2, 1)),
146            Component::Manifest => Ok((self.bootstrap, 2, 2)),
147            _ => Err(Error::Invalid),
148        }
149    }
150
151    pub fn verify_chunk(&self, index: usize, bytes: &[u8]) -> Result<()> {
152        let (id, family, class) = self.identity()?;
153        self.artifact
154            .verify_chunk(bytes, index, self.context, self.stream, id, family, class)
155    }
156
157    pub fn verify(&self, records: &[Vec<u8>]) -> Result<()> {
158        let (id, family, class) = self.identity()?;
159        self.artifact
160            .verify(records, self.context, self.stream, id, family, class)
161    }
162}
163
164#[cfg(test)]
165mod tests;