Skip to main content

aven_protocol/claim/membership/
evidence.rs

1//! Bounded public transition evidence, without server or protected-store authority.
2use super::*;
3use crate::base64_bytes;
4use serde::{Deserialize, Serialize};
5
6/// Base64 of the whole chain, up to one padding group per byte field, and
7/// JSON framing for every transition.
8pub const MAX_EVIDENCE_JSON_BYTES: usize =
9    base64_bytes::encoded_len(MAX_CHAIN_BYTES) + 4 * (3 * MAX_TRANSITIONS + 3) + 16384;
10
11#[derive(Clone, Serialize, Deserialize)]
12#[serde(deny_unknown_fields)]
13pub struct EvidenceRecord {
14    #[serde(
15        serialize_with = "base64_bytes::serialize",
16        deserialize_with = "declaration_bytes"
17    )]
18    pub declaration: Vec<u8>,
19    #[serde(
20        serialize_with = "base64_bytes::serialize",
21        deserialize_with = "request_bytes"
22    )]
23    pub request: Vec<u8>,
24    #[serde(
25        serialize_with = "base64_bytes::serialize",
26        deserialize_with = "record_bytes"
27    )]
28    pub record: Vec<u8>,
29}
30#[derive(Clone, Serialize, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct Evidence {
33    #[serde(
34        serialize_with = "base64_bytes::serialize",
35        deserialize_with = "genesis_bytes"
36    )]
37    pub genesis: Vec<u8>,
38    #[serde(
39        serialize_with = "base64_bytes::serialize",
40        deserialize_with = "publication_bytes"
41    )]
42    pub publication: Vec<u8>,
43    #[serde(
44        serialize_with = "base64_bytes::serialize",
45        deserialize_with = "descriptor_bytes"
46    )]
47    pub descriptor: Vec<u8>,
48    #[serde(deserialize_with = "records")]
49    pub transitions: Vec<EvidenceRecord>,
50}
51fn record_bytes<'de, D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Vec<u8>, D::Error> {
52    base64_bytes::bounded::<D, MAX_RECORD_BYTES>(d)
53}
54fn declaration_bytes<'de, D: serde::Deserializer<'de>>(
55    d: D,
56) -> std::result::Result<Vec<u8>, D::Error> {
57    base64_bytes::bounded::<D, DECLARATION_BYTES>(d)
58}
59fn request_bytes<'de, D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Vec<u8>, D::Error> {
60    base64_bytes::bounded::<D, REQUEST_BYTES>(d)
61}
62fn genesis_bytes<'de, D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Vec<u8>, D::Error> {
63    base64_bytes::bounded::<D, GENESIS_BYTES>(d)
64}
65fn publication_bytes<'de, D: serde::Deserializer<'de>>(
66    d: D,
67) -> std::result::Result<Vec<u8>, D::Error> {
68    base64_bytes::bounded::<D, PUBLICATION_BYTES>(d)
69}
70fn descriptor_bytes<'de, D: serde::Deserializer<'de>>(
71    d: D,
72) -> std::result::Result<Vec<u8>, D::Error> {
73    base64_bytes::bounded::<D, { crate::artifact::codec::MAX_DESCRIPTOR_BYTES }>(d)
74}
75fn records<'de, D: serde::Deserializer<'de>>(
76    d: D,
77) -> std::result::Result<Vec<EvidenceRecord>, D::Error> {
78    struct Records;
79    impl<'de> serde::de::Visitor<'de> for Records {
80        type Value = Vec<EvidenceRecord>;
81        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82            f.write_str("bounded transition evidence")
83        }
84        fn visit_seq<A: serde::de::SeqAccess<'de>>(
85            self,
86            mut seq: A,
87        ) -> std::result::Result<Self::Value, A::Error> {
88            if seq.size_hint().is_some_and(|n| n > MAX_TRANSITIONS) {
89                return Err(serde::de::Error::custom("membership-limit"));
90            }
91            let mut result = Vec::new();
92            let mut total = 0usize;
93            while let Some(record) = seq.next_element::<EvidenceRecord>()? {
94                total += record.declaration.len() + record.request.len() + record.record.len();
95                if result.len() == MAX_TRANSITIONS || total > MAX_CHAIN_BYTES {
96                    return Err(serde::de::Error::custom("membership-limit"));
97                }
98                result.push(record);
99            }
100            Ok(result)
101        }
102    }
103    d.deserialize_seq(Records)
104}
105impl Evidence {
106    /// Verify the complete chain once, returning the exact original
107    /// enrollment and the chain's current membership.
108    pub fn enrollment(
109        &self,
110        peer: &Joiner,
111        outcome: Hash,
112    ) -> Result<(VerifiedEnrollment, Membership)> {
113        let mut enrollment = None;
114        let current = self.replay(|before, a| {
115            if enrollment.is_none() && hash(&a.record) == outcome {
116                enrollment = Some(peer.verify_enrollment(before, &a.declaration, &a.record)?);
117            }
118            Ok(())
119        })?;
120        let verified = enrollment.context("error enrollment-outcome-missing")?;
121        ensure!(
122            current.extends(verified.membership()),
123            "error membership-fork"
124        );
125        Ok((verified, current))
126    }
127    /// Parse and verify, returning the membership the chain proves.
128    pub fn decode(bytes: &[u8]) -> Result<(Self, Membership)> {
129        let evidence = Self::decode_unverified(bytes)?;
130        let membership = evidence.verify()?;
131        Ok((evidence, membership))
132    }
133    /// Parse within bounds only; the caller must verify before use.
134    pub fn decode_unverified(bytes: &[u8]) -> Result<Self> {
135        ensure!(
136            bytes.len() <= MAX_EVIDENCE_JSON_BYTES,
137            "error membership-limit"
138        );
139        serde_json::from_slice(bytes).map_err(|_| anyhow::anyhow!("error membership-evidence"))
140    }
141    pub fn verify(&self) -> Result<Membership> {
142        self.replay(|_, _| Ok(()))
143    }
144    /// One bounded replay of the chain; `visit` sees each transition with the
145    /// verified membership before it.
146    fn replay(
147        &self,
148        mut visit: impl FnMut(&Membership, &EvidenceRecord) -> Result<()>,
149    ) -> Result<Membership> {
150        ensure!(
151            self.transitions.len() <= MAX_TRANSITIONS,
152            "error membership-limit"
153        );
154        let mut size = self
155            .genesis
156            .len()
157            .checked_add(self.publication.len())
158            .and_then(|n| n.checked_add(self.descriptor.len()))
159            .context("error membership-limit")?;
160        for a in &self.transitions {
161            ensure!(
162                (a.declaration.len() == DECLARATION_BYTES && a.request.len() == REQUEST_BYTES
163                    || a.declaration.is_empty() && a.request.is_empty())
164                    && a.record.len() <= MAX_RECORD_BYTES,
165                "error membership-limit"
166            );
167            size = size
168                .checked_add(a.declaration.len())
169                .and_then(|n| n.checked_add(a.request.len()))
170                .and_then(|n| n.checked_add(a.record.len()))
171                .context("error membership-limit")?;
172        }
173        ensure!(size <= MAX_CHAIN_BYTES, "error membership-limit");
174        let genesis = Genesis::from_record(&self.genesis)?;
175        let mut m = Membership::from_publication(&genesis, &self.descriptor, &self.publication)?;
176        for a in &self.transitions {
177            visit(&m, a)?;
178            m = m.append(&a.declaration, &a.request, &a.record)?;
179        }
180        Ok(m)
181    }
182}
183#[derive(Clone, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct Mailbox {
186    #[serde(
187        serialize_with = "base64_bytes::serialize",
188        deserialize_with = "declaration_bytes"
189    )]
190    pub declaration: Vec<u8>,
191    #[serde(with = "base64_bytes::option")]
192    pub request: Option<Vec<u8>>,
193    #[serde(with = "base64_bytes::option")]
194    pub admission: Option<Vec<u8>>,
195}