ic_backup/model/ic_snapshot_metadata/
mod.rs1mod wire;
4
5use super::{
6 artifacts::ArtifactChecksumRecord,
7 ic_request::{IcRequestError, admits_snapshot_id, encode_arguments, management_request_digest},
8};
9use ic_platform::management_canister_types::{
10 ReadCanisterSnapshotMetadataArgs, ReadCanisterSnapshotMetadataResult,
11};
12use std::fmt;
13use thiserror::Error;
14
15pub const MAX_IC_SNAPSHOT_METADATA_BYTES: usize = 1024 * 1024;
17pub const MAX_IC_SNAPSHOT_GLOBALS: usize = 4096;
19pub const MAX_IC_SNAPSHOT_CHUNKS: usize = 1024;
21
22#[derive(Clone)]
28pub struct IcSnapshotMetadataRequest {
29 target: String,
30 target_bytes: Vec<u8>,
31 snapshot_id: Vec<u8>,
32 arguments: Vec<u8>,
33}
34
35impl IcSnapshotMetadataRequest {
36 pub fn new(target: &str, snapshot_id: &[u8]) -> Result<Self, IcRequestError> {
42 if !admits_snapshot_id(snapshot_id) {
43 return Err(IcRequestError::InvalidSnapshotId);
44 }
45 let (target, principal) =
46 super::principal::canonical_parts(target).ok_or(IcRequestError::InvalidTarget)?;
47 let arguments = encode_arguments(ReadCanisterSnapshotMetadataArgs {
48 canister_id: principal.into(),
49 snapshot_id: snapshot_id.to_vec(),
50 })?;
51 Ok(Self {
52 target,
53 target_bytes: principal.as_slice().to_vec(),
54 snapshot_id: snapshot_id.to_vec(),
55 arguments,
56 })
57 }
58
59 #[must_use]
61 pub fn target(&self) -> &str {
62 &self.target
63 }
64
65 #[must_use]
67 pub fn snapshot_id(&self) -> &[u8] {
68 &self.snapshot_id
69 }
70
71 #[must_use]
73 pub fn arguments(&self) -> &[u8] {
74 &self.arguments
75 }
76
77 #[must_use]
79 pub const fn receiver(&self) -> &'static str {
80 "aaaaa-aa"
81 }
82
83 #[must_use]
85 pub const fn method(&self) -> &'static str {
86 "read_canister_snapshot_metadata"
87 }
88
89 #[must_use]
91 pub fn digest(&self) -> ArtifactChecksumRecord {
92 management_request_digest(&self.target_bytes, self.method(), &self.arguments)
93 }
94}
95
96impl fmt::Debug for IcSnapshotMetadataRequest {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.debug_struct("IcSnapshotMetadataRequest")
99 .field("target", &self.target)
100 .field("snapshot_id_bytes", &self.snapshot_id.len())
101 .finish_non_exhaustive()
102 }
103}
104
105pub struct IcSnapshotMetadataReply<'request> {
112 request: &'request IcSnapshotMetadataRequest,
113 metadata: ReadCanisterSnapshotMetadataResult,
114 payload_checksum: ArtifactChecksumRecord,
115}
116
117impl<'request> IcSnapshotMetadataReply<'request> {
118 pub(crate) fn region_sizes(&self) -> [u64; 3] {
119 [
120 self.metadata.wasm_module_size,
121 self.metadata.wasm_memory_size,
122 self.metadata.stable_memory_size,
123 ]
124 }
125
126 pub(crate) fn chunk_index(&self, hash: &[u8]) -> Option<usize> {
127 self.metadata
128 .wasm_chunk_store
129 .iter()
130 .position(|chunk| chunk.hash == hash)
131 }
132
133 pub fn decode(
140 request: &'request IcSnapshotMetadataRequest,
141 bytes: &[u8],
142 ) -> Result<Self, IcSnapshotMetadataError> {
143 if bytes.len() > MAX_IC_SNAPSHOT_METADATA_BYTES {
144 return Err(IcSnapshotMetadataError::ReplyTooLarge);
145 }
146 let metadata = wire::decode(bytes)?;
147 Ok(Self {
148 request,
149 metadata,
150 payload_checksum: ArtifactChecksumRecord::from_bytes(bytes),
151 })
152 }
153
154 #[must_use]
156 pub const fn request(&self) -> &'request IcSnapshotMetadataRequest {
157 self.request
158 }
159
160 #[must_use]
162 pub const fn metadata(&self) -> &ReadCanisterSnapshotMetadataResult {
163 &self.metadata
164 }
165
166 #[must_use]
168 pub const fn payload_checksum(&self) -> &ArtifactChecksumRecord {
169 &self.payload_checksum
170 }
171
172 #[must_use]
174 pub fn digest(&self) -> ArtifactChecksumRecord {
175 super::evidence_digest::reply(
176 b"ic-backup/ic-snapshot-metadata-reply/v1\0",
177 [&self.request.digest(), &self.payload_checksum],
178 )
179 }
180}
181
182impl fmt::Debug for IcSnapshotMetadataReply<'_> {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 f.debug_struct("IcSnapshotMetadataReply")
185 .field("request", &self.request)
186 .field("payload_checksum", &self.payload_checksum)
187 .finish_non_exhaustive()
188 }
189}
190
191#[derive(Debug, Error, Eq, PartialEq)]
193pub enum IcSnapshotMetadataError {
194 #[error("snapshot metadata exceeds {MAX_IC_SNAPSHOT_METADATA_BYTES} bytes")]
196 ReplyTooLarge,
197 #[error("invalid or unsupported snapshot metadata reply")]
199 InvalidReply,
200 #[error("snapshot v128 global exceeds 128 bits")]
202 InvalidGlobal,
203 #[error("snapshot chunk hash is not a unique 32-byte identity")]
205 InvalidChunkHash,
206}
207
208#[cfg(test)]
209mod tests;