ic_backup/model/ic_snapshot_metadata/
mod.rs1mod wire;
4
5use super::{
6 artifacts::ArtifactChecksumRecord,
7 ic_request::{
8 IcRequestError, MAX_IC_ARGUMENT_BYTES, MAX_IC_SNAPSHOT_ID_BYTES, management_request_digest,
9 },
10};
11use ic_management_canister_types::{
12 ReadCanisterSnapshotMetadataArgs, ReadCanisterSnapshotMetadataResult,
13};
14use std::fmt;
15use thiserror::Error;
16
17pub const MAX_IC_SNAPSHOT_METADATA_BYTES: usize = 1024 * 1024;
19pub const MAX_IC_SNAPSHOT_GLOBALS: usize = 4096;
21pub const MAX_IC_SNAPSHOT_CHUNKS: usize = 1024;
23
24#[derive(Clone)]
30pub struct IcSnapshotMetadataRequest {
31 target: String,
32 target_bytes: Vec<u8>,
33 snapshot_id: Vec<u8>,
34 arguments: Vec<u8>,
35}
36
37impl IcSnapshotMetadataRequest {
38 pub fn new(target: &str, snapshot_id: &[u8]) -> Result<Self, IcRequestError> {
44 if snapshot_id.is_empty() || snapshot_id.len() > MAX_IC_SNAPSHOT_ID_BYTES {
45 return Err(IcRequestError::InvalidSnapshotId);
46 }
47 let (target, principal) =
48 super::principal::canonical_parts(target).ok_or(IcRequestError::InvalidTarget)?;
49 let arguments = candid::encode_one(ReadCanisterSnapshotMetadataArgs {
50 canister_id: principal,
51 snapshot_id: snapshot_id.to_vec(),
52 })
53 .map_err(|error| IcRequestError::Encoding(error.to_string()))?;
54 if arguments.len() > MAX_IC_ARGUMENT_BYTES {
55 return Err(IcRequestError::ArgumentsTooLarge);
56 }
57 Ok(Self {
58 target,
59 target_bytes: principal.as_slice().to_vec(),
60 snapshot_id: snapshot_id.to_vec(),
61 arguments,
62 })
63 }
64
65 #[must_use]
67 pub fn target(&self) -> &str {
68 &self.target
69 }
70
71 #[must_use]
73 pub fn snapshot_id(&self) -> &[u8] {
74 &self.snapshot_id
75 }
76
77 #[must_use]
79 pub fn arguments(&self) -> &[u8] {
80 &self.arguments
81 }
82
83 #[must_use]
85 pub const fn receiver(&self) -> &'static str {
86 "aaaaa-aa"
87 }
88
89 #[must_use]
91 pub const fn method(&self) -> &'static str {
92 "read_canister_snapshot_metadata"
93 }
94
95 #[must_use]
97 pub fn digest(&self) -> ArtifactChecksumRecord {
98 management_request_digest(&self.target_bytes, self.method(), &self.arguments)
99 }
100}
101
102impl fmt::Debug for IcSnapshotMetadataRequest {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 f.debug_struct("IcSnapshotMetadataRequest")
105 .field("target", &self.target)
106 .field("snapshot_id_bytes", &self.snapshot_id.len())
107 .finish_non_exhaustive()
108 }
109}
110
111pub struct IcSnapshotMetadataReply<'request> {
118 request: &'request IcSnapshotMetadataRequest,
119 metadata: ReadCanisterSnapshotMetadataResult,
120 payload_checksum: ArtifactChecksumRecord,
121}
122
123impl<'request> IcSnapshotMetadataReply<'request> {
124 pub fn decode(
131 request: &'request IcSnapshotMetadataRequest,
132 bytes: &[u8],
133 ) -> Result<Self, IcSnapshotMetadataError> {
134 if bytes.len() > MAX_IC_SNAPSHOT_METADATA_BYTES {
135 return Err(IcSnapshotMetadataError::ReplyTooLarge);
136 }
137 let metadata = wire::decode(bytes)?;
138 Ok(Self {
139 request,
140 metadata,
141 payload_checksum: ArtifactChecksumRecord::from_bytes(bytes),
142 })
143 }
144
145 #[must_use]
147 pub const fn request(&self) -> &'request IcSnapshotMetadataRequest {
148 self.request
149 }
150
151 #[must_use]
153 pub const fn metadata(&self) -> &ReadCanisterSnapshotMetadataResult {
154 &self.metadata
155 }
156
157 #[must_use]
159 pub const fn payload_checksum(&self) -> &ArtifactChecksumRecord {
160 &self.payload_checksum
161 }
162
163 #[must_use]
165 pub fn digest(&self) -> ArtifactChecksumRecord {
166 super::evidence_digest::reply(
167 b"ic-backup/ic-snapshot-metadata-reply/v1\0",
168 [&self.request.digest(), &self.payload_checksum],
169 )
170 }
171}
172
173impl fmt::Debug for IcSnapshotMetadataReply<'_> {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 f.debug_struct("IcSnapshotMetadataReply")
176 .field("request", &self.request)
177 .field("payload_checksum", &self.payload_checksum)
178 .finish_non_exhaustive()
179 }
180}
181
182#[derive(Debug, Error, Eq, PartialEq)]
184pub enum IcSnapshotMetadataError {
185 #[error("snapshot metadata exceeds {MAX_IC_SNAPSHOT_METADATA_BYTES} bytes")]
187 ReplyTooLarge,
188 #[error("invalid or unsupported snapshot metadata reply")]
190 InvalidReply,
191 #[error("snapshot v128 global exceeds 128 bits")]
193 InvalidGlobal,
194 #[error("snapshot chunk hash is not a unique 32-byte identity")]
196 InvalidChunkHash,
197}
198
199#[cfg(test)]
200mod tests;