ic-backup 0.63.0

Host-side snapshot backup and same-release recovery for Internet Computer canisters
Documentation
//! Decode-only pinned wire fields with bounded allocation and no skipped work.

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,
    })
}