@@ -6,16 +6,18 @@ use ic_cycles_account_manager::ResourceSaturation;
66use ic_error_types:: { ErrorCode , RejectCode } ;
77use ic_management_canister_types_private:: {
88 self as ic00, CanisterChange , CanisterChangeDetails , CanisterSettingsArgsBuilder ,
9- CanisterSnapshotResponse , ClearChunkStoreArgs , DeleteCanisterSnapshotArgs ,
9+ CanisterSnapshotResponse , ClearChunkStoreArgs , DeleteCanisterSnapshotArgs , GlobalTimer ,
1010 ListCanisterSnapshotArgs , LoadCanisterSnapshotArgs , Method , OnLowWasmMemoryHookStatus ,
11- Payload as Ic00Payload , TakeCanisterSnapshotArgs , UpdateSettingsArgs , UploadChunkArgs ,
11+ Payload as Ic00Payload , ReadCanisterSnapshotMetadataArgs , ReadCanisterSnapshotMetadataResponse ,
12+ SnapshotSource , TakeCanisterSnapshotArgs , UpdateSettingsArgs , UploadChunkArgs ,
1213} ;
1314use ic_registry_subnet_type:: SubnetType ;
1415use ic_replicated_state:: {
1516 canister_snapshots:: SnapshotOperation ,
1617 canister_state:: {
1718 execution_state:: { WasmBinary , WasmExecutionMode } ,
1819 system_state:: CyclesUseCase ,
20+ WASM_PAGE_SIZE_IN_BYTES ,
1921 } ,
2022 CanisterState , ExecutionState , SchedulerState ,
2123} ;
@@ -30,6 +32,7 @@ use ic_types::{
3032 time:: UNIX_EPOCH ,
3133 CanisterId , Cycles , NumInstructions , SnapshotId ,
3234} ;
35+ use ic_types_test_utils:: ids:: user_test_id;
3336use ic_universal_canister:: { wasm, UNIVERSAL_CANISTER_WASM } ;
3437use more_asserts:: assert_gt;
3538use serde_bytes:: ByteBuf ;
@@ -1995,6 +1998,155 @@ fn snapshot_must_include_globals() {
19951998 assert_eq ! ( result, WasmResult :: Reply ( vec![ 1 , 0 , 0 , 0 ] ) ) ;
19961999}
19972000
2001+ #[ test]
2002+ fn read_canister_snapshot_metadata_succeeds ( ) {
2003+ let own_subnet = subnet_test_id ( 1 ) ;
2004+ let caller_canister = canister_test_id ( 1 ) ;
2005+ let mut test = ExecutionTestBuilder :: new ( )
2006+ . with_snapshot_metadata_download ( )
2007+ . with_own_subnet_id ( own_subnet)
2008+ . with_caller ( own_subnet, caller_canister)
2009+ . build ( ) ;
2010+ // Create new canister.
2011+ let uni_canister_wasm = UNIVERSAL_CANISTER_WASM . to_vec ( ) ;
2012+ let canister_id = test
2013+ . canister_from_cycles_and_binary (
2014+ Cycles :: new ( 1_000_000_000_000_000 ) ,
2015+ uni_canister_wasm. clone ( ) ,
2016+ )
2017+ . unwrap ( ) ;
2018+
2019+ // Upload chunk.
2020+ let chunk = vec ! [ 1 , 2 , 3 , 4 , 5 ] ;
2021+ let upload_args = UploadChunkArgs {
2022+ canister_id : canister_id. into ( ) ,
2023+ chunk,
2024+ } ;
2025+ let result = test. subnet_message ( "upload_chunk" , upload_args. encode ( ) ) ;
2026+ assert ! ( result. is_ok( ) ) ;
2027+ // Grow the stable memory
2028+ let stable_pages = 13 ;
2029+ let payload = wasm ( ) . stable64_grow ( stable_pages) . reply ( ) . build ( ) ;
2030+ let _res = test. ingress ( canister_id, "update" , payload) . unwrap ( ) ;
2031+ // Set some cert data
2032+ let cert_data = [ 42 ] ;
2033+ let payload = wasm ( ) . certified_data_set ( & cert_data) . reply ( ) . build ( ) ;
2034+ let _res = test. ingress ( canister_id, "update" , payload) . unwrap ( ) ;
2035+ // Set a global timer
2036+ let timestamp = 43 ;
2037+ let payload = wasm ( ) . api_global_timer_set ( timestamp) . reply ( ) . build ( ) ;
2038+ let _res = test. ingress ( canister_id, "update" , payload) . unwrap ( ) ;
2039+
2040+ // Take a snapshot of the canister.
2041+ let args: TakeCanisterSnapshotArgs = TakeCanisterSnapshotArgs :: new ( canister_id, None ) ;
2042+ let result = test. subnet_message ( "take_canister_snapshot" , args. encode ( ) ) ;
2043+ let snapshot_id = CanisterSnapshotResponse :: decode ( & result. unwrap ( ) . bytes ( ) )
2044+ . unwrap ( )
2045+ . snapshot_id ( ) ;
2046+
2047+ // Get the metadata
2048+ let args = ReadCanisterSnapshotMetadataArgs :: new ( canister_id, snapshot_id) ;
2049+ let WasmResult :: Reply ( bytes) = test
2050+ . subnet_message ( "read_canister_snapshot_metadata" , args. encode ( ) )
2051+ . unwrap ( )
2052+ else {
2053+ panic ! ( "expected WasmResult::Reply" )
2054+ } ;
2055+ let metadata = Decode ! ( & bytes, ReadCanisterSnapshotMetadataResponse ) . unwrap ( ) ;
2056+ assert_eq ! ( metadata. source, SnapshotSource :: TakenFromCanister ) ;
2057+ assert_eq ! (
2058+ metadata. stable_memory_size,
2059+ WASM_PAGE_SIZE_IN_BYTES as u64 * stable_pages
2060+ ) ;
2061+ assert_eq ! ( metadata. wasm_module_size, uni_canister_wasm. len( ) as u64 ) ;
2062+ assert_eq ! ( metadata. wasm_chunk_store. len( ) , 1 ) ;
2063+ assert_eq ! ( metadata. certified_data, cert_data) ;
2064+ assert_eq ! ( metadata. global_timer, GlobalTimer :: Active ( timestamp) ) ;
2065+ assert_eq ! ( metadata. canister_version, 4 ) ;
2066+ }
2067+
2068+ #[ test]
2069+ fn read_canister_snapshot_metadata_fails_canister_and_snapshot_must_match ( ) {
2070+ let own_subnet = subnet_test_id ( 1 ) ;
2071+ let caller_canister = canister_test_id ( 1 ) ;
2072+ let mut test = ExecutionTestBuilder :: new ( )
2073+ . with_snapshot_metadata_download ( )
2074+ . with_own_subnet_id ( own_subnet)
2075+ . with_manual_execution ( )
2076+ . with_caller ( own_subnet, caller_canister)
2077+ . build ( ) ;
2078+
2079+ // Create canister
2080+ let canister_id = test
2081+ . canister_from_cycles_and_binary (
2082+ Cycles :: new ( 1_000_000_000_000_000 ) ,
2083+ UNIVERSAL_CANISTER_WASM . to_vec ( ) ,
2084+ )
2085+ . unwrap ( ) ;
2086+
2087+ // Create other canister.
2088+ let other_canister_id = test
2089+ . canister_from_cycles_and_binary (
2090+ Cycles :: new ( 1_000_000_000_000_000 ) ,
2091+ UNIVERSAL_CANISTER_WASM . to_vec ( ) ,
2092+ )
2093+ . unwrap ( ) ;
2094+
2095+ // Take a snapshot of the first canister.
2096+ let args: TakeCanisterSnapshotArgs = TakeCanisterSnapshotArgs :: new ( canister_id, None ) ;
2097+ let result = test. subnet_message ( "take_canister_snapshot" , args. encode ( ) ) ;
2098+ let snapshot_id = CanisterSnapshotResponse :: decode ( & result. unwrap ( ) . bytes ( ) )
2099+ . unwrap ( )
2100+ . snapshot_id ( ) ;
2101+
2102+ // Try to access metadata via the wrong canister id
2103+ let args = ReadCanisterSnapshotMetadataArgs :: new ( other_canister_id, snapshot_id) ;
2104+ let error = test
2105+ . subnet_message ( "read_canister_snapshot_metadata" , args. encode ( ) )
2106+ . unwrap_err ( ) ;
2107+ assert_eq ! ( error. code( ) , ErrorCode :: CanisterRejectedMessage ) ;
2108+
2109+ // Try to access metadata via a bad snapshot id
2110+ let args = ReadCanisterSnapshotMetadataArgs :: new ( canister_id, ( canister_id, 42 ) . into ( ) ) ;
2111+ let error = test
2112+ . subnet_message ( "read_canister_snapshot_metadata" , args. encode ( ) )
2113+ . unwrap_err ( ) ;
2114+ assert_eq ! ( error. code( ) , ErrorCode :: CanisterSnapshotNotFound ) ;
2115+ }
2116+
2117+ #[ test]
2118+ fn read_canister_snapshot_metadata_fails_invalid_controller ( ) {
2119+ let own_subnet = subnet_test_id ( 1 ) ;
2120+ let mut test = ExecutionTestBuilder :: new ( )
2121+ . with_snapshot_metadata_download ( )
2122+ . with_own_subnet_id ( own_subnet)
2123+ . with_manual_execution ( )
2124+ . build ( ) ;
2125+ // Create new canister.
2126+ let uni_canister_wasm = UNIVERSAL_CANISTER_WASM . to_vec ( ) ;
2127+ let canister_id = test
2128+ . canister_from_cycles_and_binary (
2129+ Cycles :: new ( 1_000_000_000_000_000 ) ,
2130+ uni_canister_wasm. clone ( ) ,
2131+ )
2132+ . unwrap ( ) ;
2133+
2134+ // Take a snapshot of the canister.
2135+ let args: TakeCanisterSnapshotArgs = TakeCanisterSnapshotArgs :: new ( canister_id, None ) ;
2136+ let result = test. subnet_message ( "take_canister_snapshot" , args. encode ( ) ) ;
2137+ let snapshot_id = CanisterSnapshotResponse :: decode ( & result. unwrap ( ) . bytes ( ) )
2138+ . unwrap ( )
2139+ . snapshot_id ( ) ;
2140+
2141+ // Non-controller user tries to get metadata
2142+ test. set_user_id ( user_test_id ( 42 ) ) ;
2143+ let args = ReadCanisterSnapshotMetadataArgs :: new ( canister_id, snapshot_id) ;
2144+ let error = test
2145+ . subnet_message ( "read_canister_snapshot_metadata" , args. encode ( ) )
2146+ . unwrap_err ( ) ;
2147+ assert_eq ! ( error. code( ) , ErrorCode :: CanisterInvalidController ) ;
2148+ }
2149+
19982150/// Early warning system / stumbling block forcing the authors of changes adding
19992151/// or removing canister state fields to think about and/or ask the Execution
20002152/// team to think about any repercussions to the canister snapshot logic.
0 commit comments