diff --git a/rs/config/src/subnet_config.rs b/rs/config/src/subnet_config.rs index 60b2bb03d96e..cbc861d2b69e 100644 --- a/rs/config/src/subnet_config.rs +++ b/rs/config/src/subnet_config.rs @@ -411,6 +411,9 @@ pub struct CyclesAccountManagerConfig { /// Fee for storing a GiB of data per second. pub gib_storage_per_second_fee: Cycles, + /// Base fee charged per second for every canister, regardless of resource usage. + pub base_per_second_fee: Cycles, + /// Fee for each percent of the reserved compute allocation. Note that /// reserved compute allocation is a scarce resource, and should be /// appropriately charged for. @@ -480,6 +483,7 @@ impl CyclesAccountManagerConfig { ingress_byte_reception_fee: Cycles::new(2_000), // 10 SDR per GiB per year => 10e12 Cycles per year gib_storage_per_second_fee: Cycles::new(317_500), + base_per_second_fee: Cycles::new(10_000), duration_between_allocation_charges: Duration::from_secs(10), ecdsa_signature_fee: ECDSA_SIGNATURE_FEE, schnorr_signature_fee: SCHNORR_SIGNATURE_FEE, @@ -513,6 +517,7 @@ impl CyclesAccountManagerConfig { ingress_message_reception_fee: Cycles::new(0), ingress_byte_reception_fee: Cycles::new(0), gib_storage_per_second_fee: Cycles::new(0), + base_per_second_fee: Cycles::new(0), duration_between_allocation_charges: Duration::from_secs(10), // ECDSA and Schnorr signature fees are the fees charged when creating a // signature on this subnet. The request likely came from a @@ -549,6 +554,7 @@ impl CyclesAccountManagerConfig { ingress_message_reception_fee: Cycles::zero(), ingress_byte_reception_fee: Cycles::zero(), gib_storage_per_second_fee: Cycles::zero(), + base_per_second_fee: Cycles::zero(), compute_percent_allocated_per_second_fee: Cycles::zero(), duration_between_allocation_charges: Duration::from_secs(u64::MAX), ecdsa_signature_fee: Cycles::zero(), diff --git a/rs/cycles_account_manager/src/cycles_account_manager.rs b/rs/cycles_account_manager/src/cycles_account_manager.rs index f99f05c6bb47..f5c3344c6046 100644 --- a/rs/cycles_account_manager/src/cycles_account_manager.rs +++ b/rs/cycles_account_manager/src/cycles_account_manager.rs @@ -156,6 +156,15 @@ impl CyclesAccountManager { ) } + /// Returns the base fee per second charged to every canister that has any memory usage. + pub fn base_per_second_fee( + &self, + subnet_size: usize, + cost_schedule: CanisterCyclesCostSchedule, + ) -> CompoundCycles { + self.scale_cost(self.config.base_per_second_fee, subnet_size, cost_schedule) + } + /// Returns the fee per byte of ingress message received in [`Cycles`]. pub fn ingress_byte_received_fee( &self, @@ -227,7 +236,8 @@ impl CyclesAccountManager { let memory = memory_allocation.allocated_bytes(memory_usage); CyclesBurnedRate { - memory: self.memory_cost(memory, DAY, subnet_size, cost_schedule), + memory: self.memory_cost(memory, DAY, subnet_size, cost_schedule) + + self.canister_base_cost(memory, DAY, subnet_size, cost_schedule), message_memory: self.memory_cost( message_memory_usage.total(), DAY, @@ -700,6 +710,21 @@ impl CyclesAccountManager { self.scale_cost(cycles, subnet_size, cost_schedule) } + pub fn canister_base_cost( + &self, + bytes: NumBytes, + duration: Duration, + subnet_size: usize, + cost_schedule: CanisterCyclesCostSchedule, + ) -> CompoundCycles { + let cycles = if bytes > NumBytes::new(0) { + self.config.base_per_second_fee * duration.as_secs() as u128 + } else { + Cycles::zero() + }; + self.scale_cost(cycles, subnet_size, cost_schedule) + } + /// Returns the amount of reserved cycles required for allocating the given /// number of bytes at the given resource saturation level. pub fn storage_reservation_cycles( diff --git a/rs/cycles_account_manager/tests/cycles_account_manager.rs b/rs/cycles_account_manager/tests/cycles_account_manager.rs index 3296ab0eee72..09a31b2ecf80 100644 --- a/rs/cycles_account_manager/tests/cycles_account_manager.rs +++ b/rs/cycles_account_manager/tests/cycles_account_manager.rs @@ -96,6 +96,9 @@ fn test_can_charge_application_subnets() { .real() + cycles_account_manager .memory_cost(memory, duration, subnet_size, cost_schedule) + .real() + + cycles_account_manager + .canister_base_cost(memory, duration, subnet_size, cost_schedule) .real(); let initial_cycles = expected_fee; canister.system_state.add_cycles(initial_cycles); @@ -530,7 +533,15 @@ fn charge_canister_for_memory_usage() { assert_eq!( cycles_account_manager .memory_cost(memory_usage, HOUR, SMALL_APP_SUBNET_MAX_SIZE, cost_schedule) - .real(), + .real() + + cycles_account_manager + .canister_base_cost( + memory_usage, + HOUR, + SMALL_APP_SUBNET_MAX_SIZE, + cost_schedule + ) + .real(), cycles_burned ) }) @@ -1230,10 +1241,12 @@ fn withdraw_cycles_for_transfer_checks_reserved_balance() { let mut system_state = SystemState::new_running_for_testing( canister_test_id(1), canister_test_id(2).get(), - Cycles::new(2_000_000), + Cycles::new(21_000_000), NumSeconds::from(1_000), ); - system_state.reserve_cycles(Cycles::new(1_000_000)).unwrap(); + system_state + .reserve_cycles(Cycles::new(20_000_000)) + .unwrap(); let mut new_balance = system_state.balance(); cycles_account_manager .withdraw_cycles_for_transfer( diff --git a/rs/embedders/tests/system_api.rs b/rs/embedders/tests/system_api.rs index 9918ea126cdd..c10c9062540a 100644 --- a/rs/embedders/tests/system_api.rs +++ b/rs/embedders/tests/system_api.rs @@ -1500,7 +1500,7 @@ fn call_perform_not_enough_cycles_does_not_trap() { /// it clamps the amount to the available cycles minus freeze threshold. #[test] fn cycles_burn128_clamps_to_available_cycles() { - const INITIAL_CYCLES: Cycles = Cycles::new(1000); + const INITIAL_CYCLES: Cycles = Cycles::new(200_000); let cycles_account_manager = CyclesAccountManagerBuilder::new() .with_subnet_type(SubnetType::Application) @@ -1560,7 +1560,9 @@ fn growing_wasm_memory_updates_subnet_available_memory() { SubnetAvailableMemory::new_for_testing(subnet_available_memory_bytes, 0, 0); let wasm_custom_sections_available_memory_before = subnet_available_memory.get_wasm_custom_sections_memory(); - let system_state = SystemStateBuilder::default().build(); + let system_state = SystemStateBuilder::default() + .initial_cycles(Cycles::new(20_000_000_000_000)) + .build(); let cycles_account_manager = CyclesAccountManagerBuilder::new().build(); let api_type = ApiTypeBuilder::build_update_api(); let execution_parameters = execution_parameters(api_type.execution_mode()); diff --git a/rs/execution_environment/src/canister_manager/tests.rs b/rs/execution_environment/src/canister_manager/tests.rs index 9a53a2292ed9..1cf70343cb9b 100644 --- a/rs/execution_environment/src/canister_manager/tests.rs +++ b/rs/execution_environment/src/canister_manager/tests.rs @@ -2839,6 +2839,7 @@ fn install_code_preserves_system_state_and_scheduler_state() { let certified_data = vec![42]; let mut original_canister = CanisterStateBuilder::new() .with_canister_id(canister_id) + .with_cycles(Cycles::new(15_000_000_000_000)) .with_status(CanisterStatusType::Running) .with_controller(controller) .with_certified_data(certified_data.clone()) diff --git a/rs/execution_environment/src/execution/response/tests.rs b/rs/execution_environment/src/execution/response/tests.rs index 159b0f3d03bb..37fd5d15f28a 100644 --- a/rs/execution_environment/src/execution/response/tests.rs +++ b/rs/execution_environment/src/execution/response/tests.rs @@ -2755,10 +2755,10 @@ fn cycles_balance_changes_applied_correctly() { .with_instruction_limit(20_000_000_000) .build(); let a_id = test - .universal_canister_with_cycles(Cycles::new(10_000_000_000_000)) + .universal_canister_with_cycles(Cycles::new(20_000_000_000_000)) .unwrap(); let b_id = test - .universal_canister_with_cycles(Cycles::new(301_000_000_000)) + .universal_canister_with_cycles(Cycles::new(400_000_000_000)) .unwrap(); test.ingress( @@ -2811,7 +2811,7 @@ fn test_cycles_burn() { let canister_memory_usage = NumBytes::from(1_000_000); let canister_message_memory_usage = MessageMemoryUsage::ZERO; - let amount = 1_000_000_000; + let amount = 100_000_000_000; let mut balance = Cycles::new(amount); let amount_to_burn = Cycles::new(amount / 10); @@ -2850,7 +2850,7 @@ fn cycles_burn_up_to_the_threshold_on_not_enough_cycles() { Cycles::zero(), ); - let amount = 1_000_000_000; + let amount = 100_000_000_000; let mut balance = Cycles::new(amount); let burned = test.cycles_account_manager().cycles_burn( diff --git a/rs/execution_environment/src/execution/upgrade/tests.rs b/rs/execution_environment/src/execution/upgrade/tests.rs index aebc5e607fb3..367986ea2d40 100644 --- a/rs/execution_environment/src/execution/upgrade/tests.rs +++ b/rs/execution_environment/src/execution/upgrade/tests.rs @@ -258,7 +258,7 @@ fn upgrade_fails_on_not_enough_cycles() { fn upgrade_fails_on_no_execution_state() { let mut test = execution_test_with_max_rounds(1); // Create canister with no binary and hence no execution state - let canister_id = test.create_canister(1_000_000_000_u64.into()); + let canister_id = test.create_canister(30_000_000_000_u64.into()); let canister_state_before = test.canister_state(canister_id).clone(); let result = test.upgrade_canister(canister_id, new_empty_binary()); @@ -970,7 +970,7 @@ fn dts_uninstall_with_aborted_upgrade() { fn upgrade_with_skip_pre_upgrade_fails_on_no_execution_state() { let mut test = execution_test_with_max_rounds(1); // Create canister with no binary and hence no execution state - let canister_id = test.create_canister(1_000_000_000_u64.into()); + let canister_id = test.create_canister(30_000_000_000_u64.into()); let canister_state_before = test.canister_state(canister_id).clone(); let result = test.upgrade_canister_v2( diff --git a/rs/execution_environment/src/execution_environment/tests.rs b/rs/execution_environment/src/execution_environment/tests.rs index 26f6f96e6a06..f1d5afd2a57d 100644 --- a/rs/execution_environment/src/execution_environment/tests.rs +++ b/rs/execution_environment/src/execution_environment/tests.rs @@ -833,6 +833,12 @@ fn get_canister_status_from_another_canister_when_memory_low() { .real() .get()) / one_gib + + test + .cycles_account_manager() + .base_per_second_fee(test.subnet_size(), CanisterCyclesCostSchedule::Normal) + .real() + .get() + * seconds_per_day ); } diff --git a/rs/execution_environment/src/execution_environment/tests/canister_task.rs b/rs/execution_environment/src/execution_environment/tests/canister_task.rs index 03d62d3a04fc..a581af3ba127 100644 --- a/rs/execution_environment/src/execution_environment/tests/canister_task.rs +++ b/rs/execution_environment/src/execution_environment/tests/canister_task.rs @@ -665,7 +665,7 @@ fn global_timer_refunds_cycles_for_request_in_prep() { .unwrap(); let canister_id = env - .install_canister_with_cycles(binary, vec![], None, Cycles::new(301_000_000_000)) + .install_canister_with_cycles(binary, vec![], None, Cycles::new(400_000_000_000)) .unwrap(); let result = env.execute_ingress(canister_id, "test", vec![]).unwrap(); @@ -729,7 +729,7 @@ fn global_timer_set_returns_zero_in_canister_global_timer_method() { .unwrap(); let canister_id = env - .install_canister_with_cycles(binary, vec![], None, Cycles::new(301_000_000_000)) + .install_canister_with_cycles(binary, vec![], None, Cycles::new(400_000_000_000)) .unwrap(); let result = env diff --git a/rs/execution_environment/src/scheduler/test_utilities.rs b/rs/execution_environment/src/scheduler/test_utilities.rs index 3780d9c57a48..d92497b32e60 100644 --- a/rs/execution_environment/src/scheduler/test_utilities.rs +++ b/rs/execution_environment/src/scheduler/test_utilities.rs @@ -755,6 +755,19 @@ impl SchedulerTest { ) } + pub fn canister_base_cost( + &self, + bytes: NumBytes, + duration: Duration, + ) -> CompoundCycles { + self.scheduler.cycles_account_manager.canister_base_cost( + bytes, + duration, + self.subnet_size(), + self.state.as_ref().unwrap().get_own_cost_schedule(), + ) + } + pub fn compute_allocation_cost( &self, compute_allocation: ComputeAllocation, diff --git a/rs/execution_environment/src/scheduler/tests/charging.rs b/rs/execution_environment/src/scheduler/tests/charging.rs index 83f6d0cc74b1..8d256fef9063 100644 --- a/rs/execution_environment/src/scheduler/tests/charging.rs +++ b/rs/execution_environment/src/scheduler/tests/charging.rs @@ -70,7 +70,12 @@ fn only_charge_for_allocation_after_specified_duration() { test.execute_round(ExecutionRoundType::OrdinaryRound); assert_eq!( test.canister_state(canister).system_state.balance().get(), - initial_cycles - 10, + initial_cycles + - 10 + - test + .canister_base_cost(NumBytes::from(bytes_per_cycle), time_between_batches * 2,) + .real() + .get(), ); } @@ -129,6 +134,9 @@ fn charging_for_message_memory_works() { assert_eq!( canister_state.system_state.balance(), balance_before + - test + .canister_base_cost(canister_state.memory_usage(), charge_duration) + .real() - test .memory_cost( canister_state.message_memory_usage().total(), @@ -186,6 +194,9 @@ fn charging_for_logging_memory_works() { assert_eq!( canister_state.system_state.balance(), balance_before + - test + .canister_base_cost(canister_state.memory_usage(), charge_duration) + .real() - test .memory_cost( canister_state.log_memory_store_memory_usage(), @@ -339,7 +350,9 @@ fn dont_charge_allocations_for_paused_canisters() { fn assert_balance_change(test: &SchedulerTest, canister: CanisterId, duration: Duration) { assert_eq!( test.canister_state(canister).system_state.balance(), - INITIAL_CYCLES - test.memory_cost(MEMORY_ALLOCATION, duration).real() + INITIAL_CYCLES + - test.memory_cost(MEMORY_ALLOCATION, duration).real() + - test.canister_base_cost(MEMORY_ALLOCATION, duration).real() ); } // Balance has changed for the canister with no paused execution. diff --git a/rs/execution_environment/src/scheduler/tests/metrics.rs b/rs/execution_environment/src/scheduler/tests/metrics.rs index eb264724905d..9aea8bf9f570 100644 --- a/rs/execution_environment/src/scheduler/tests/metrics.rs +++ b/rs/execution_environment/src/scheduler/tests/metrics.rs @@ -1184,18 +1184,17 @@ fn consumed_cycles_for_resource_allocations_are_updated_from_valid_canisters() { &no_op_logger(), ); + let expected_memory_cycles = (test.memory_cost(memory_allocation, duration) + + test.canister_base_cost(memory_allocation, duration)) + .nominal() + .get() as f64; assert_eq!( fetch_gauge_vec( test.metrics_registry(), "replicated_state_consumed_cycles_from_replica_start", ), metric_vec(&[ - ( - &[("use_case", "Memory")], - test.memory_cost(memory_allocation, duration) - .nominal() - .get() as f64 - ), + (&[("use_case", "Memory")], expected_memory_cycles), ( &[("use_case", "ComputeAllocation")], test.compute_allocation_cost(compute_allocation, duration) @@ -1210,12 +1209,7 @@ fn consumed_cycles_for_resource_allocations_are_updated_from_valid_canisters() { "replicated_state_consumed_cycles_from_replica_start_as_counters", ), metric_vec(&[ - ( - &[("use_case", "Memory")], - test.memory_cost(memory_allocation, duration) - .nominal() - .get() as f64 - ), + (&[("use_case", "Memory")], expected_memory_cycles), ( &[("use_case", "ComputeAllocation")], test.compute_allocation_cost(compute_allocation, duration) diff --git a/rs/execution_environment/tests/canister_logging.rs b/rs/execution_environment/tests/canister_logging.rs index 5841b11fcf3f..e3b39a12a673 100644 --- a/rs/execution_environment/tests/canister_logging.rs +++ b/rs/execution_environment/tests/canister_logging.rs @@ -48,7 +48,7 @@ const MAX_INSTRUCTIONS_PER_ROUND: NumInstructions = NumInstructions::new(5 * B); const MAX_INSTRUCTIONS_PER_MESSAGE: NumInstructions = NumInstructions::new(25 * B); const MAX_INSTRUCTIONS_PER_SLICE: NumInstructions = NumInstructions::new(5 * B); -const CANISTER_INIT_CYCLES: Cycles = Cycles::new(310_000_000_000_u128); +const CANISTER_INIT_CYCLES: Cycles = Cycles::new(400_000_000_000_u128); fn system_time_to_nanos(t: SystemTime) -> u64 { t.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos() as u64 diff --git a/rs/execution_environment/tests/dts.rs b/rs/execution_environment/tests/dts.rs index 7dbd7e4a3250..f7029fd1097e 100644 --- a/rs/execution_environment/tests/dts.rs +++ b/rs/execution_environment/tests/dts.rs @@ -467,9 +467,15 @@ fn dts_install_code_with_concurrent_ingress_insufficient_cycles_and_freezing_thr .compute_percent_allocated_per_second_fee * freezing_threshold; + // A freshly created canister has non-zero memory (canister history), so the + // base_per_second_fee also contributes to the freeze threshold. + let base_per_second_fee_cycles = + config.cycles_account_manager_config.base_per_second_fee * freezing_threshold; + // The initial balance is sufficient to only pay the reservation for installing code - // and the compute allocation during the freezing threshold. - let initial_balance = max_install_code_cost() + compute_allocation_cycles; + // and the freeze threshold (compute allocation + base fee). + let initial_balance = + max_install_code_cost() + compute_allocation_cycles + base_per_second_fee_cycles; let canister_id = env.create_canister_with_cycles( None, @@ -506,7 +512,7 @@ fn dts_install_code_with_concurrent_ingress_insufficient_cycles_and_freezing_thr // i.e., consuming any cycles would make the canister frozen. assert_eq!( env.cycle_balance(canister_id), - compute_allocation_cycles.get() + (compute_allocation_cycles + base_per_second_fee_cycles).get() ); let sender = PrincipalId::new_anonymous(); let method = "update"; @@ -534,9 +540,9 @@ fn dts_install_code_with_concurrent_ingress_insufficient_cycles_and_freezing_thr let err = env.await_ingress(install_code_ingress_id, 100).unwrap_err(); assert_eq!(err.code(), ErrorCode::CanisterInstructionLimitExceeded); - // The cycles to cover the compute allocation during the freezing threshold are only needed to keep the canister unfrozen + // The cycles to cover the freeze threshold are only needed to keep the canister unfrozen // and are not actually used. - let unused_cycles = compute_allocation_cycles; + let unused_cycles = compute_allocation_cycles + base_per_second_fee_cycles; assert_eq!(env.cycle_balance(canister_id), unused_cycles.get()); } diff --git a/rs/execution_environment/tests/execution_test.rs b/rs/execution_environment/tests/execution_test.rs index 5aa63293b48b..4bae0142fd35 100644 --- a/rs/execution_environment/tests/execution_test.rs +++ b/rs/execution_environment/tests/execution_test.rs @@ -1624,7 +1624,7 @@ fn heap_delta_initial_reserve_allows_round_executions_right_after_checkpoint() { fn install_canister(env: &StateMachine) -> Result { let wasm = wat::parse_str(TEST_CANISTER).expect("invalid WAT"); - env.install_canister_with_cycles(wasm, vec![], None, Cycles::new(301 * B)) + env.install_canister_with_cycles(wasm, vec![], None, Cycles::new(400 * B)) } fn send_ingress(env: &StateMachine, canister_id: &CanisterId) -> MessageId { @@ -1794,7 +1794,7 @@ fn current_interval_length_works_on_app_subnets() { let wasm = wat::parse_str(DIRTY_PAGE_CANISTER).unwrap(); let _canister_id = env - .install_canister_with_cycles(wasm, vec![], None, Cycles::new(301 * B)) + .install_canister_with_cycles(wasm, vec![], None, Cycles::new(400 * B)) .unwrap(); // One empty round is always performed when creating a `StateMachine` @@ -2489,7 +2489,7 @@ fn canister_create_with_default_wasm_memory_limit() { .with_subnet_type(SubnetType::Application) .build(); - let initial_cycles = Cycles::new(301 * B); + let initial_cycles = Cycles::new(400 * B); let canister_id = create_universal_canister_with_cycles(&env, None, initial_cycles); let wasm_memory_limit = fetch_wasm_memory_limit(&env, canister_id); @@ -2666,16 +2666,18 @@ fn test_canister_liquid_cycle_balance() { // and that the accepted cycles are way more than that. let lost_cycles = liquid_balance - accepted_cycles; assert_lt!(lost_cycles, 100 * B); - assert_gt!(accepted_cycles, INITIAL_CYCLES_BALANCE.get() - 100 * B); + // The base_per_second_fee raises the freeze threshold by ~26B, reducing the liquid balance (and thus + // accepted cycles) by that amount relative to INITIAL_CYCLES_BALANCE; allow 150B total overhead. + assert_gt!(accepted_cycles, INITIAL_CYCLES_BALANCE.get() - 150 * B); // Finally, we assert that the cycles have indeed moved from one universal canister to the other one. - // The remaining balance of the sender is larger than the lost cycles by the unspent cycles in the execution of the ingress message, - // but still less than 100B. + // The remaining balance of the sender is approximately the freeze threshold (which is ~26B higher than + // before due to the base_per_second_fee) plus unspent execution budget, so allow up to 200B. let balance = env.cycle_balance(canister_id); - assert_lt!(balance, 100 * B); + assert_lt!(balance, 200 * B); // The receiver now holds the joint cycles balance of both canisters at the beginning minus some overhead. let receiver_balance = env.cycle_balance(callee); - assert_gt!(receiver_balance, 2 * INITIAL_CYCLES_BALANCE.get() - 100 * B); + assert_gt!(receiver_balance, 2 * INITIAL_CYCLES_BALANCE.get() - 200 * B); } /// Test that a message which results in many calls with large payloads (2 GB in diff --git a/rs/execution_environment/tests/hypervisor.rs b/rs/execution_environment/tests/hypervisor.rs index 740eb6806493..c3b76707253e 100644 --- a/rs/execution_environment/tests/hypervisor.rs +++ b/rs/execution_environment/tests/hypervisor.rs @@ -3527,7 +3527,7 @@ fn ic0_call_cycles_add_deducts_cycles() { (data (i32.const 0) "some_remote_method XYZ") (data (i32.const 100) "\09\03\00\00\00\00\00\00\ff\01") )"#; - let initial_cycles = Cycles::new(301_000_000_000); + let initial_cycles = Cycles::new(400_000_000_000); let canister_id = test .canister_from_cycles_and_wat(initial_cycles, wat) .unwrap(); @@ -3604,7 +3604,7 @@ fn ic0_call_cycles_add_has_no_effect_without_ic0_call_perform() { (data (i32.const 100) "\09\03\00\00\00\00\00\00\ff\01") )"#; - let initial_cycles = Cycles::new(301_000_000_000); + let initial_cycles = Cycles::new(400_000_000_000); let canister_id = test .canister_from_cycles_and_wat(initial_cycles, wat) .unwrap(); @@ -7876,7 +7876,7 @@ fn charge_for_dirty_pages() { #[test] fn stable_grow_checks_freezing_threshold_in_update() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let canister_id = test.universal_canister().unwrap(); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) @@ -7895,7 +7895,7 @@ fn stable_grow_checks_freezing_threshold_in_update() { #[test] fn stable64_grow_checks_freezing_threshold_in_update() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let canister_id = test.universal_canister().unwrap(); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) @@ -7914,7 +7914,7 @@ fn stable64_grow_checks_freezing_threshold_in_update() { #[test] fn memory_grow_checks_freezing_threshold_in_update() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -7939,7 +7939,7 @@ fn memory_grow_checks_freezing_threshold_in_update() { #[test] fn stable_grow_does_not_check_freezing_threshold_in_query() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let canister_id = test.universal_canister().unwrap(); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) @@ -7952,7 +7952,7 @@ fn stable_grow_does_not_check_freezing_threshold_in_query() { #[test] fn stable64_grow_does_not_check_freezing_threshold_in_query() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let canister_id = test.universal_canister().unwrap(); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) @@ -7965,7 +7965,7 @@ fn stable64_grow_does_not_check_freezing_threshold_in_query() { #[test] fn memory_grow_does_not_check_freezing_threshold_in_query() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -7984,7 +7984,7 @@ fn memory_grow_does_not_check_freezing_threshold_in_query() { #[test] fn stable_grow_does_not_check_freezing_threshold_in_reply() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let callee = test.universal_canister().unwrap(); let canister_id = test.universal_canister().unwrap(); @@ -8009,7 +8009,7 @@ fn stable_grow_does_not_check_freezing_threshold_in_reply() { #[test] fn stable_grow_does_not_check_freezing_threshold_in_reject() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let callee = test.universal_canister().unwrap(); let canister_id = test.universal_canister().unwrap(); @@ -8034,7 +8034,7 @@ fn stable_grow_does_not_check_freezing_threshold_in_reject() { #[test] fn stable_grow_checks_freezing_threshold_in_pre_upgrade() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8067,7 +8067,7 @@ fn stable_grow_checks_freezing_threshold_in_pre_upgrade() { #[test] fn stable_grow_checks_freezing_threshold_in_post_upgrade() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8100,7 +8100,7 @@ fn stable_grow_checks_freezing_threshold_in_post_upgrade() { #[test] fn stable_grow_checks_freezing_threshold_in_start() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let empty_wat = "(module)"; let wat = r#" @@ -8130,7 +8130,7 @@ fn stable_grow_checks_freezing_threshold_in_start() { #[test] fn stable_grow_checks_freezing_threshold_in_init() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8143,7 +8143,7 @@ fn stable_grow_checks_freezing_threshold_in_init() { (memory 0) )"#; let wasm = wat::parse_str(wat).unwrap(); - let canister_id = test.create_canister(Cycles::new(1_000_000_000_000)); + let canister_id = test.create_canister(Cycles::new(12_000_000_000_000)); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) .unwrap(); let err = test.install_canister(canister_id, wasm).unwrap_err(); @@ -8158,7 +8158,7 @@ fn stable_grow_checks_freezing_threshold_in_init() { #[test] fn memory_grow_does_not_check_freezing_threshold_in_pre_upgrade() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8179,7 +8179,7 @@ fn memory_grow_does_not_check_freezing_threshold_in_pre_upgrade() { #[test] fn memory_grow_checks_freezing_threshold_in_post_upgrade() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8211,7 +8211,7 @@ fn memory_grow_checks_freezing_threshold_in_post_upgrade() { #[test] fn memory_grow_checks_freezing_threshold_in_start() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let empty_wat = "(module)"; let wat = r#" @@ -8240,7 +8240,7 @@ fn memory_grow_checks_freezing_threshold_in_start() { #[test] fn memory_grow_checks_freezing_threshold_in_init() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(12_000_000_000_000) .build(); let wat = r#" (module @@ -8252,7 +8252,7 @@ fn memory_grow_checks_freezing_threshold_in_init() { (memory 0) )"#; let wasm = wat::parse_str(wat).unwrap(); - let canister_id = test.create_canister(Cycles::new(1_000_000_000_000)); + let canister_id = test.create_canister(Cycles::new(12_000_000_000_000)); test.update_freezing_threshold(canister_id, NumSeconds::new(1_000_000_000)) .unwrap(); let err = test.install_canister(canister_id, wasm).unwrap_err(); @@ -8270,7 +8270,7 @@ fn call_perform_checks_freezing_threshold_in_update() { .with_initial_canister_cycles(2_500_000_000_000) .build(); let canister_id = test.universal_canister().unwrap(); - test.update_freezing_threshold(canister_id, NumSeconds::new(1_500_000_000)) + test.update_freezing_threshold(canister_id, NumSeconds::new(212_000_000)) .unwrap(); let body = wasm() .call_simple( @@ -8291,7 +8291,7 @@ fn call_perform_checks_freezing_threshold_in_update() { #[test] fn call_perform_does_not_check_freezing_threshold_in_reply() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(15_000_000_000_000) .build(); let callee = test.universal_canister().unwrap(); let canister_id = test.universal_canister().unwrap(); @@ -8325,7 +8325,7 @@ fn call_perform_does_not_check_freezing_threshold_in_reply() { #[test] fn call_perform_does_not_check_freezing_threshold_in_reject() { let mut test = ExecutionTestBuilder::new() - .with_initial_canister_cycles(2_500_000_000_000) + .with_initial_canister_cycles(15_000_000_000_000) .build(); let callee = test.universal_canister().unwrap(); let canister_id = test.universal_canister().unwrap(); diff --git a/rs/execution_environment/tests/in_replicated_execution.rs b/rs/execution_environment/tests/in_replicated_execution.rs index 53228f42ec0b..a3a939b59395 100644 --- a/rs/execution_environment/tests/in_replicated_execution.rs +++ b/rs/execution_environment/tests/in_replicated_execution.rs @@ -14,7 +14,7 @@ fn setup() -> (StateMachine, CanisterId) { .with_checkpoints_enabled(false) .build(); let canister_id = - env.create_canister_with_cycles(None, Cycles::from(301_000_000_000_u128), None); + env.create_canister_with_cycles(None, Cycles::from(400_000_000_000_u128), None); env.install_wasm_in_mode( canister_id, CanisterInstallMode::Install, diff --git a/rs/execution_environment/tests/subnet_size_test.rs b/rs/execution_environment/tests/subnet_size_test.rs index 7ea7c172be73..497753919397 100644 --- a/rs/execution_environment/tests/subnet_size_test.rs +++ b/rs/execution_environment/tests/subnet_size_test.rs @@ -768,6 +768,7 @@ fn get_cycles_account_manager_config(subnet_type: SubnetType) -> CyclesAccountMa ingress_message_reception_fee: Cycles::new(0), ingress_byte_reception_fee: Cycles::new(0), gib_storage_per_second_fee: Cycles::new(0), + base_per_second_fee: Cycles::new(0), duration_between_allocation_charges: Duration::from_secs(10), // ECDSA and Schnorr signature fees are the fees charged when creating a // signature on this subnet. The request likely came from a @@ -810,6 +811,7 @@ fn get_cycles_account_manager_config(subnet_type: SubnetType) -> CyclesAccountMa ingress_byte_reception_fee: Cycles::new(2_000), // 10 SDR per GiB per year => 10e12 Cycles per year gib_storage_per_second_fee: Cycles::new(317_500), + base_per_second_fee: Cycles::new(10_000), duration_between_allocation_charges: Duration::from_secs(10), ecdsa_signature_fee: ECDSA_SIGNATURE_FEE, schnorr_signature_fee: SCHNORR_SIGNATURE_FEE, @@ -860,6 +862,20 @@ fn compute_allocation_cost( scale_cost(config, cycles, subnet_size) } +fn canister_base_cost( + config: &CyclesAccountManagerConfig, + bytes: NumBytes, + duration: Duration, + subnet_size: usize, +) -> Cycles { + if bytes.get() > 0 { + let cycles = config.base_per_second_fee * duration.as_secs() as u128; + scale_cost(config, cycles, subnet_size) + } else { + Cycles::zero() + } +} + fn calculate_one_gib_per_second_cost( config: &CyclesAccountManagerConfig, subnet_size: usize, @@ -868,6 +884,7 @@ fn calculate_one_gib_per_second_cost( let one_gib = NumBytes::from(1 << 30); let duration = Duration::from_secs(1); memory_cost(config, one_gib, duration, subnet_size) + + canister_base_cost(config, one_gib, duration, subnet_size) + compute_allocation_cost(config, compute_allocation, duration, subnet_size) } @@ -980,15 +997,15 @@ fn test_subnet_size_one_gib_storage_default_cost() { // Assert small subnet size cost per year. let cost = simulate_one_gib_per_second_cost(subnet_type, subnet_size_lo, compute_allocation); - assert_eq!(cost * per_year, trillion_cycles(10.012_680_000)); + assert_eq!(cost * per_year, trillion_cycles(10.328_040_000)); // Assert big subnet size cost per year. let cost = simulate_one_gib_per_second_cost(subnet_type, subnet_size_hi, compute_allocation); - assert_eq!(cost * per_year, trillion_cycles(26.186_989_824)); + assert_eq!(cost * per_year, trillion_cycles(27.011_782_368)); // Assert big subnet size cost per year scaled to a small size. let adjusted_cost = (cost * subnet_size_lo) / subnet_size_hi; - assert_eq!(adjusted_cost * per_year, trillion_cycles(10.012_648_464)); + assert_eq!(adjusted_cost * per_year, trillion_cycles(10.328_008_464)); } // Storage cost tests split into 2: zero and non-zero compute allocation. diff --git a/rs/ingress_manager/src/ingress_selector.rs b/rs/ingress_manager/src/ingress_selector.rs index e9186ca92951..6fda65cc1109 100644 --- a/rs/ingress_manager/src/ingress_selector.rs +++ b/rs/ingress_manager/src/ingress_selector.rs @@ -2232,7 +2232,7 @@ pub(crate) mod tests { } } - /// Generates a list of ingress messages with given parameters, and 500 billion cycles + /// Generates a list of ingress messages with given parameters, and 50 trillion cycles pub(crate) fn generate_ingress_messages_with_params( cid: CanisterId, msg_count: usize, @@ -2247,7 +2247,7 @@ pub(crate) mod tests { msgs, CanisterStateBuilder::default() .with_canister_id(cid) - .with_cycles(Cycles::new(500_000_000_000)) + .with_cycles(Cycles::new(50_000_000_000_000)) .build(), ) } diff --git a/rs/migration_canister/tests/tests.rs b/rs/migration_canister/tests/tests.rs index 38dea96213a8..e1a0225100e1 100644 --- a/rs/migration_canister/tests/tests.rs +++ b/rs/migration_canister/tests/tests.rs @@ -179,9 +179,9 @@ async fn setup( None } else { Some(if LOG_MEMORY_STORE_FEATURE_ENABLED { - 11_200_000 + 40_000_000_000 } else { - 2_000_000 + 30_000_000_000 }) }; let migrated_canister = pic diff --git a/rs/test_utilities/state/src/lib.rs b/rs/test_utilities/state/src/lib.rs index afa4d69574ad..29530fc3cdd7 100644 --- a/rs/test_utilities/state/src/lib.rs +++ b/rs/test_utilities/state/src/lib.rs @@ -59,7 +59,7 @@ pub use history::MockIngressHistory; const WASM_PAGE_SIZE_BYTES: usize = 65536; const DEFAULT_FREEZE_THRESHOLD: NumSeconds = NumSeconds::new(1 << 30); -const INITIAL_CYCLES: Cycles = Cycles::new(5_000_000_000_000); +const INITIAL_CYCLES: Cycles = Cycles::new(100_000_000_000_000); const TEST_DEFAULT_LOG_MEMORY_LIMIT: usize = 4 * 1024; // 4 KiB /// Valid, but minimal wasm code.