diff --git a/rs/registry/canister/canister/canister.rs b/rs/registry/canister/canister/canister.rs index b3517e71dd9c..119293d6e81b 100644 --- a/rs/registry/canister/canister/canister.rs +++ b/rs/registry/canister/canister/canister.rs @@ -45,7 +45,7 @@ use registry_canister::{ do_add_node_operator::AddNodeOperatorPayload, do_add_nodes_to_subnet::AddNodesToSubnetPayload, do_change_subnet_membership::ChangeSubnetMembershipPayload, - do_create_subnet::{CreateSubnetPayload, NewSubnet}, + do_create_subnet::{CreateSubnetPayload, CreateSubnetPayloadV2, NewSubnet}, do_delete_subnet::DeleteSubnetPayload, do_deploy_guestos_to_all_subnet_nodes::DeployGuestosToAllSubnetNodesPayload, do_deploy_guestos_to_all_unassigned_nodes::DeployGuestosToAllUnassignedNodesPayload, @@ -72,7 +72,7 @@ use registry_canister::{ DeployHostosToSomeNodes, UpdateNodesHostosVersionPayload, }, do_update_ssh_readonly_access_for_all_unassigned_nodes::UpdateSshReadOnlyAccessForAllUnassignedNodesPayload, - do_update_subnet::UpdateSubnetPayload, + do_update_subnet::{UpdateSubnetPayload, UpdateSubnetPayloadV2}, do_update_subnet_admins::UpdateSubnetAdminsPayload, do_update_unassigned_nodes_config::UpdateUnassignedNodesConfigPayload, firewall::{ @@ -637,7 +637,7 @@ fn add_node_operator_(payload: AddNodeOperatorPayload) { #[unsafe(export_name = "canister_update create_subnet")] fn create_subnet() { check_caller_is_governance_or_engine_controller_and_log("create_subnet"); - over_async(candid_one, |payload: CreateSubnetPayload| async move { + over_async(candid_one, |payload: CreateSubnetPayloadV2| async move { create_subnet_(payload).await }); } @@ -647,8 +647,10 @@ fn create_subnet() { /// panics instead of returning Err (ensuring any partial changes do not get /// committed). #[candid_method(update, rename = "create_subnet")] -async fn create_subnet_(payload: CreateSubnetPayload) -> Result { - let new_subnet = registry_mut().do_create_subnet(payload).await; +async fn create_subnet_(payload: CreateSubnetPayloadV2) -> Result { + let new_subnet = registry_mut() + .do_create_subnet(CreateSubnetPayload::from(payload)) + .await; recertify_registry(); Ok(new_subnet) } @@ -871,15 +873,15 @@ fn remove_node_operators_(payload: RemoveNodeOperatorsPayload) { #[unsafe(export_name = "canister_update update_subnet")] fn update_subnet() { check_caller_is_governance_or_engine_controller_and_log("update_subnet"); - over(candid_one, |payload: UpdateSubnetPayload| { + over(candid_one, |payload: UpdateSubnetPayloadV2| { update_subnet_(payload) }); } #[candid_method(update, rename = "update_subnet")] -fn update_subnet_(payload: UpdateSubnetPayload) { +fn update_subnet_(payload: UpdateSubnetPayloadV2) { let caller = dfn_core::api::caller(); - registry_mut().do_update_subnet(caller, payload); + registry_mut().do_update_subnet(caller, UpdateSubnetPayload::from(payload)); recertify_registry(); } diff --git a/rs/registry/canister/canister/registry.did b/rs/registry/canister/canister/registry.did index 399cf2f84b52..5e64b6731d77 100644 --- a/rs/registry/canister/canister/registry.did +++ b/rs/registry/canister/canister/registry.did @@ -86,9 +86,15 @@ type ResourceLimits = record { maximum_state_delta : opt nat64; }; -type CreateSubnetPayload = record { +type SubnetFeaturesV2 = record { + canister_sandboxing : opt bool; + http_requests : opt bool; + sev_enabled : opt bool; +}; + +type CreateSubnetPayloadV2 = record { unit_delay_millis : nat64; - features : SubnetFeatures; + features : SubnetFeaturesV2; max_ingress_bytes_per_message : nat64; max_ingress_bytes_per_block: opt nat64; dkg_dealings_per_block : nat64; @@ -494,10 +500,10 @@ type UpdateSshReadOnlyAccessForAllUnassignedNodesPayload = record { ssh_readonly_keys : vec text; }; -type UpdateSubnetPayload = record { +type UpdateSubnetPayloadV2 = record { unit_delay_millis : opt nat64; max_duplicity : opt nat32; - features : opt SubnetFeatures; + features : opt SubnetFeaturesV2; resource_limits : opt ResourceLimits; set_gossip_config_to_default : bool; halt_at_cup_height : opt bool; @@ -619,7 +625,7 @@ service : { change_subnet_membership : (ChangeSubnetMembershipPayload) -> (); clear_provisional_whitelist : () -> (); complete_canister_migration : (CompleteCanisterMigrationPayload) -> (); - create_subnet : (CreateSubnetPayload) -> (CreateSubnetResponse); + create_subnet : (CreateSubnetPayloadV2) -> (CreateSubnetResponse); delete_subnet : (DeleteSubnetPayload) -> (DeleteSubnetResponse); deploy_guestos_to_all_subnet_nodes : ( DeployGuestosToAllSubnetNodesPayload @@ -670,7 +676,7 @@ service : { update_ssh_readonly_access_for_all_unassigned_nodes : ( UpdateSshReadOnlyAccessForAllUnassignedNodesPayload ) -> (); - update_subnet : (UpdateSubnetPayload) -> (); + update_subnet : (UpdateSubnetPayloadV2) -> (); update_subnet_admins : (UpdateSubnetAdminsPayload) -> (); update_unassigned_nodes_config : (UpdateUnassignedNodesConfigPayload) -> (); swap_node_in_subnet_directly : (SwapNodeInSubnetDirectlyPayload) -> (); diff --git a/rs/registry/canister/canister/registry_test.did b/rs/registry/canister/canister/registry_test.did index 2f78294f4b19..4d4d6dd19ee2 100644 --- a/rs/registry/canister/canister/registry_test.did +++ b/rs/registry/canister/canister/registry_test.did @@ -86,9 +86,15 @@ type ResourceLimits = record { maximum_state_delta : opt nat64; }; -type CreateSubnetPayload = record { +type SubnetFeaturesV2 = record { + canister_sandboxing : opt bool; + http_requests : opt bool; + sev_enabled : opt bool; +}; + +type CreateSubnetPayloadV2 = record { unit_delay_millis : nat64; - features : SubnetFeatures; + features : SubnetFeaturesV2; max_ingress_bytes_per_message : nat64; max_ingress_bytes_per_block: opt nat64; dkg_dealings_per_block : nat64; @@ -494,10 +500,10 @@ type UpdateSshReadOnlyAccessForAllUnassignedNodesPayload = record { ssh_readonly_keys : vec text; }; -type UpdateSubnetPayload = record { +type UpdateSubnetPayloadV2 = record { unit_delay_millis : opt nat64; max_duplicity : opt nat32; - features : opt SubnetFeatures; + features : opt SubnetFeaturesV2; resource_limits : opt ResourceLimits; set_gossip_config_to_default : bool; halt_at_cup_height : opt bool; @@ -619,7 +625,7 @@ service : { change_subnet_membership : (ChangeSubnetMembershipPayload) -> (); clear_provisional_whitelist : () -> (); complete_canister_migration : (CompleteCanisterMigrationPayload) -> (); - create_subnet : (CreateSubnetPayload) -> (CreateSubnetResponse); + create_subnet : (CreateSubnetPayloadV2) -> (CreateSubnetResponse); delete_subnet : (DeleteSubnetPayload) -> (DeleteSubnetResponse); deploy_guestos_to_all_subnet_nodes : ( DeployGuestosToAllSubnetNodesPayload @@ -670,7 +676,7 @@ service : { update_ssh_readonly_access_for_all_unassigned_nodes : ( UpdateSshReadOnlyAccessForAllUnassignedNodesPayload ) -> (); - update_subnet : (UpdateSubnetPayload) -> (); + update_subnet : (UpdateSubnetPayloadV2) -> (); update_subnet_admins : (UpdateSubnetAdminsPayload) -> (); update_unassigned_nodes_config : (UpdateUnassignedNodesConfigPayload) -> (); swap_node_in_subnet_directly : (SwapNodeInSubnetDirectlyPayload) -> (); diff --git a/rs/registry/canister/src/mutations/do_create_subnet.rs b/rs/registry/canister/src/mutations/do_create_subnet.rs index 91a11caf9418..4efa3194c7b7 100644 --- a/rs/registry/canister/src/mutations/do_create_subnet.rs +++ b/rs/registry/canister/src/mutations/do_create_subnet.rs @@ -24,7 +24,9 @@ use ic_registry_keys::{ make_node_record_key, make_subnet_list_record_key, make_subnet_record_key, }; use ic_registry_resource_limits::ResourceLimits; -use ic_registry_subnet_features::{KeyConfig as KeyConfigInternal, SubnetFeatures}; +use ic_registry_subnet_features::{ + KeyConfig as KeyConfigInternal, SubnetFeatures, SubnetFeaturesV2, +}; use ic_registry_subnet_type::SubnetType; use ic_registry_transport::pb::v1::{RegistryMutation, RegistryValue, registry_mutation}; use on_wire::bytes; @@ -325,6 +327,96 @@ pub struct CreateSubnetPayload { pub gossip_retransmission_request_ms: u32, } +/// Input type for `create_subnet` canister method. +/// Same as `CreateSubnetPayload` but with `SubnetFeaturesV2` (all `Option`) for `features`. +#[derive(Clone, Eq, PartialEq, Debug, Default, CandidType, Deserialize, Serialize)] +pub struct CreateSubnetPayloadV2 { + pub node_ids: Vec, + + pub subnet_id_override: Option, + pub initial_dkg_subnet_id: Option, + + pub max_ingress_bytes_per_message: u64, + pub max_ingress_bytes_per_block: Option, + pub max_ingress_messages_per_block: u64, + pub max_block_payload_size: u64, + pub unit_delay_millis: u64, + pub initial_notary_delay_millis: u64, + pub replica_version_id: std::string::String, + pub dkg_interval_length: u64, + pub dkg_dealings_per_block: u64, + + pub start_as_nns: bool, + + pub subnet_type: SubnetType, + + pub is_halted: bool, + + pub features: SubnetFeaturesV2, + + pub max_number_of_canisters: u64, + pub ssh_readonly_access: Vec, + pub ssh_backup_access: Vec, + + pub chain_key_config: Option, + + pub canister_cycles_cost_schedule: Option, + + pub subnet_admins: Option>, + + pub resource_limits: Option, + + // TODO(NNS1-2444): The fields below are deprecated and they are not read anywhere. + pub ingress_bytes_per_block_soft_cap: u64, + pub gossip_max_artifact_streams_per_peer: u32, + pub gossip_max_chunk_wait_ms: u32, + pub gossip_max_duplicity: u32, + pub gossip_max_chunk_size: u32, + pub gossip_receive_check_cache_size: u32, + pub gossip_pfn_evaluation_period_ms: u32, + pub gossip_registry_poll_period_ms: u32, + pub gossip_retransmission_request_ms: u32, +} + +impl From for CreateSubnetPayload { + fn from(arg: CreateSubnetPayloadV2) -> Self { + Self { + node_ids: arg.node_ids, + subnet_id_override: arg.subnet_id_override, + initial_dkg_subnet_id: arg.initial_dkg_subnet_id, + max_ingress_bytes_per_message: arg.max_ingress_bytes_per_message, + max_ingress_bytes_per_block: arg.max_ingress_bytes_per_block, + max_ingress_messages_per_block: arg.max_ingress_messages_per_block, + max_block_payload_size: arg.max_block_payload_size, + unit_delay_millis: arg.unit_delay_millis, + initial_notary_delay_millis: arg.initial_notary_delay_millis, + replica_version_id: arg.replica_version_id, + dkg_interval_length: arg.dkg_interval_length, + dkg_dealings_per_block: arg.dkg_dealings_per_block, + start_as_nns: arg.start_as_nns, + subnet_type: arg.subnet_type, + is_halted: arg.is_halted, + features: SubnetFeatures::from(arg.features).into(), + max_number_of_canisters: arg.max_number_of_canisters, + ssh_readonly_access: arg.ssh_readonly_access, + ssh_backup_access: arg.ssh_backup_access, + chain_key_config: arg.chain_key_config, + canister_cycles_cost_schedule: arg.canister_cycles_cost_schedule, + subnet_admins: arg.subnet_admins, + resource_limits: arg.resource_limits, + ingress_bytes_per_block_soft_cap: arg.ingress_bytes_per_block_soft_cap, + gossip_max_artifact_streams_per_peer: arg.gossip_max_artifact_streams_per_peer, + gossip_max_chunk_wait_ms: arg.gossip_max_chunk_wait_ms, + gossip_max_duplicity: arg.gossip_max_duplicity, + gossip_max_chunk_size: arg.gossip_max_chunk_size, + gossip_receive_check_cache_size: arg.gossip_receive_check_cache_size, + gossip_pfn_evaluation_period_ms: arg.gossip_pfn_evaluation_period_ms, + gossip_registry_poll_period_ms: arg.gossip_registry_poll_period_ms, + gossip_retransmission_request_ms: arg.gossip_retransmission_request_ms, + } + } +} + #[derive(Clone, Eq, PartialEq, Debug, Default, CandidType, Deserialize, Serialize)] pub struct NewSubnet { pub new_subnet_id: Option, diff --git a/rs/registry/canister/src/mutations/do_update_subnet.rs b/rs/registry/canister/src/mutations/do_update_subnet.rs index b88f19e29f7d..5b041ccbbfa9 100644 --- a/rs/registry/canister/src/mutations/do_update_subnet.rs +++ b/rs/registry/canister/src/mutations/do_update_subnet.rs @@ -14,6 +14,7 @@ use ic_protobuf::{ use ic_registry_keys::{make_chain_key_enabled_subnet_list_key, make_subnet_record_key}; use ic_registry_subnet_features::{ ChainKeyConfig as ChainKeyConfigInternal, KeyConfig as KeyConfigInternal, SubnetFeatures, + SubnetFeaturesV2, }; use ic_registry_subnet_type::SubnetType; use ic_registry_transport::{pb::v1::RegistryMutation, upsert}; @@ -402,6 +403,92 @@ pub struct UpdateSubnetPayload { pub set_gossip_config_to_default: bool, } +/// Input type for `update_subnet` canister method. +/// Same as `UpdateSubnetPayload` but with `SubnetFeaturesV2` (all `Option`) for `features`. +#[derive(Clone, Eq, PartialEq, Debug, CandidType, Deserialize, Serialize)] +pub struct UpdateSubnetPayloadV2 { + pub subnet_id: SubnetId, + + pub max_ingress_bytes_per_message: Option, + pub max_ingress_bytes_per_block: Option, + pub max_ingress_messages_per_block: Option, + pub max_block_payload_size: Option, + pub unit_delay_millis: Option, + pub initial_notary_delay_millis: Option, + pub dkg_interval_length: Option, + pub dkg_dealings_per_block: Option, + + pub start_as_nns: Option, + + pub subnet_type: Option, + + pub is_halted: Option, + pub halt_at_cup_height: Option, + + pub features: Option, + pub resource_limits: Option, + + pub chain_key_config: Option, + pub chain_key_signing_enable: Option>, + pub chain_key_signing_disable: Option>, + + pub max_number_of_canisters: Option, + + pub ssh_readonly_access: Option>, + pub ssh_backup_access: Option>, + + pub subnet_admins: Option>, + + // TODO(NNS1-2444): The fields below are deprecated and they are not read anywhere. + pub max_artifact_streams_per_peer: Option, + pub max_chunk_wait_ms: Option, + pub max_duplicity: Option, + pub max_chunk_size: Option, + pub receive_check_cache_size: Option, + pub pfn_evaluation_period_ms: Option, + pub registry_poll_period_ms: Option, + pub retransmission_request_ms: Option, + pub set_gossip_config_to_default: bool, +} + +impl From for UpdateSubnetPayload { + fn from(arg: UpdateSubnetPayloadV2) -> Self { + Self { + subnet_id: arg.subnet_id, + max_ingress_bytes_per_message: arg.max_ingress_bytes_per_message, + max_ingress_bytes_per_block: arg.max_ingress_bytes_per_block, + max_ingress_messages_per_block: arg.max_ingress_messages_per_block, + max_block_payload_size: arg.max_block_payload_size, + unit_delay_millis: arg.unit_delay_millis, + initial_notary_delay_millis: arg.initial_notary_delay_millis, + dkg_interval_length: arg.dkg_interval_length, + dkg_dealings_per_block: arg.dkg_dealings_per_block, + start_as_nns: arg.start_as_nns, + subnet_type: arg.subnet_type, + is_halted: arg.is_halted, + halt_at_cup_height: arg.halt_at_cup_height, + features: arg.features.map(|f| SubnetFeatures::from(f).into()), + resource_limits: arg.resource_limits, + chain_key_config: arg.chain_key_config, + chain_key_signing_enable: arg.chain_key_signing_enable, + chain_key_signing_disable: arg.chain_key_signing_disable, + max_number_of_canisters: arg.max_number_of_canisters, + ssh_readonly_access: arg.ssh_readonly_access, + ssh_backup_access: arg.ssh_backup_access, + subnet_admins: arg.subnet_admins, + max_artifact_streams_per_peer: arg.max_artifact_streams_per_peer, + max_chunk_wait_ms: arg.max_chunk_wait_ms, + max_duplicity: arg.max_duplicity, + max_chunk_size: arg.max_chunk_size, + receive_check_cache_size: arg.receive_check_cache_size, + pfn_evaluation_period_ms: arg.pfn_evaluation_period_ms, + registry_poll_period_ms: arg.registry_poll_period_ms, + retransmission_request_ms: arg.retransmission_request_ms, + set_gossip_config_to_default: arg.set_gossip_config_to_default, + } + } +} + #[derive(Clone, Eq, PartialEq, Debug, Default, CandidType, Deserialize, Serialize)] pub struct ChainKeyConfig { pub key_configs: Vec, diff --git a/rs/registry/canister/unreleased_changelog.md b/rs/registry/canister/unreleased_changelog.md index 1a792512f212..c51d3642222f 100644 --- a/rs/registry/canister/unreleased_changelog.md +++ b/rs/registry/canister/unreleased_changelog.md @@ -16,6 +16,12 @@ on the process that this file is part of, see ## Changed +* The `features` field in `create_subnet` and `update_subnet` now has each + sub-field (`canister_sandboxing`, `http_requests`, `sev_enabled`) typed as + `opt bool` instead of `bool`. Omitting a sub-field (i.e., passing `null`) + leaves the corresponding feature at its default value. This is a + backward-compatible Candid interface change. + ## Deprecated ## Removed diff --git a/rs/registry/subnet_features/src/lib.rs b/rs/registry/subnet_features/src/lib.rs index b9d0dfb2a157..ad307456323b 100644 --- a/rs/registry/subnet_features/src/lib.rs +++ b/rs/registry/subnet_features/src/lib.rs @@ -10,6 +10,28 @@ use std::{convert::TryFrom, str::FromStr}; pub const DEFAULT_ECDSA_MAX_QUEUE_SIZE: u32 = 20; +/// Input type for subnet features in canister API calls. +/// All fields are optional; `None` means "use the default value". +#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, CandidType, Deserialize, Serialize)] +pub struct SubnetFeaturesV2 { + pub canister_sandboxing: Option, + pub http_requests: Option, + pub sev_enabled: Option, +} + +impl From for SubnetFeatures { + fn from(input: SubnetFeaturesV2) -> Self { + let default = SubnetFeatures::default(); + Self { + canister_sandboxing: input + .canister_sandboxing + .unwrap_or(default.canister_sandboxing), + http_requests: input.http_requests.unwrap_or(default.http_requests), + sev_enabled: input.sev_enabled.unwrap_or(default.sev_enabled), + } + } +} + /// List of features that can be enabled or disabled on the given subnet. #[derive(Copy, Clone, Eq, PartialEq, Debug, CandidType, Deserialize, Serialize)] #[serde(default)]