ic_backup/model/ic_snapshot_upload/reply/
mod.rs1use super::{
4 IcSnapshotUploadError, IcSnapshotUploadKind, IcSnapshotUploadRequest,
5 MAX_IC_SNAPSHOT_UPLOAD_REPLY_BYTES, validate_destination,
6};
7use crate::model::{
8 artifacts::ArtifactChecksumRecord, ic_lifecycle_reply::EMPTY_CANDID_REPLY,
9 ic_snapshot_reply::snapshot_id,
10};
11use candid::{
12 CandidType,
13 de::{DecoderConfig, IDLDeserialize},
14};
15use serde::Deserialize;
16use std::fmt;
17
18#[derive(CandidType, Deserialize)]
19struct WireMetadataReply {
20 #[serde(deserialize_with = "snapshot_id")]
21 snapshot_id: Vec<u8>,
22}
23
24#[derive(Debug, Eq, PartialEq)]
26pub enum IcSnapshotUploadReplyKind {
27 Metadata {
29 snapshot_id: Vec<u8>,
31 },
32 DataAcknowledgement,
34}
35
36pub struct IcSnapshotUploadReply<'request, 'source> {
38 request: &'request IcSnapshotUploadRequest<'source>,
39 kind: IcSnapshotUploadReplyKind,
40 payload_checksum: ArtifactChecksumRecord,
41}
42
43impl<'request, 'source> IcSnapshotUploadReply<'request, 'source> {
44 pub fn decode(
52 request: &'request IcSnapshotUploadRequest<'source>,
53 bytes: &[u8],
54 ) -> Result<Self, IcSnapshotUploadError> {
55 if bytes.len() > MAX_IC_SNAPSHOT_UPLOAD_REPLY_BYTES {
56 return Err(IcSnapshotUploadError::ReplyTooLarge);
57 }
58 let kind = match request.kind() {
59 IcSnapshotUploadKind::Metadata => {
60 let mut config = DecoderConfig::new();
61 config
62 .set_decoding_quota(64 * 1024)
63 .set_skipping_quota(0)
64 .set_max_type_len(16)
65 .set_max_header_len(4096)
66 .set_full_error_message(false);
67 let mut decoder = IDLDeserialize::new_with_config(bytes, &config)
68 .map_err(|_| IcSnapshotUploadError::InvalidReply)?;
69 let reply = decoder
70 .get_value::<WireMetadataReply>()
71 .map_err(|_| IcSnapshotUploadError::InvalidReply)?;
72 if !decoder.is_done() {
73 return Err(IcSnapshotUploadError::InvalidReply);
74 }
75 decoder
76 .done()
77 .map_err(|_| IcSnapshotUploadError::InvalidReply)?;
78 validate_destination(request.source(), &reply.snapshot_id)?;
79 IcSnapshotUploadReplyKind::Metadata {
80 snapshot_id: reply.snapshot_id,
81 }
82 }
83 IcSnapshotUploadKind::Data { .. } => {
84 if bytes != EMPTY_CANDID_REPLY {
85 return Err(IcSnapshotUploadError::InvalidReply);
86 }
87 IcSnapshotUploadReplyKind::DataAcknowledgement
88 }
89 };
90 Ok(Self {
91 request,
92 kind,
93 payload_checksum: ArtifactChecksumRecord::from_bytes(bytes),
94 })
95 }
96 #[must_use]
98 pub const fn request(&self) -> &'request IcSnapshotUploadRequest<'source> {
99 self.request
100 }
101 #[must_use]
103 pub const fn kind(&self) -> &IcSnapshotUploadReplyKind {
104 &self.kind
105 }
106 #[must_use]
108 pub const fn payload_checksum(&self) -> &ArtifactChecksumRecord {
109 &self.payload_checksum
110 }
111 #[must_use]
113 pub fn digest(&self) -> ArtifactChecksumRecord {
114 let mut bytes = b"ic-backup/ic-snapshot-upload-reply/v1\0".to_vec();
115 bytes.extend_from_slice(self.request.binding_digest().hash().as_bytes());
116 bytes.extend_from_slice(self.payload_checksum.hash().as_bytes());
117 ArtifactChecksumRecord::from_bytes(&bytes)
118 }
119}
120
121impl fmt::Debug for IcSnapshotUploadReply<'_, '_> {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.debug_struct("IcSnapshotUploadReply")
124 .field("method", &self.request.method())
125 .field("payload_checksum", &self.payload_checksum)
126 .finish_non_exhaustive()
127 }
128}