Skip to main content

ic_backup/model/ic_update_response/
mod.rs

1//! Shared passive IC update-response fields, bounded admission and redacted formatting.
2
3use crate::model::{
4    artifacts::ArtifactChecksumRecord, attempt_journal::MAX_OPERATION_ATTEMPTS,
5    operation_plan::PlanContextRecord,
6};
7use std::fmt;
8
9/// Passive provider fields; actual authentication and association are integration-owned.
10#[derive(Clone)]
11pub struct IcUpdateResponseInput {
12    /// Full original authority digest, including plan/context/operation/limits.
13    pub authority: ArtifactChecksumRecord,
14    /// Original already reserved mutation attempt, never a new call number.
15    pub mutation_attempt: u32,
16    /// Actual authenticated network/caller/release claimed by the provider.
17    pub context: PlanContextRecord,
18    /// Actual response routing target claimed by the provider; canonicalized on admission.
19    pub target: String,
20    /// Exact raw Candid reply, retained under the existing codec byte ceilings.
21    pub reply: Vec<u8>,
22    /// Opaque provider evidence binding original request/attempt and all actual fields.
23    pub evidence: ArtifactChecksumRecord,
24}
25
26pub(crate) enum UpdateResponseAdmissionError {
27    InvalidAttempt,
28    ReplyTooLarge,
29    InvalidTarget,
30}
31
32impl IcUpdateResponseInput {
33    pub(crate) fn admit(
34        &mut self,
35        max_reply_bytes: usize,
36    ) -> Result<(), UpdateResponseAdmissionError> {
37        if self.mutation_attempt == 0 || self.mutation_attempt > MAX_OPERATION_ATTEMPTS {
38            return Err(UpdateResponseAdmissionError::InvalidAttempt);
39        }
40        if self.reply.len() > max_reply_bytes {
41            return Err(UpdateResponseAdmissionError::ReplyTooLarge);
42        }
43        self.target = crate::model::principal::canonical_text(&self.target)
44            .ok_or(UpdateResponseAdmissionError::InvalidTarget)?;
45        Ok(())
46    }
47
48    pub(crate) fn fmt_redacted(
49        &self,
50        formatter: &mut fmt::Formatter<'_>,
51        name: &str,
52    ) -> fmt::Result {
53        formatter
54            .debug_struct(name)
55            .field("authority", &self.authority)
56            .field("mutation_attempt", &self.mutation_attempt)
57            .field("target", &self.target)
58            .field("reply_bytes", &self.reply.len())
59            .finish_non_exhaustive()
60    }
61}
62
63#[cfg(test)]
64mod tests;