From 6bdaae9f03bfd57026d33d738168465e7a895f7e Mon Sep 17 00:00:00 2001 From: Leo Eichhorn Date: Wed, 15 May 2024 13:04:21 +0000 Subject: [PATCH] feat(schnorr): CON-1254 Make `AlgorithmId` configurable during pre-signature generation --- rs/consensus/src/ecdsa/payload_builder.rs | 25 ++- .../{quadruples.rs => pre_signatures.rs} | 183 +++++++++++++----- .../src/ecdsa/payload_builder/signatures.rs | 2 +- rs/consensus/src/ecdsa/test_utils.rs | 9 +- rs/consensus/src/ecdsa/utils.rs | 15 +- 5 files changed, 171 insertions(+), 63 deletions(-) rename rs/consensus/src/ecdsa/payload_builder/{quadruples.rs => pre_signatures.rs} (84%) diff --git a/rs/consensus/src/ecdsa/payload_builder.rs b/rs/consensus/src/ecdsa/payload_builder.rs index 16f20c7eee9..a0b4cd78d22 100644 --- a/rs/consensus/src/ecdsa/payload_builder.rs +++ b/rs/consensus/src/ecdsa/payload_builder.rs @@ -38,7 +38,7 @@ use std::time::Duration; mod errors; mod key_transcript; -mod quadruples; +mod pre_signatures; pub(super) mod resharing; pub(super) mod signatures; @@ -598,7 +598,7 @@ pub(crate) fn create_data_payload_helper_2( ); if matches!(certified_height, CertifiedHeight::ReachedSummaryHeight) { - quadruples::purge_old_key_quadruples(ecdsa_payload, all_signing_requests); + pre_signatures::purge_old_key_quadruples(ecdsa_payload, all_signing_requests); } // We count the number of quadruples in the payload that were already matched, @@ -617,14 +617,19 @@ pub(crate) fn create_data_payload_helper_2( } } - quadruples::make_new_quadruples_if_needed( + pre_signatures::make_new_pre_signatures_if_needed( ecdsa_config, ecdsa_payload, &matched_quadruples_per_key_id, ); let new_transcripts = [ - quadruples::update_quadruples_in_creation(ecdsa_payload, transcript_builder, height, log)?, + pre_signatures::update_quadruples_in_creation( + ecdsa_payload, + transcript_builder, + height, + log, + )?, key_transcript::update_next_key_transcripts( receivers, next_interval_registry_version, @@ -664,8 +669,8 @@ pub(crate) fn create_data_payload_helper_2( mod tests { use super::*; use crate::consensus::batch_delivery::generate_responses_to_sign_with_ecdsa_calls; - use crate::ecdsa::payload_builder::quadruples::test_utils::create_available_quadruple; - use crate::ecdsa::payload_builder::quadruples::test_utils::create_new_quadruple_in_creation; + use crate::ecdsa::payload_builder::pre_signatures::test_utils::create_available_quadruple; + use crate::ecdsa::payload_builder::pre_signatures::test_utils::create_new_quadruple_in_creation; use crate::ecdsa::test_utils::*; use crate::ecdsa::utils::block_chain_reader; use crate::ecdsa::utils::get_context_request_id; @@ -1212,7 +1217,7 @@ mod tests { // Add some quadruples in creation let block_reader = TestEcdsaBlockReader::new(); let (kappa_config_ref, _lambda_config_ref) = - quadruples::test_utils::create_new_quadruple_in_creation( + pre_signatures::test_utils::create_new_quadruple_in_creation( &subnet_nodes, env.newest_registry_version, &mut ecdsa_payload.uid_generator, @@ -1234,7 +1239,7 @@ mod tests { .idkg_transcripts .insert(kappa_config_ref.as_ref().transcript_id, kappa_transcript); let parent_block_height = Height::new(15); - let result = quadruples::update_quadruples_in_creation( + let result = pre_signatures::update_quadruples_in_creation( &mut ecdsa_payload, &transcript_builder, parent_block_height, @@ -1450,7 +1455,7 @@ mod tests { // Add some quadruples in creation let block_reader = TestEcdsaBlockReader::new(); let (kappa_config_ref, _lambda_config_ref) = - quadruples::test_utils::create_new_quadruple_in_creation( + pre_signatures::test_utils::create_new_quadruple_in_creation( &subnet_nodes, env.newest_registry_version, &mut ecdsa_payload.uid_generator, @@ -1472,7 +1477,7 @@ mod tests { .idkg_transcripts .insert(kappa_config_ref.as_ref().transcript_id, kappa_transcript); let parent_block_height = Height::new(15); - let result = quadruples::update_quadruples_in_creation( + let result = pre_signatures::update_quadruples_in_creation( &mut ecdsa_payload, &transcript_builder, parent_block_height, diff --git a/rs/consensus/src/ecdsa/payload_builder/quadruples.rs b/rs/consensus/src/ecdsa/payload_builder/pre_signatures.rs similarity index 84% rename from rs/consensus/src/ecdsa/payload_builder/quadruples.rs rename to rs/consensus/src/ecdsa/payload_builder/pre_signatures.rs index d8ca4a797a4..3bd55fef646 100644 --- a/rs/consensus/src/ecdsa/payload_builder/quadruples.rs +++ b/rs/consensus/src/ecdsa/payload_builder/pre_signatures.rs @@ -1,8 +1,8 @@ use super::EcdsaPayloadError; -use crate::ecdsa::pre_signer::EcdsaTranscriptBuilder; +use crate::ecdsa::{pre_signer::EcdsaTranscriptBuilder, utils::algorithm_for_key_id}; use ic_logger::{debug, error, ReplicaLogger}; -use ic_management_canister_types::{EcdsaKeyId, MasterPublicKeyId}; +use ic_management_canister_types::MasterPublicKeyId; use ic_registry_subnet_features::EcdsaConfig; use ic_replicated_state::metadata_state::subnet_call_context_manager::SignWithEcdsaContext; use ic_types::{ @@ -10,9 +10,10 @@ use ic_types::{ self, common::{PreSignatureInCreation, PreSignatureRef}, ecdsa::{PreSignatureQuadrupleRef, QuadrupleInCreation}, + schnorr::TranscriptInCreation, EcdsaUIDGenerator, HasMasterPublicKeyId, QuadrupleId, TranscriptAttributes, }, - crypto::{canister_threshold_sig::idkg::IDkgTranscript, AlgorithmId}, + crypto::canister_threshold_sig::idkg::IDkgTranscript, messages::CallbackId, Height, NodeId, RegistryVersion, }; @@ -243,83 +244,100 @@ pub(super) fn purge_old_key_quadruples( }); } -/// Creating new quadruples if necessary by updating quadruples_in_creation, -/// considering currently available quadruples, quadruples in creation, and +/// Creating new pre-signatures if necessary by updating pre_signatures_in_creation, +/// considering currently available pre-signatures, pre-signatures in creation, and /// ecdsa configs. -pub(super) fn make_new_quadruples_if_needed( +pub(super) fn make_new_pre_signatures_if_needed( ecdsa_config: &EcdsaConfig, ecdsa_payload: &mut idkg::EcdsaPayload, - matched_quadruples_per_key_id: &BTreeMap, + matched_pre_signatures_per_key_id: &BTreeMap, ) { for (key_id, key_transcript) in &ecdsa_payload.key_transcripts { let Some(key_transcript) = key_transcript.current.as_ref() else { continue; }; - let matched_quadruples = matched_quadruples_per_key_id + let matched_pre_signature = matched_pre_signatures_per_key_id .get(key_id) .copied() .unwrap_or_default(); - let unassigned_quadruples = ecdsa_payload + let unassigned_pre_signatures = ecdsa_payload .iter_pre_signature_ids(key_id) .count() - .saturating_sub(matched_quadruples); - - let MasterPublicKeyId::Ecdsa(key_id) = key_id else { - //TODO(CON-1254): Make pre-signature type creation configurable - continue; - }; + .saturating_sub(matched_pre_signature); let node_ids: Vec<_> = key_transcript.receivers().iter().copied().collect(); - let new_quadruples = make_new_quadruples_if_needed_helper( + let new_pre_signatures = make_new_pre_signatures_if_needed_helper( &node_ids, key_transcript.registry_version(), ecdsa_config, key_id, &mut ecdsa_payload.uid_generator, - unassigned_quadruples, + unassigned_pre_signatures, ); ecdsa_payload .pre_signatures_in_creation - .extend(new_quadruples); + .extend(new_pre_signatures); } } -fn make_new_quadruples_if_needed_helper( +fn make_new_pre_signatures_if_needed_helper( subnet_nodes: &[NodeId], registry_version: RegistryVersion, ecdsa_config: &EcdsaConfig, - key_id: &EcdsaKeyId, + key_id: &MasterPublicKeyId, uid_generator: &mut EcdsaUIDGenerator, - unassigned_quadruples: usize, + unassigned_pre_signatures: usize, ) -> BTreeMap { - let mut new_quadruples = BTreeMap::new(); - - let quadruples_to_create = ecdsa_config.quadruples_to_create_in_advance as usize; - if quadruples_to_create > unassigned_quadruples { - for _ in 0..(quadruples_to_create - unassigned_quadruples) { - let kappa_config = - new_random_unmasked_config(subnet_nodes, registry_version, uid_generator); - let lambda_config = new_random_config(subnet_nodes, registry_version, uid_generator); - new_quadruples.insert( - uid_generator.next_quadruple_id(), + let mut new_pre_signatures = BTreeMap::new(); + //TODO(CON-1292): Get correct number for key_id from ChainKeyConfig + let pre_signatures_to_create = ecdsa_config.quadruples_to_create_in_advance as usize; + if pre_signatures_to_create <= unassigned_pre_signatures { + return new_pre_signatures; + } + + for _ in 0..(pre_signatures_to_create - unassigned_pre_signatures) { + let pre_signature = match key_id { + MasterPublicKeyId::Ecdsa(ecdsa_key_id) => { + let kappa_config = new_random_unmasked_config( + key_id, + subnet_nodes, + registry_version, + uid_generator, + ); + let lambda_config = + new_random_config(key_id, subnet_nodes, registry_version, uid_generator); PreSignatureInCreation::Ecdsa(QuadrupleInCreation::new( - key_id.clone(), + ecdsa_key_id.clone(), kappa_config, lambda_config, - )), - ); - } + )) + } + MasterPublicKeyId::Schnorr(schnorr_key_id) => { + let blinder_config = new_random_unmasked_config( + key_id, + subnet_nodes, + registry_version, + uid_generator, + ); + PreSignatureInCreation::Schnorr(TranscriptInCreation::new( + schnorr_key_id.clone(), + blinder_config, + )) + } + }; + new_pre_signatures.insert(uid_generator.next_quadruple_id(), pre_signature); } - new_quadruples + new_pre_signatures } /// Create a new masked random transcript config and advance the /// next_unused_transcript_id by one. fn new_random_config( + key_id: &MasterPublicKeyId, subnet_nodes: &[NodeId], summary_registry_version: RegistryVersion, uid_generator: &mut idkg::EcdsaUIDGenerator, @@ -333,14 +351,14 @@ fn new_random_config( dealers, receivers, summary_registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, + algorithm_for_key_id(key_id), ) } /// Create a new random unmasked transcript config and advance the /// next_unused_transcript_id by one. -#[allow(dead_code)] pub fn new_random_unmasked_config( + key_id: &MasterPublicKeyId, subnet_nodes: &[NodeId], summary_registry_version: RegistryVersion, uid_generator: &mut idkg::EcdsaUIDGenerator, @@ -354,7 +372,7 @@ pub fn new_random_unmasked_config( dealers, receivers, summary_registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, + algorithm_for_key_id(key_id), ) } @@ -382,9 +400,18 @@ pub(super) mod test_utils { idkg::RandomUnmaskedTranscriptParams, idkg::RandomTranscriptParams, ) { - let kappa_config_ref = - new_random_unmasked_config(subnet_nodes, registry_version, uid_generator); - let lambda_config_ref = new_random_config(subnet_nodes, registry_version, uid_generator); + let kappa_config_ref = new_random_unmasked_config( + &MasterPublicKeyId::Ecdsa(key_id.clone()), + subnet_nodes, + registry_version, + uid_generator, + ); + let lambda_config_ref = new_random_config( + &MasterPublicKeyId::Ecdsa(key_id.clone()), + subnet_nodes, + registry_version, + uid_generator, + ); quadruples_in_creation.insert( uid_generator.next_quadruple_id(), PreSignatureInCreation::Ecdsa(QuadrupleInCreation::new( @@ -465,19 +492,22 @@ pub(super) mod tests { use super::*; use crate::ecdsa::test_utils::{ - fake_ecdsa_key_id, fake_sign_with_ecdsa_context_with_quadruple, set_up_ecdsa_payload, - EcdsaPayloadTestHelper, TestEcdsaBlockReader, TestEcdsaTranscriptBuilder, + fake_ecdsa_key_id, fake_schnorr_master_public_key_id, + fake_sign_with_ecdsa_context_with_quadruple, set_up_ecdsa_payload, EcdsaPayloadTestHelper, + TestEcdsaBlockReader, TestEcdsaTranscriptBuilder, }; use ic_crypto_test_utils_canister_threshold_sigs::{ generate_key_transcript, CanisterThresholdSigTestEnvironment, IDkgParticipants, }; use ic_crypto_test_utils_reproducible_rng::{reproducible_rng, ReproducibleRng}; use ic_logger::replica_logger::no_op_logger; - use ic_management_canister_types::EcdsaKeyId; - use ic_test_utilities_types::ids::subnet_test_id; + use ic_management_canister_types::{EcdsaKeyId, SchnorrAlgorithm}; + use ic_test_utilities_types::ids::{node_test_id, subnet_test_id}; use ic_types::{ - consensus::idkg::{common::PreSignatureRef, EcdsaPayload, UnmaskedTranscript}, - crypto::canister_threshold_sig::idkg::IDkgTranscriptId, + consensus::idkg::{ + common::PreSignatureRef, EcdsaPayload, IDkgTranscriptOperationRef, UnmaskedTranscript, + }, + crypto::{canister_threshold_sig::idkg::IDkgTranscriptId, AlgorithmId}, SubnetId, }; @@ -507,7 +537,60 @@ pub(super) mod tests { } #[test] - fn test_ecdsa_make_new_quadruples_if_needed() { + fn test_schnorr_make_new_pre_signatures_if_needed_helper() { + let nodes = &[node_test_id(0)]; + let registry_version = RegistryVersion::from(1); + let subnet_id = subnet_test_id(1); + let height = Height::new(10); + let mut uid_generator = EcdsaUIDGenerator::new(subnet_id, height); + let quadruples_to_create_in_advance = 4; + let ecdsa_config = EcdsaConfig { + quadruples_to_create_in_advance, + ..EcdsaConfig::default() + }; + + let mut create_pre_signatures = |key_id, unassigned| { + make_new_pre_signatures_if_needed_helper( + nodes, + registry_version, + &ecdsa_config, + key_id, + &mut uid_generator, + unassigned, + ) + }; + + let key_id_bib340 = fake_schnorr_master_public_key_id(SchnorrAlgorithm::Bip340Secp256k1); + let key_id_eddsa = fake_schnorr_master_public_key_id(SchnorrAlgorithm::Ed25519); + + for key_id in &[key_id_bib340, key_id_eddsa] { + assert!(create_pre_signatures(key_id, 4).is_empty()); + let pre_sigs = create_pre_signatures(key_id, 1); + assert_eq!(pre_sigs.len(), 3); + for pre_sig in pre_sigs.values() { + let PreSignatureInCreation::Schnorr(transcript) = pre_sig else { + panic!("Expected Schnorr pre-signature"); + }; + assert!(transcript.blinder_unmasked.is_none()); + assert_eq!( + key_id, + &MasterPublicKeyId::Schnorr(transcript.key_id.clone()) + ); + let config = transcript.blinder_unmasked_config.as_ref(); + assert_eq!(config.algorithm_id, algorithm_for_key_id(key_id)); + assert_eq!(config.registry_version, registry_version); + assert_eq!(config.dealers, config.receivers); + assert_eq!(config.dealers, BTreeSet::from(*nodes)); + assert_eq!( + config.operation_type_ref, + IDkgTranscriptOperationRef::RandomUnmasked + ); + } + } + } + + #[test] + fn test_ecdsa_make_new_pre_signatures_if_needed() { let mut rng = reproducible_rng(); let subnet_id = subnet_test_id(1); let height = Height::new(10); @@ -535,7 +618,7 @@ pub(super) mod tests { - (ecdsa_payload.available_pre_signatures.len() - quadruples_already_matched); assert_eq!(expected_quadruples_in_creation, 3); - make_new_quadruples_if_needed( + make_new_pre_signatures_if_needed( &ecdsa_config, &mut ecdsa_payload, &BTreeMap::from([( diff --git a/rs/consensus/src/ecdsa/payload_builder/signatures.rs b/rs/consensus/src/ecdsa/payload_builder/signatures.rs index 1e119b25ee7..254ed6a2b00 100644 --- a/rs/consensus/src/ecdsa/payload_builder/signatures.rs +++ b/rs/consensus/src/ecdsa/payload_builder/signatures.rs @@ -163,7 +163,7 @@ mod tests { }; use crate::ecdsa::{ - payload_builder::quadruples::test_utils::create_available_quadruple, + payload_builder::pre_signatures::test_utils::create_available_quadruple, test_utils::{ empty_ecdsa_payload_with_key_ids, empty_response, fake_completed_sign_with_ecdsa_context, fake_ecdsa_key_id, diff --git a/rs/consensus/src/ecdsa/test_utils.rs b/rs/consensus/src/ecdsa/test_utils.rs index 4f8e5e519d6..e97772dd80b 100644 --- a/rs/consensus/src/ecdsa/test_utils.rs +++ b/rs/consensus/src/ecdsa/test_utils.rs @@ -17,7 +17,7 @@ use ic_crypto_tree_hash::{LabeledTree, MixedHashTree}; use ic_interfaces::ecdsa::{EcdsaChangeAction, EcdsaPool}; use ic_interfaces_state_manager::{CertifiedStateSnapshot, Labeled}; use ic_logger::ReplicaLogger; -use ic_management_canister_types::{EcdsaKeyId, MasterPublicKeyId}; +use ic_management_canister_types::{EcdsaKeyId, MasterPublicKeyId, SchnorrAlgorithm, SchnorrKeyId}; use ic_metrics::MetricsRegistry; use ic_replicated_state::metadata_state::subnet_call_context_manager::{ EcdsaDealingsContext, SignWithEcdsaContext, @@ -1482,6 +1482,13 @@ pub(crate) fn fake_ecdsa_key_id() -> EcdsaKeyId { EcdsaKeyId::from_str("Secp256k1:some_key").unwrap() } +pub(crate) fn fake_schnorr_master_public_key_id(algorithm: SchnorrAlgorithm) -> MasterPublicKeyId { + MasterPublicKeyId::Schnorr(SchnorrKeyId { + algorithm, + name: String::from("some_schnorr_key"), + }) +} + pub(crate) fn create_reshare_request(num_nodes: u64, registry_version: u64) -> EcdsaReshareRequest { let key_id = fake_ecdsa_key_id(); EcdsaReshareRequest { diff --git a/rs/consensus/src/ecdsa/utils.rs b/rs/consensus/src/ecdsa/utils.rs index eb06a1c205b..d7044a99575 100644 --- a/rs/consensus/src/ecdsa/utils.rs +++ b/rs/consensus/src/ecdsa/utils.rs @@ -8,7 +8,7 @@ use ic_interfaces::consensus_pool::ConsensusBlockChain; use ic_interfaces::ecdsa::{EcdsaChangeAction, EcdsaChangeSet, EcdsaPool}; use ic_interfaces_registry::RegistryClient; use ic_logger::{warn, ReplicaLogger}; -use ic_management_canister_types::{EcdsaKeyId, MasterPublicKeyId}; +use ic_management_canister_types::{EcdsaCurve, EcdsaKeyId, MasterPublicKeyId, SchnorrAlgorithm}; use ic_protobuf::registry::subnet::v1 as pb; use ic_registry_client_helpers::ecdsa_keys::EcdsaKeysRegistry; use ic_registry_client_helpers::subnet::SubnetRegistry; @@ -29,6 +29,7 @@ use ic_types::crypto::canister_threshold_sig::idkg::{ IDkgTranscript, IDkgTranscriptOperation, InitialIDkgDealings, }; use ic_types::crypto::canister_threshold_sig::{ExtendedDerivationPath, MasterPublicKey}; +use ic_types::crypto::AlgorithmId; use ic_types::registry::RegistryClientError; use ic_types::{Height, RegistryVersion, SubnetId}; use phantom_newtype::Id; @@ -349,6 +350,18 @@ pub(crate) fn inspect_ecdsa_initializations( Ok(initial_dealings_per_key_id) } +pub(crate) fn algorithm_for_key_id(key_id: &MasterPublicKeyId) -> AlgorithmId { + match key_id { + MasterPublicKeyId::Ecdsa(ecdsa_key_id) => match ecdsa_key_id.curve { + EcdsaCurve::Secp256k1 => AlgorithmId::ThresholdEcdsaSecp256k1, + }, + MasterPublicKeyId::Schnorr(schnorr_key_id) => match schnorr_key_id.algorithm { + SchnorrAlgorithm::Bip340Secp256k1 => AlgorithmId::ThresholdSchnorrBip340, + SchnorrAlgorithm::Ed25519 => AlgorithmId::ThresholdEd25519, + }, + } +} + /// Return [`EcdsaConfig`] if it is enabled for the given subnet. pub(crate) fn get_ecdsa_config_if_enabled( subnet_id: SubnetId,