use super::MAX_IC_SNAPSHOT_METADATA_BYTES;
use super::{IcSnapshotMetadataError, MAX_IC_SNAPSHOT_CHUNKS, MAX_IC_SNAPSHOT_GLOBALS};
use crate::model::ic_candid::{BoundedVec, StrictOption, StrictReserved};
use ic_platform::candid_codec::{
Reserved,
decoder::{Limits, decode_one_with_skip_limit},
};
use ic_platform::management_canister_types::{
CanisterTimer, ChunkHash, OnLowWasmMemoryHookStatus, ReadCanisterSnapshotMetadataResult,
SnapshotMetadataGlobal, SnapshotSource,
};
use std::collections::BTreeSet;
struct WireMetadata {
source: StrictOption<WireSource>,
taken_at_timestamp: u64,
wasm_module_size: u64,
globals: BoundedVec<StrictOption<SnapshotMetadataGlobal>, MAX_IC_SNAPSHOT_GLOBALS>,
wasm_memory_size: u64,
stable_memory_size: u64,
wasm_chunk_store: BoundedVec<WireChunk, MAX_IC_SNAPSHOT_CHUNKS>,
canister_version: u64,
certified_data: BoundedVec<u8, 32>,
global_timer: StrictOption<CanisterTimer>,
on_low_wasm_memory_hook_status: StrictOption<OnLowWasmMemoryHookStatus>,
}
ic_platform::candid_record!(WireMetadata {
339_281_328 => globals: BoundedVec<StrictOption<SnapshotMetadataGlobal>, MAX_IC_SNAPSHOT_GLOBALS>,
783_802_816 => canister_version: u64,
842_117_339 => source: StrictOption<WireSource>,
864_574_658 => certified_data: BoundedVec<u8, 32>,
903_275_689 => global_timer: StrictOption<CanisterTimer>,
1_282_538_240 => on_low_wasm_memory_hook_status: StrictOption<OnLowWasmMemoryHookStatus>,
1_299_009_433 => wasm_module_size: u64,
1_710_030_651 => stable_memory_size: u64,
1_767_008_372 => wasm_chunk_store: BoundedVec<WireChunk, MAX_IC_SNAPSHOT_CHUNKS>,
3_569_147_778 => taken_at_timestamp: u64,
3_598_099_748 => wasm_memory_size: u64,
});
struct WireChunk {
hash: BoundedVec<u8, 32>,
}
ic_platform::candid_record!(WireChunk {
1_158_164_430 => hash: BoundedVec<u8, 32>,
});
enum WireSource {
TakenFromCanister(StrictReserved),
MetadataUpload(StrictReserved),
}
ic_platform::candid_variant!(WireSource {
833_703_953 => MetadataUpload(StrictReserved),
2_359_638_948 => TakenFromCanister(StrictReserved),
});
pub(super) fn decode(
bytes: &[u8],
) -> Result<ReadCanisterSnapshotMetadataResult, IcSnapshotMetadataError> {
let value: WireMetadata = decode_one_with_skip_limit(
bytes,
Limits {
bytes: MAX_IC_SNAPSHOT_METADATA_BYTES,
header: 16 * 1024,
types: 64,
work: 2 * 1024 * 1024,
allocation: 2 * 1024 * 1024,
},
0,
)
.map_err(|_| IcSnapshotMetadataError::InvalidReply)?;
if value
.globals
.0
.iter()
.filter_map(|global| global.0.as_ref())
.any(|global| matches!(global, SnapshotMetadataGlobal::V128(n) if n.0.bits() > 128))
{
return Err(IcSnapshotMetadataError::InvalidGlobal);
}
let mut hashes = BTreeSet::new();
for chunk in &value.wasm_chunk_store.0 {
if chunk.hash.0.len() != 32 || !hashes.insert(chunk.hash.0.as_slice()) {
return Err(IcSnapshotMetadataError::InvalidChunkHash);
}
}
Ok(ReadCanisterSnapshotMetadataResult {
source: value.source.0.map(|source| match source {
WireSource::TakenFromCanister(_) => SnapshotSource::TakenFromCanister(Reserved),
WireSource::MetadataUpload(_) => SnapshotSource::MetadataUpload(Reserved),
}),
taken_at_timestamp: value.taken_at_timestamp,
wasm_module_size: value.wasm_module_size,
globals: value.globals.0.into_iter().map(|global| global.0).collect(),
wasm_memory_size: value.wasm_memory_size,
stable_memory_size: value.stable_memory_size,
wasm_chunk_store: value
.wasm_chunk_store
.0
.into_iter()
.map(|c| ChunkHash { hash: c.hash.0 })
.collect(),
canister_version: value.canister_version,
certified_data: value.certified_data.0,
global_timer: value.global_timer.0,
on_low_wasm_memory_hook_status: value.on_low_wasm_memory_hook_status.0,
})
}