ic_backup/model/application_ingress/payload/
mod.rs1use super::ApplicationIngressError;
4use crate::model::{
5 artifacts::ArtifactChecksumRecord, attempt_journal::OperationBindingRecord,
6 evidence_digest::EvidenceDigest,
7};
8use std::fmt;
9
10pub const MAX_APPLICATION_INGRESS_ARGUMENT_BYTES: usize = 1024 * 1024;
12pub const MAX_APPLICATION_INGRESS_METHOD_BYTES: usize = 128;
14pub const MAX_APPLICATION_INGRESS_REPLY_BYTES: usize = 1024 * 1024;
16
17#[derive(Clone)]
25pub struct ApplicationIngressPayload {
26 receiver: String,
27 target: String,
28 receiver_bytes: Vec<u8>,
29 target_bytes: Vec<u8>,
30 method: String,
31 arguments: Vec<u8>,
32 reply_limit: usize,
33}
34
35impl ApplicationIngressPayload {
36 pub fn new(
40 receiver: &str,
41 target: &str,
42 method: &str,
43 arguments: &[u8],
44 reply_limit: usize,
45 ) -> Result<Self, ApplicationIngressError> {
46 crate::model::application_payload::validate_bytes(
47 arguments,
48 MAX_APPLICATION_INGRESS_ARGUMENT_BYTES,
49 method,
50 MAX_APPLICATION_INGRESS_METHOD_BYTES,
51 )?;
52 if !(1..=MAX_APPLICATION_INGRESS_REPLY_BYTES).contains(&reply_limit) {
53 return Err(ApplicationIngressError::InvalidReplyLimit);
54 }
55 let (receiver, receiver_bytes) = crate::model::application_payload::principal(receiver)?;
56 let (target, target_bytes) = crate::model::application_payload::principal(target)?;
57 Ok(Self {
58 receiver,
59 target,
60 receiver_bytes,
61 target_bytes,
62 method: method.into(),
63 arguments: arguments.into(),
64 reply_limit,
65 })
66 }
67 #[must_use]
69 pub fn receiver(&self) -> &str {
70 &self.receiver
71 }
72 #[must_use]
74 pub fn target(&self) -> &str {
75 &self.target
76 }
77 #[must_use]
79 pub fn method(&self) -> &str {
80 &self.method
81 }
82 #[must_use]
84 pub fn arguments(&self) -> &[u8] {
85 &self.arguments
86 }
87 #[must_use]
89 pub const fn reply_limit(&self) -> usize {
90 self.reply_limit
91 }
92 #[must_use]
95 pub fn digest(&self) -> ArtifactChecksumRecord {
96 let mut framing = EvidenceDigest::new(b"ic-backup/application-ingress/v1\0");
97 for principal in [&self.receiver_bytes, &self.target_bytes] {
98 framing.bytes_u8(principal);
99 }
100 framing.byte(1);
101 for bytes in [self.method.as_bytes(), self.arguments.as_slice()] {
102 framing.bytes_u64(bytes);
103 }
104 framing.u64_be(self.reply_limit as u64);
105 framing.finish()
106 }
107 pub fn validate_mutation_binding(
111 &self,
112 binding: &OperationBindingRecord,
113 ) -> Result<(), ApplicationIngressError> {
114 self.validate_target(binding)?;
115 if self.digest().hash() != binding.request() {
116 return Err(ApplicationIngressError::PayloadMismatch);
117 }
118 Ok(())
119 }
120 pub fn validate_observation_binding(
127 &self,
128 binding: &OperationBindingRecord,
129 ) -> Result<(), ApplicationIngressError> {
130 self.validate_target(binding)
131 }
132 fn validate_target(
133 &self,
134 binding: &OperationBindingRecord,
135 ) -> Result<(), ApplicationIngressError> {
136 if self.target != binding.target() {
137 return Err(ApplicationIngressError::TargetMismatch);
138 }
139 Ok(())
140 }
141}
142
143impl fmt::Debug for ApplicationIngressPayload {
144 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
145 formatter
146 .debug_struct("ApplicationIngressPayload")
147 .field("receiver", &self.receiver)
148 .field("target", &self.target)
149 .field("method", &self.method)
150 .field("argument_bytes", &self.arguments.len())
151 .field("reply_limit", &self.reply_limit)
152 .finish_non_exhaustive()
153 }
154}