diff --git a/rs/execution_environment/src/canister_manager.rs b/rs/execution_environment/src/canister_manager.rs index 85d0f6016d58..5e1ad8738bdf 100644 --- a/rs/execution_environment/src/canister_manager.rs +++ b/rs/execution_environment/src/canister_manager.rs @@ -465,8 +465,11 @@ impl CanisterManager { | Ok(Ic00Method::SetupInitialDKG) | Ok(Ic00Method::SignWithECDSA) | Ok(Ic00Method::ComputeInitialIDkgDealings) + | Ok(Ic00Method::ReshareChainKey) | Ok(Ic00Method::SchnorrPublicKey) | Ok(Ic00Method::SignWithSchnorr) + | Ok(Ic00Method::VetKdPublicKey) + | Ok(Ic00Method::VetKdDeriveEncryptedKey) // "DepositCycles" can be called by anyone however as ingress message // cannot carry cycles, it does not make sense to allow them from users. | Ok(Ic00Method::DepositCycles) diff --git a/rs/execution_environment/src/execution_environment.rs b/rs/execution_environment/src/execution_environment.rs index 2ebe90074c0b..20de37490132 100644 --- a/rs/execution_environment/src/execution_environment.rs +++ b/rs/execution_environment/src/execution_environment.rs @@ -1242,6 +1242,16 @@ impl ExecutionEnvironment { } }, + Ok(Ic00Method::ReshareChainKey) + | Ok(Ic00Method::VetKdPublicKey) + | Ok(Ic00Method::VetKdDeriveEncryptedKey) => ExecuteSubnetMessageResult::Finished { + response: Err(UserError::new( + ErrorCode::CanisterRejectedMessage, + format!("{} API is not yet implemented.", msg.method_name()), + )), + refund: msg.take_cycles(), + }, + Ok(Ic00Method::ProvisionalCreateCanisterWithCycles) => { let res = ProvisionalCreateCanisterWithCyclesArgs::decode(payload).and_then(|args| { diff --git a/rs/execution_environment/src/execution_environment/tests.rs b/rs/execution_environment/src/execution_environment/tests.rs index 26c41de0aa02..aef67db214fa 100644 --- a/rs/execution_environment/src/execution_environment/tests.rs +++ b/rs/execution_environment/src/execution_environment/tests.rs @@ -7,7 +7,7 @@ use ic_management_canister_types::{ DerivationPath, EcdsaKeyId, EmptyBlob, FetchCanisterLogsRequest, HttpMethod, LogVisibilityV2, MasterPublicKeyId, Method, Payload as Ic00Payload, ProvisionalCreateCanisterWithCyclesArgs, ProvisionalTopUpCanisterArgs, SchnorrAlgorithm, SchnorrKeyId, TakeCanisterSnapshotArgs, - TransformContext, TransformFunc, IC_00, + TransformContext, TransformFunc, VetKdCurve, VetKdKeyId, IC_00, }; use ic_registry_routing_table::{canister_id_into_u64, CanisterIdRange, RoutingTable}; use ic_registry_subnet_type::SubnetType; @@ -173,6 +173,13 @@ fn sign_with_threshold_key_payload(method: Method, key_id: MasterPublicKeyId) -> key_id: into_inner_schnorr(key_id), } .encode(), + Method::VetKdDeriveEncryptedKey => ic00::VetKdDeriveEncryptedKeyArgs { + derivation_id: vec![], + encryption_public_key: vec![], + derivation_path: DerivationPath::new(vec![]), + key_id: into_inner_vetkd(key_id), + } + .encode(), _ => panic!("unexpected method"), } } @@ -2284,6 +2291,13 @@ fn make_schnorr_key(name: &str) -> MasterPublicKeyId { }) } +fn make_vetkd_key(name: &str) -> MasterPublicKeyId { + MasterPublicKeyId::VetKd(VetKdKeyId { + curve: VetKdCurve::Bls12_381_G2, + name: name.to_string(), + }) +} + fn into_inner_ecdsa(key_id: MasterPublicKeyId) -> EcdsaKeyId { match key_id { MasterPublicKeyId::Ecdsa(key) => key, @@ -2298,6 +2312,13 @@ fn into_inner_schnorr(key_id: MasterPublicKeyId) -> SchnorrKeyId { } } +fn into_inner_vetkd(key_id: MasterPublicKeyId) -> VetKdKeyId { + match key_id { + MasterPublicKeyId::VetKd(key) => key, + _ => panic!("unexpected key_id type"), + } +} + #[test] fn canister_output_queue_does_not_overflow_when_calling_ic00() { let own_subnet = subnet_test_id(1); @@ -3147,3 +3168,87 @@ fn test_sign_with_schnorr_api_is_enabled() { 1 ); } + +#[test] +fn test_vetkd_public_key_api_is_disabled() { + let own_subnet = subnet_test_id(1); + let nns_subnet = subnet_test_id(2); + let nns_canister = canister_test_id(0x10); + let mut test = ExecutionTestBuilder::new() + .with_own_subnet_id(own_subnet) + .with_nns_subnet_id(nns_subnet) + .with_caller(nns_subnet, nns_canister) + .build(); + test.inject_call_to_ic00( + Method::VetKdPublicKey, + ic00::VetKdPublicKeyArgs { + canister_id: None, + derivation_path: DerivationPath::new(vec![]), + key_id: into_inner_vetkd(make_vetkd_key("some_key")), + } + .encode(), + Cycles::new(0), + ); + test.execute_all(); + let response = test.xnet_messages()[0].clone(); + assert_eq!( + get_reject_message(response), + "vetkd_public_key API is not yet implemented.", + ) +} + +#[test] +fn test_vetkd_derive_encrypted_key_api_is_disabled() { + let own_subnet = subnet_test_id(1); + let nns_subnet = subnet_test_id(2); + let nns_canister = canister_test_id(0x10); + let mut test = ExecutionTestBuilder::new() + .with_own_subnet_id(own_subnet) + .with_nns_subnet_id(nns_subnet) + .with_caller(nns_subnet, nns_canister) + .build(); + let method = Method::VetKdDeriveEncryptedKey; + test.inject_call_to_ic00( + method, + sign_with_threshold_key_payload(method, make_vetkd_key("some_key")), + Cycles::new(0), + ); + test.execute_all(); + let response = test.xnet_messages()[0].clone(); + assert_eq!( + get_reject_message(response), + "vetkd_derive_encrypted_key API is not yet implemented.", + ) +} + +#[test] +fn reshare_chain_key_api_is_disabled() { + let own_subnet = subnet_test_id(1); + let nns_subnet = subnet_test_id(2); + let nns_canister = canister_test_id(0x10); + let nodes = vec![node_test_id(1), node_test_id(2)].into_iter().collect(); + let registry_version = RegistryVersion::from(100); + let mut test = ExecutionTestBuilder::new() + .with_own_subnet_id(own_subnet) + .with_nns_subnet_id(nns_subnet) + .with_caller(nns_subnet, nns_canister) + .build(); + let method = Method::ReshareChainKey; + test.inject_call_to_ic00( + method, + ic00::ReshareChainKeyArgs::new( + make_vetkd_key("some_key"), + nns_subnet, + nodes, + registry_version, + ) + .encode(), + Cycles::new(0), + ); + test.execute_all(); + let response = test.xnet_messages()[0].clone(); + assert_eq!( + get_reject_message(response), + "reshare_chain_key API is not yet implemented.", + ) +} diff --git a/rs/execution_environment/src/execution_environment_metrics.rs b/rs/execution_environment/src/execution_environment_metrics.rs index 77d1f2cafea5..3d6420c79a2e 100644 --- a/rs/execution_environment/src/execution_environment_metrics.rs +++ b/rs/execution_environment/src/execution_environment_metrics.rs @@ -186,6 +186,7 @@ impl ExecutionEnvironmentMetrics { | ic00::Method::UninstallCode | ic00::Method::ECDSAPublicKey | ic00::Method::SchnorrPublicKey + | ic00::Method::VetKdPublicKey | ic00::Method::UpdateSettings | ic00::Method::BitcoinGetBalance | ic00::Method::BitcoinGetUtxos @@ -216,7 +217,9 @@ impl ExecutionEnvironmentMetrics { | ic00::Method::HttpRequest | ic00::Method::SignWithECDSA | ic00::Method::SignWithSchnorr + | ic00::Method::VetKdDeriveEncryptedKey | ic00::Method::ComputeInitialIDkgDealings + | ic00::Method::ReshareChainKey | ic00::Method::BitcoinSendTransactionInternal | ic00::Method::BitcoinGetSuccessors => String::from("slow"), }; diff --git a/rs/execution_environment/src/ic00_permissions.rs b/rs/execution_environment/src/ic00_permissions.rs index 3aef0204944d..4020f67043f6 100644 --- a/rs/execution_environment/src/ic00_permissions.rs +++ b/rs/execution_environment/src/ic00_permissions.rs @@ -103,6 +103,11 @@ impl Ic00MethodPermissions { allow_remote_subnet_sender: true, allow_only_nns_subnet_sender: true, }, + Ic00Method::ReshareChainKey => Self { + method, + allow_remote_subnet_sender: true, + allow_only_nns_subnet_sender: true, + }, Ic00Method::SchnorrPublicKey => Self { method, allow_remote_subnet_sender: true, @@ -113,6 +118,16 @@ impl Ic00MethodPermissions { allow_remote_subnet_sender: true, allow_only_nns_subnet_sender: false, }, + Ic00Method::VetKdPublicKey => Self { + method, + allow_remote_subnet_sender: true, + allow_only_nns_subnet_sender: false, + }, + Ic00Method::VetKdDeriveEncryptedKey => Self { + method, + allow_remote_subnet_sender: true, + allow_only_nns_subnet_sender: false, + }, Ic00Method::BitcoinGetBalance => Self { method, allow_remote_subnet_sender: true, diff --git a/rs/execution_environment/src/scheduler.rs b/rs/execution_environment/src/scheduler.rs index 44ba71280082..8e89f0a8e8ed 100644 --- a/rs/execution_environment/src/scheduler.rs +++ b/rs/execution_environment/src/scheduler.rs @@ -2203,8 +2203,11 @@ fn can_execute_subnet_msg( | Ic00Method::UninstallCode | Ic00Method::UpdateSettings | Ic00Method::ComputeInitialIDkgDealings + | Ic00Method::ReshareChainKey | Ic00Method::SchnorrPublicKey | Ic00Method::SignWithSchnorr + | Ic00Method::VetKdPublicKey + | Ic00Method::VetKdDeriveEncryptedKey | Ic00Method::BitcoinGetBalance | Ic00Method::BitcoinGetUtxos | Ic00Method::BitcoinGetBlockHeaders @@ -2263,8 +2266,11 @@ fn get_instructions_limits_for_subnet_message( | SetupInitialDKG | SignWithECDSA | ComputeInitialIDkgDealings + | ReshareChainKey | SchnorrPublicKey | SignWithSchnorr + | VetKdPublicKey + | VetKdDeriveEncryptedKey | StartCanister | StopCanister | UninstallCode diff --git a/rs/execution_environment/tests/dts.rs b/rs/execution_environment/tests/dts.rs index 713af2c276f0..5f230404890e 100644 --- a/rs/execution_environment/tests/dts.rs +++ b/rs/execution_environment/tests/dts.rs @@ -1154,8 +1154,11 @@ fn dts_aborted_execution_does_not_block_subnet_messages() { | Method::SetupInitialDKG | Method::SignWithECDSA | Method::ComputeInitialIDkgDealings + | Method::ReshareChainKey | Method::SchnorrPublicKey | Method::SignWithSchnorr + | Method::VetKdPublicKey + | Method::VetKdDeriveEncryptedKey | Method::BitcoinGetBalance | Method::BitcoinGetUtxos | Method::BitcoinGetBlockHeaders diff --git a/rs/system_api/src/routing.rs b/rs/system_api/src/routing.rs index ff798b4071ae..a17a4235bb19 100644 --- a/rs/system_api/src/routing.rs +++ b/rs/system_api/src/routing.rs @@ -10,9 +10,10 @@ use ic_management_canister_types::{ ClearChunkStoreArgs, ComputeInitialIDkgDealingsArgs, DeleteCanisterSnapshotArgs, ECDSAPublicKeyArgs, InstallChunkedCodeArgs, InstallCodeArgsV2, ListCanisterSnapshotArgs, LoadCanisterSnapshotArgs, MasterPublicKeyId, Method as Ic00Method, NodeMetricsHistoryArgs, - Payload, ProvisionalTopUpCanisterArgs, SchnorrPublicKeyArgs, SignWithECDSAArgs, - SignWithSchnorrArgs, StoredChunksArgs, SubnetInfoArgs, TakeCanisterSnapshotArgs, - UninstallCodeArgs, UpdateSettingsArgs, UploadChunkArgs, + Payload, ProvisionalTopUpCanisterArgs, ReshareChainKeyArgs, SchnorrPublicKeyArgs, + SignWithECDSAArgs, SignWithSchnorrArgs, StoredChunksArgs, SubnetInfoArgs, + TakeCanisterSnapshotArgs, UninstallCodeArgs, UpdateSettingsArgs, UploadChunkArgs, + VetKdDeriveEncryptedKeyArgs, VetKdPublicKeyArgs, }; use ic_replicated_state::NetworkTopology; use itertools::Itertools; @@ -205,6 +206,15 @@ pub(super) fn resolve_destination( IDkgSubnetKind::OnlyHoldsKey, ) } + Ok(Ic00Method::ReshareChainKey) => { + let args = ReshareChainKeyArgs::decode(payload)?; + route_idkg_message( + &args.key_id, + network_topology, + &Some(args.subnet_id), + IDkgSubnetKind::OnlyHoldsKey, + ) + } Ok(Ic00Method::SchnorrPublicKey) => { let args = SchnorrPublicKeyArgs::decode(payload)?; route_idkg_message( @@ -223,6 +233,24 @@ pub(super) fn resolve_destination( IDkgSubnetKind::HoldsAndSignWithKey, ) } + Ok(Ic00Method::VetKdPublicKey) => { + let args = VetKdPublicKeyArgs::decode(payload)?; + route_idkg_message( + &MasterPublicKeyId::VetKd(args.key_id), + network_topology, + &None, + IDkgSubnetKind::OnlyHoldsKey, + ) + } + Ok(Ic00Method::VetKdDeriveEncryptedKey) => { + let args = VetKdDeriveEncryptedKeyArgs::decode(payload)?; + route_idkg_message( + &MasterPublicKeyId::VetKd(args.key_id), + network_topology, + &None, + IDkgSubnetKind::HoldsAndSignWithKey, + ) + } Ok(Ic00Method::UploadChunk) => { let args = UploadChunkArgs::decode(payload)?; let canister_id = args.get_canister_id(); @@ -279,11 +307,15 @@ pub(super) fn resolve_destination( )), } } + +/// TODO(CRP-2614): Rename to include VetKD enum IDkgSubnetKind { OnlyHoldsKey, HoldsAndSignWithKey, } +/// TODO(CRP-2614): Rename to include VetKD +/// TODO(CRP-2615): Unit tests for VetKD routing /// Routes to the `requested_subnet` if it holds the key (and fails if that /// subnet doesn't hold the key). If a `requested_subnet` is not provided, /// route to the first subnet enabled to sign with the given key. diff --git a/rs/system_api/src/sandbox_safe_system_state.rs b/rs/system_api/src/sandbox_safe_system_state.rs index 94fe92725011..32c169d47320 100644 --- a/rs/system_api/src/sandbox_safe_system_state.rs +++ b/rs/system_api/src/sandbox_safe_system_state.rs @@ -249,8 +249,11 @@ impl SystemStateChanges { | Ok(Ic00Method::SetupInitialDKG) | Ok(Ic00Method::ECDSAPublicKey) | Ok(Ic00Method::ComputeInitialIDkgDealings) + | Ok(Ic00Method::ReshareChainKey) | Ok(Ic00Method::SchnorrPublicKey) | Ok(Ic00Method::SignWithSchnorr) + | Ok(Ic00Method::VetKdPublicKey) + | Ok(Ic00Method::VetKdDeriveEncryptedKey) | Ok(Ic00Method::ProvisionalTopUpCanister) | Ok(Ic00Method::BitcoinSendTransactionInternal) | Ok(Ic00Method::BitcoinGetSuccessors) diff --git a/rs/types/management_canister_types/src/lib.rs b/rs/types/management_canister_types/src/lib.rs index f2c0fb7a6b57..63a06d95bc22 100644 --- a/rs/types/management_canister_types/src/lib.rs +++ b/rs/types/management_canister_types/src/lib.rs @@ -83,11 +83,18 @@ pub enum Method { UninstallCode, UpdateSettings, ComputeInitialIDkgDealings, + ReshareChainKey, // Schnorr interface. SchnorrPublicKey, SignWithSchnorr, + // VetKd interface. + #[strum(serialize = "vetkd_public_key")] + VetKdPublicKey, + #[strum(serialize = "vetkd_derive_encrypted_key")] + VetKdDeriveEncryptedKey, + // Bitcoin Interface. BitcoinGetBalance, BitcoinGetUtxos, @@ -2622,6 +2629,88 @@ impl ComputeInitialIDkgDealingsArgs { } } +/// Argument of the reshare_chain_key API. +/// `(record { +/// key_id: master_public_key_id; +/// subnet_id: principal; +/// nodes: vec principal; +/// registry_version: nat64; +/// })` +#[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] +pub struct ReshareChainKeyArgs { + pub key_id: MasterPublicKeyId, + pub subnet_id: SubnetId, + nodes: BoundedNodes, + registry_version: u64, +} + +impl Payload<'_> for ReshareChainKeyArgs {} + +impl ReshareChainKeyArgs { + pub fn new( + key_id: MasterPublicKeyId, + subnet_id: SubnetId, + nodes: BTreeSet, + registry_version: RegistryVersion, + ) -> Self { + Self { + key_id, + subnet_id, + nodes: BoundedNodes::new(nodes.iter().map(|id| id.get()).collect()), + registry_version: registry_version.get(), + } + } + + pub fn get_set_of_nodes(&self) -> Result, UserError> { + let mut set = BTreeSet::::new(); + for node_id in self.nodes.get().iter() { + if !set.insert(NodeId::new(*node_id)) { + return Err(UserError::new( + ErrorCode::InvalidManagementPayload, + format!( + "Expected a set of NodeIds. The NodeId {} is repeated", + node_id + ), + )); + } + } + Ok(set) + } + + pub fn get_registry_version(&self) -> RegistryVersion { + RegistryVersion::new(self.registry_version) + } +} + +/// Struct used to return the chain key resharing. +#[derive(Debug, Deserialize, Serialize)] +pub enum ReshareChainKeyResponse { + IDkg(InitialIDkgDealings), + NiDkg(InitialNiDkgTranscriptRecord), +} + +impl ReshareChainKeyResponse { + pub fn encode(&self) -> Vec { + let serde_encoded_bytes = self.encode_with_serde_cbor(); + Encode!(&serde_encoded_bytes).unwrap() + } + + fn encode_with_serde_cbor(&self) -> Vec { + serde_cbor::to_vec(self).unwrap() + } + + pub fn decode(blob: &[u8]) -> Result { + let serde_encoded_bytes = + Decode!([decoder_config()]; blob, Vec).map_err(candid_error_to_user_error)?; + serde_cbor::from_slice::(&serde_encoded_bytes).map_err(|err| { + UserError::new( + ErrorCode::InvalidManagementPayload, + format!("Payload deserialization error: '{}'", err), + ) + }) + } +} + /// Represents the argument of the sign_with_schnorr API. /// ```text /// (record { @@ -2715,6 +2804,72 @@ impl ComputeInitialIDkgDealingsResponse { } } +// Represents the argument of the vetkd_derive_encrypted_key API. +/// ```text +/// (record { +/// derivation_id: blob; +/// derivation_path : vec blob; +/// key_id : record { curve : vetkd_curve; name : text }; +/// encryption_public_key: blob; +/// }) +/// ``` +#[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] +pub struct VetKdDeriveEncryptedKeyArgs { + pub derivation_path: DerivationPath, + #[serde(with = "serde_bytes")] + pub derivation_id: Vec, + pub key_id: VetKdKeyId, + #[serde(with = "serde_bytes")] + pub encryption_public_key: Vec, +} + +impl Payload<'_> for VetKdDeriveEncryptedKeyArgs {} + +/// Struct used to return vet KD result. +/// ```text +/// (record { +/// encrypted_key : blob; +/// }) +/// ``` +#[derive(Debug, CandidType, Deserialize)] +pub struct VetKdDeriveEncryptedKeyResult { + #[serde(with = "serde_bytes")] + pub encrypted_key: Vec, +} + +impl Payload<'_> for VetKdDeriveEncryptedKeyResult {} + +/// Represents the argument of the vetkd_public_key API. +/// ```text +/// (record { +/// canister_id : opt canister_id; +/// derivation_path : vec blob; +/// key_id : record { curve : vetkd_curve; name : text }; +/// }) +/// ``` +#[derive(Eq, PartialEq, Debug, CandidType, Deserialize)] +pub struct VetKdPublicKeyArgs { + pub canister_id: Option, + pub derivation_path: DerivationPath, + pub key_id: VetKdKeyId, +} + +impl Payload<'_> for VetKdPublicKeyArgs {} + +/// Represents the response of the vetkd_public_key API. +/// ```text +/// (record { +/// public_key : blob; +/// }) +/// ``` +#[derive(Debug, CandidType, Deserialize)] +pub struct VetKdPublicKeyResult { + #[serde(with = "serde_bytes")] + pub public_key: Vec, +} + +impl Payload<'_> for VetKdPublicKeyResult {} + // Export the bitcoin types. pub use ic_btc_interface::{ GetBalanceRequest as BitcoinGetBalanceArgs, diff --git a/rs/types/types/src/messages/ingress_messages.rs b/rs/types/types/src/messages/ingress_messages.rs index d94911c8ef58..212bb2d285b8 100644 --- a/rs/types/types/src/messages/ingress_messages.rs +++ b/rs/types/types/src/messages/ingress_messages.rs @@ -560,8 +560,11 @@ pub fn extract_effective_canister_id( | Ok(Method::ECDSAPublicKey) | Ok(Method::SignWithECDSA) | Ok(Method::ComputeInitialIDkgDealings) + | Ok(Method::ReshareChainKey) | Ok(Method::SchnorrPublicKey) | Ok(Method::SignWithSchnorr) + | Ok(Method::VetKdPublicKey) + | Ok(Method::VetKdDeriveEncryptedKey) | Ok(Method::BitcoinGetBalance) | Ok(Method::BitcoinGetUtxos) | Ok(Method::BitcoinGetBlockHeaders) diff --git a/rs/types/types/src/messages/inter_canister.rs b/rs/types/types/src/messages/inter_canister.rs index 24d562bc1593..f4ac78cc6ab2 100644 --- a/rs/types/types/src/messages/inter_canister.rs +++ b/rs/types/types/src/messages/inter_canister.rs @@ -211,8 +211,11 @@ impl Request { | Ok(Method::ECDSAPublicKey) | Ok(Method::SignWithECDSA) | Ok(Method::ComputeInitialIDkgDealings) + | Ok(Method::ReshareChainKey) | Ok(Method::SchnorrPublicKey) | Ok(Method::SignWithSchnorr) + | Ok(Method::VetKdPublicKey) + | Ok(Method::VetKdDeriveEncryptedKey) | Ok(Method::BitcoinGetBalance) | Ok(Method::BitcoinGetUtxos) | Ok(Method::BitcoinGetBlockHeaders)