Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rs/prep/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ DEPENDENCIES = [
"//rs/registry/local_store",
"//rs/registry/proto_data_provider",
"//rs/registry/provisional_whitelist",
"//rs/registry/resource_limits",
"//rs/registry/routing_table",
"//rs/registry/subnet_features",
"//rs/registry/subnet_type",
Expand Down
1 change: 1 addition & 0 deletions rs/prep/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ic-registry-keys = { path = "../registry/keys" }
ic-registry-local-store = { path = "../registry/local_store" }
ic-registry-proto-data-provider = { path = "../registry/proto_data_provider" }
ic-registry-provisional-whitelist = { path = "../registry/provisional_whitelist" }
ic-registry-resource-limits = { path = "../registry/resource_limits" }
ic-registry-routing-table = { path = "../registry/routing_table" }
ic-registry-subnet-features = { path = "../registry/subnet_features" }
ic-registry-subnet-type = { path = "../registry/subnet_type" }
Expand Down
1 change: 1 addition & 0 deletions rs/prep/src/bin/prep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ fn main() -> Result<()> {
/*max_instructions_per_round=*/ None,
/*max_instructions_per_install_code=*/ None,
/*features=*/ None,
/*resource_limits=*/ None,
/*chain_key_config=*/ None,
/*max_number_of_canisters=*/ None,
valid_args.ssh_readonly_access.clone(),
Expand Down
1 change: 1 addition & 0 deletions rs/prep/src/prep_state_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ mod tests {
None,
None,
None,
None,
vec![],
vec![],
SubnetRunningState::Active,
Expand Down
11 changes: 9 additions & 2 deletions rs/prep/src/subnet_configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ use ic_protobuf::registry::{
crypto::v1::PublicKey,
subnet::v1::{
CanisterCyclesCostSchedule, CatchUpPackageContents, ChainKeyConfig, GenesisArgs,
InitialNiDkgTranscriptRecord, SubnetRecord, catch_up_package_contents::CupType,
InitialNiDkgTranscriptRecord, ResourceLimits as ResourceLimitsPb, SubnetRecord,
catch_up_package_contents::CupType,
},
};
use ic_registry_resource_limits::ResourceLimits;
use ic_registry_subnet_features::SubnetFeatures;
use ic_registry_subnet_type::SubnetType;
use ic_types::crypto::threshold_sig::ni_dkg::ThresholdSigPublicKeyError;
Expand Down Expand Up @@ -113,6 +115,9 @@ pub struct SubnetConfig {
/// Flags to mark which features are enabled for this subnet.
pub features: SubnetFeatures,

/// Limits on resource consumption (e.g., memory usage) of the subnet.
pub resource_limits: Option<ResourceLimits>,

/// Optional chain key configuration for this subnet.
pub chain_key_config: Option<ChainKeyConfig>,

Expand Down Expand Up @@ -252,6 +257,7 @@ impl SubnetConfig {
max_instructions_per_round: Option<u64>,
max_instructions_per_install_code: Option<u64>,
features: Option<SubnetFeatures>,
resource_limits: Option<ResourceLimits>,
chain_key_config: Option<ChainKeyConfig>,
max_number_of_canisters: Option<u64>,
ssh_readonly_access: Vec<String>,
Expand Down Expand Up @@ -288,6 +294,7 @@ impl SubnetConfig {
max_instructions_per_install_code: max_instructions_per_install_code
.unwrap_or_else(|| scheduler_config.max_instructions_per_install_code.get()),
features: features.unwrap_or_default(),
resource_limits,
chain_key_config,
max_number_of_canisters: max_number_of_canisters.unwrap_or(0),
ssh_readonly_access,
Expand Down Expand Up @@ -348,7 +355,7 @@ impl SubnetConfig {
chain_key_config: self.chain_key_config,
canister_cycles_cost_schedule: i32::from(self.canister_cycles_cost_schedule),
subnet_admins: vec![],
resource_limits: Default::default(),
resource_limits: self.resource_limits.map(ResourceLimitsPb::from),
recalled_replica_version_ids: vec![],
};

Expand Down
1 change: 1 addition & 0 deletions rs/registry/regedit/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub fn run_ic_prep() -> (TempDir, IcPrepStateDir) {
None,
None,
None,
None,
vec![],
vec![],
SubnetRunningState::Active,
Expand Down
1 change: 1 addition & 0 deletions rs/replica_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ pub fn get_ic_config() -> IcConfig {
/*max_instructions_per_round=*/ None,
/*max_instructions_per_install_code=*/ None,
/*features=*/ None,
/*resource_limits=*/ None,
/*chain_key_config=*/ None,
/*max_number_of_canisters=*/ None,
/*ssh_readonly_access=*/ vec![],
Expand Down
1 change: 1 addition & 0 deletions rs/tests/driver/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ rust_library(
"//rs/registry/nns_data_provider",
"//rs/registry/provisional_whitelist",
"//rs/registry/regedit",
"//rs/registry/resource_limits",
"//rs/registry/routing_table",
"//rs/registry/subnet_features",
"//rs/registry/subnet_type",
Expand Down
1 change: 1 addition & 0 deletions rs/tests/driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ic-registry-keys = { path = "../../registry/keys" }
ic-registry-local-registry = { path = "../../registry/local_registry" }
ic-registry-nns-data-provider = { path = "../../registry/nns_data_provider" }
ic-registry-provisional-whitelist = { path = "../../registry/provisional_whitelist" }
ic-registry-resource-limits = { path = "../../registry/resource_limits" }
ic-registry-routing-table = { path = "../../registry/routing_table" }
ic-registry-subnet-features = { path = "../../registry/subnet_features" }
ic-registry-subnet-type = { path = "../../registry/subnet_type" }
Expand Down
1 change: 1 addition & 0 deletions rs/tests/driver/src/driver/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ pub fn init_ic(
subnet.max_instructions_per_round,
subnet.max_instructions_per_install_code,
subnet.features,
subnet.resource_limits,
subnet.chain_key_config.clone().map(|c| c.into()),
subnet.max_number_of_canisters,
subnet.ssh_readonly_access.clone(),
Expand Down
9 changes: 9 additions & 0 deletions rs/tests/driver/src/driver/ic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use ic_prep_lib::prep_state_directory::IcPrepStateDir;
use ic_prep_lib::{node::NodeSecretKeyStore, subnet_configuration::SubnetRunningState};
use ic_regedit;
use ic_registry_canister_api::IPv4Config;
use ic_registry_resource_limits::ResourceLimits;
use ic_registry_subnet_features::{ChainKeyConfig, SubnetFeatures};
use ic_registry_subnet_type::SubnetType;
use ic_types::malicious_behavior::MaliciousBehavior;
Expand Down Expand Up @@ -480,6 +481,7 @@ pub struct Subnet {
pub max_instructions_per_round: Option<u64>,
pub max_instructions_per_install_code: Option<u64>,
pub features: Option<SubnetFeatures>,
pub resource_limits: Option<ResourceLimits>,
pub max_number_of_canisters: Option<u64>,
pub ssh_readonly_access: Vec<String>,
pub ssh_backup_access: Vec<String>,
Expand Down Expand Up @@ -509,6 +511,7 @@ impl Subnet {
max_instructions_per_round: None,
max_instructions_per_install_code: None,
features: None,
resource_limits: None,
max_number_of_canisters: None,
subnet_type,
canister_cycles_cost_schedule: CanisterCyclesCostSchedule::Normal,
Expand Down Expand Up @@ -676,6 +679,11 @@ impl Subnet {
self
}

pub fn with_resource_limits(mut self, resource_limits: ResourceLimits) -> Self {
self.resource_limits = Some(resource_limits);
self
}

pub fn with_max_number_of_canisters(mut self, max_number_of_canisters: u64) -> Self {
self.max_number_of_canisters = Some(max_number_of_canisters);
self
Expand Down Expand Up @@ -777,6 +785,7 @@ impl Default for Subnet {
max_instructions_per_round: None,
max_instructions_per_install_code: None,
features: None,
resource_limits: None,
max_number_of_canisters: None,
ssh_readonly_access: vec![],
ssh_backup_access: vec![],
Expand Down
14 changes: 14 additions & 0 deletions rs/tests/execution/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,20 @@ system_test(
],
)

system_test(
name = "resource_limits_test",
runtime_deps = UNIVERSAL_CANISTER_RUNTIME_DEPS,
deps = [
"//rs/config",
"//rs/registry/resource_limits",
"//rs/registry/subnet_type",
"//rs/tests/driver:ic-system-test-driver",
"//rs/types/types",
"//rs/universal_canister/lib",
"@crate_index//:anyhow",
],
)

system_test_nns(
name = "canister_migration_test",
enable_mainnet_nns_variant = False,
Expand Down
5 changes: 5 additions & 0 deletions rs/tests/execution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ futures = { workspace = true }
ic-agent = { workspace = true }
ic_consensus_system_test_utils = { path = "../consensus/utils" }
ic-management-canister-types-private = { path = "../../types/management_canister_types" }
ic-registry-resource-limits = { path = "../../registry/resource_limits" }
ic-registry-subnet-type = { path = "../../registry/subnet_type" }
ic-registry-nns-data-provider = { path = "../../registry/nns_data_provider" }
ic-system-test-driver = { path = "../driver" }
Expand Down Expand Up @@ -65,6 +66,10 @@ path = "inter_canister_queries_test.rs"
name = "max_number_of_canisters_test"
path = "max_number_of_canisters_test.rs"

[[bin]]
name = "resource_limits_test"
path = "resource_limits_test.rs"

[[bin]]
name = "system_api_security_test"
path = "system_api_security_test.rs"
Expand Down
84 changes: 84 additions & 0 deletions rs/tests/execution/resource_limits_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use anyhow::Result;
use ic_config::execution_environment::SUBNET_MEMORY_RESERVATION;
use ic_registry_resource_limits::ResourceLimits;
use ic_registry_subnet_type::SubnetType;
use ic_system_test_driver::driver::group::SystemTestGroup;
use ic_system_test_driver::driver::test_env_api::{GetFirstHealthyNodeSnapshot, HasPublicApiUrl};
use ic_system_test_driver::driver::{
ic::{InternetComputer, Subnet},
test_env::TestEnv,
};
use ic_system_test_driver::systest;
use ic_system_test_driver::util::*;
use ic_types::NumBytes;
use ic_universal_canister::wasm;

const MAXIMUM_CANISTER_STATE_SIZE: u64 = 1 << 30; // 1 GiB

fn maximum_state_size() -> NumBytes {
SUBNET_MEMORY_RESERVATION + NumBytes::new(MAXIMUM_CANISTER_STATE_SIZE)
}

fn main() -> Result<()> {
SystemTestGroup::new()
.with_setup(setup)
.add_test(systest!(memory_grow_fails_beyond_maximum_state_size))
.execute_from_args()?;

Ok(())
}

pub fn setup(env: TestEnv) {
let resource_limits = ResourceLimits {
maximum_state_size: Some(maximum_state_size()),
maximum_state_delta: None,
};
InternetComputer::new()
.add_subnet(
Subnet::fast_single_node(SubnetType::Application).with_resource_limits(resource_limits),
)
.setup_and_start(&env)
.expect("failed to setup IC under test");
}

pub fn memory_grow_fails_beyond_maximum_state_size(env: TestEnv) {
let logger = env.logger();
let node = env.get_first_healthy_node_snapshot();
let agent = node.build_default_agent();
block_on({
async move {
let canister =
UniversalCanister::new_with_retries(&agent, node.effective_canister_id(), &logger)
.await;

let stable_grow = |pages: u64| {
let canister = canister.clone(); // ensure owned inside async block
async move {
canister
.update(
wasm()
.stable64_grow(pages)
.int64_to_blob()
.append_and_reply()
.build(),
)
.await
.unwrap()
}
};

// sanity check: `ic0.stable64_grow` works for a small amount of pages
// and returns the previous stable memory size in pages (1)
let res = stable_grow(42).await;
assert_eq!(res, 1_u64.to_le_bytes());

// growing stable memory to `MAXIMUM_CANISTER_STATE_SIZE` should fail
// because other pieces of the canister state already take some memory usage
// and thus the total state size after the stable memory growth exceeds `MAXIMUM_CANISTER_STATE_SIZE`
let maximum_canister_state_size_in_wasm_pages = MAXIMUM_CANISTER_STATE_SIZE >> 16;
let res = stable_grow(maximum_canister_state_size_in_wasm_pages).await;
// `ic0.stable64_grow` should return -1 upon failure
assert_eq!(res, u64::MAX.to_le_bytes());
}
})
}
Loading