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