diff --git a/crates/contract/src/lib.rs b/crates/contract/src/lib.rs index d11f6bce0..5b39b51fd 100644 --- a/crates/contract/src/lib.rs +++ b/crates/contract/src/lib.rs @@ -23,7 +23,10 @@ use crate::{ errors::{Error, RequestError}, primitives::ckd::{CKDRequest, CKDRequestArgs}, storage_keys::StorageKey, - tee::{quote::TeeQuoteStatus, tee_state::TeeState}, + tee::{ + quote::TeeQuoteStatus, + tee_state::{AttestationRecord, TeeState}, + }, update::{ProposeUpdateArgs, ProposedUpdates, Update, UpdateId}, }; @@ -55,10 +58,7 @@ use primitives::{ thresholds::{Threshold, ThresholdParameters}, }; use state::{running::RunningContractState, ProtocolContractState}; -use tee::{ - proposal::MpcDockerImageHash, - tee_state::{NodeId, TeeValidationResult}, -}; +use tee::{proposal::MpcDockerImageHash, tee_state::TeeValidationResult}; /// Gas required for a sign request const GAS_FOR_SIGN_CALL: Gas = Gas::from_tgas(15); @@ -584,11 +584,11 @@ impl MpcContract { // Add the participant information to the contract state let is_new_attestation = self.tee_state.add_participant( - NodeId { + tls_public_key, + AttestationRecord { account_id: account_id.clone(), - tls_public_key, + attestation: proposed_participant_attestation, }, - proposed_participant_attestation, ); // Both participants and non-participants can propose. Non-participants must pay for the @@ -991,7 +991,7 @@ impl MpcContract { /// Returns all accounts that have TEE attestations stored in the contract. /// Note: This includes both current protocol participants and accounts that may have /// submitted TEE information but are not currently part of the active participant set. - pub fn get_tee_accounts(&self) -> Vec { + pub fn get_tee_accounts(&self) -> Vec { log!("get_tee_accounts: signer={}", env::signer_account_id()); self.tee_state.get_tee_accounts() } @@ -1430,18 +1430,12 @@ impl MpcContract { )), ); } - // ensure that this node has a valid TEE quote - let node_id = NodeId { - account_id: account_id.clone(), - tls_public_key: expected_destination_node - .destination_node_info - .sign_pk - .clone(), - }; + // ensure that the node has a valid TEE quote + let public_key = &expected_destination_node.signer_account_pk; if !(matches!( self.tee_state.verify_tee_participant( - &node_id, + public_key, Duration::from_secs(self.config.tee_upgrade_deadline_duration_seconds) ), TeeQuoteStatus::Valid diff --git a/crates/contract/src/primitives/participants.rs b/crates/contract/src/primitives/participants.rs index c25866288..b98110cc2 100644 --- a/crates/contract/src/primitives/participants.rs +++ b/crates/contract/src/primitives/participants.rs @@ -3,9 +3,6 @@ use crate::errors::{Error, InvalidCandidateSet, InvalidParameters}; use near_sdk::{near, AccountId, PublicKey}; use std::{collections::BTreeSet, fmt::Display}; -#[cfg(any(test, feature = "test-utils"))] -use crate::tee::tee_state::NodeId; - pub mod hpke { pub type PublicKey = [u8; 32]; } @@ -204,13 +201,10 @@ impl Participants { } } - pub fn get_node_ids(&self) -> BTreeSet { + pub fn get_public_keys(&self) -> BTreeSet { self.participants() .iter() - .map(|(account_id, _, p_info)| NodeId { - account_id: account_id.clone(), - tls_public_key: p_info.sign_pk.clone(), - }) + .map(|(_, _, p_info)| p_info.sign_pk.clone()) .collect() } } diff --git a/crates/contract/src/tee/tee_state.rs b/crates/contract/src/tee/tee_state.rs index 3cc4f64b6..02cf1866a 100644 --- a/crates/contract/src/tee/tee_state.rs +++ b/crates/contract/src/tee/tee_state.rs @@ -14,16 +14,13 @@ use attestation::{ }; use borsh::{BorshDeserialize, BorshSerialize}; use mpc_primitives::hash::LauncherDockerComposeHash; -use near_sdk::{env, near, store::IterableMap, AccountId, PublicKey}; +use near_sdk::{env, store::IterableMap, AccountId, PublicKey}; use std::{collections::HashSet, time::Duration}; -#[near(serializers=[borsh, json])] -#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone, Hash)] -pub struct NodeId { - /// Operator account +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct AttestationRecord { pub account_id: AccountId, - /// TLS public key - pub tls_public_key: PublicKey, + pub attestation: Attestation, } pub enum TeeValidationResult { @@ -40,7 +37,7 @@ pub struct TeeState { pub(crate) allowed_docker_image_hashes: AllowedDockerImageHashes, pub(crate) allowed_launcher_compose_hashes: Vec, pub(crate) votes: CodeHashesVotes, - pub(crate) participants_attestations: IterableMap, + pub(crate) participants_attestations: IterableMap, } impl Default for TeeState { @@ -84,23 +81,22 @@ impl TeeState { /// Verifies the TEE quote and Docker image pub(crate) fn verify_tee_participant( &mut self, - node_id: &NodeId, + tls_public_key: &PublicKey, tee_upgrade_deadline_duration: Duration, ) -> TeeQuoteStatus { let allowed_mpc_docker_image_hashes = self.get_allowed_mpc_docker_image_hashes(tee_upgrade_deadline_duration); let allowed_launcher_compose_hashes = &self.allowed_launcher_compose_hashes; - let participant_attestation = self.participants_attestations.get(node_id); - let Some(participant_attestation) = participant_attestation else { + let participant_attestation = self.participants_attestations.get(tls_public_key); + let Some(attestation_record) = participant_attestation else { return TeeQuoteStatus::None; }; - let expected_report_data = - ReportData::V1(ReportDataV1::new(node_id.tls_public_key.clone())); + let expected_report_data = ReportData::V1(ReportDataV1::new(tls_public_key.clone())); let time_stamp_seconds = Self::current_time_seconds(); - let quote_result = participant_attestation.verify( + let quote_result = attestation_record.attestation.verify( expected_report_data, time_stamp_seconds, &allowed_mpc_docker_image_hashes, @@ -130,17 +126,11 @@ impl TeeState { let participants_with_valid_attestation: Vec<_> = participants .participants() .iter() - .filter(|(account_id, _, participant_info)| { - let tls_public_key = participant_info.sign_pk.clone(); + .filter(|(_, _, participant_info)| { + let tls_public_key = &participant_info.sign_pk; matches!( - self.verify_tee_participant( - &NodeId { - account_id: account_id.clone(), - tls_public_key - }, - tee_upgrade_deadline_duration - ), + self.verify_tee_participant(tls_public_key, tee_upgrade_deadline_duration), TeeQuoteStatus::Valid | TeeQuoteStatus::None ) }) @@ -166,11 +156,11 @@ impl TeeState { /// - `false` if the node already had an attestation (the existing one was replaced). pub fn add_participant( &mut self, - node_id: NodeId, - proposed_tee_participant: Attestation, + node_public_key: PublicKey, + proposed_tee_participant: AttestationRecord, ) -> bool { self.participants_attestations - .insert(node_id, proposed_tee_participant) + .insert(node_public_key, proposed_tee_participant) .is_none() } @@ -216,32 +206,26 @@ impl TeeState { /// Removes TEE information for accounts that are not in the provided participants list. /// This is used to clean up storage after a resharing concludes. pub fn clean_non_participants(&mut self, participants: &Participants) { - let participant_accounts: HashSet = participants + let participant_public_keys: HashSet<&PublicKey> = participants .participants() .iter() - .map(|(account_id, _, p_info)| NodeId { - account_id: account_id.clone(), - tls_public_key: p_info.sign_pk.clone(), - }) + .map(|(_, _, p_info)| &p_info.sign_pk) .collect(); - // Collect accounts to remove (can't remove while iterating) - let nodes_to_remove: Vec = self + let keys_to_remove: Vec<_> = self .participants_attestations .keys() - .filter(|node_id| !participant_accounts.contains(node_id)) + .filter(|public_key| participant_public_keys.contains(public_key)) .cloned() .collect(); - // Remove non-participant TEE information - for node_id in &nodes_to_remove { - self.participants_attestations.remove(node_id); + for public_key in keys_to_remove { + self.participants_attestations.remove(&public_key); } } - /// Returns the list of accounts that currently have TEE attestations stored. - /// Note: This may include accounts that are no longer active protocol participants. - pub fn get_tee_accounts(&self) -> Vec { + /// Returns the list of node public keys that have attestations associated with them. + pub fn get_tee_accounts(&self) -> Vec { self.participants_attestations.keys().cloned().collect() } } diff --git a/crates/contract/tests/sandbox/integration_tee_cleanup_after_resharing.rs b/crates/contract/tests/sandbox/integration_tee_cleanup_after_resharing.rs index 66804e8e5..18f5ee269 100644 --- a/crates/contract/tests/sandbox/integration_tee_cleanup_after_resharing.rs +++ b/crates/contract/tests/sandbox/integration_tee_cleanup_after_resharing.rs @@ -39,7 +39,7 @@ async fn test_tee_cleanup_after_full_resharing_flow() -> Result<()> { // extract initial participants: let initial_participants = assert_running_return_participants(&contract).await?; - let expected_node_ids = initial_participants.get_node_ids(); + let expected_node_ids = initial_participants.get_public_keys(); // submit attestations submit_tee_attestations(&contract, &mut env_accounts, &expected_node_ids).await?; @@ -51,7 +51,7 @@ async fn test_tee_cleanup_after_full_resharing_flow() -> Result<()> { // Add two prospective Participants // Note: this test fails if `vote_reshared` needs to clean up more than 3 attestations let (mut env_non_participant_accounts, non_participants) = gen_accounts(&worker, 1).await; - let non_participant_uids = non_participants.get_node_ids(); + let non_participant_uids = non_participants.get_public_keys(); submit_tee_attestations( &contract, &mut env_non_participant_accounts, @@ -95,7 +95,7 @@ async fn test_tee_cleanup_after_full_resharing_flow() -> Result<()> { .expect("Failed to insert participant"); } - let expected_tee_post_resharing = new_participants.get_node_ids(); + let expected_tee_post_resharing = new_participants.get_public_keys(); let new_threshold_parameters = ThresholdParameters::new(new_participants, Threshold::new(2)).unwrap(); @@ -116,7 +116,7 @@ async fn test_tee_cleanup_after_full_resharing_flow() -> Result<()> { .expect("Expected contract to be in Running state after resharing."); // Get current participants to compare - let final_participants_node_ids = final_participants.get_node_ids(); + let final_participants_node_ids = final_participants.get_public_keys(); // Verify only the new participants remain assert_eq!(final_participants_node_ids, expected_tee_post_resharing); // Verify TEE participants are properly cleaned up diff --git a/crates/contract/tests/sandbox/tee.rs b/crates/contract/tests/sandbox/tee.rs index 9daa7dfff..278925019 100644 --- a/crates/contract/tests/sandbox/tee.rs +++ b/crates/contract/tests/sandbox/tee.rs @@ -336,7 +336,7 @@ async fn test_clean_tee_status_succeeds_when_contract_calls_itself() -> Result<( let participant_uids = assert_running_return_participants(&contract) .await? - .get_node_ids(); + .get_public_keys(); submit_tee_attestations(&contract, &mut accounts, &participant_uids).await?; // Verify current participants have TEE data @@ -346,7 +346,7 @@ async fn test_clean_tee_status_succeeds_when_contract_calls_itself() -> Result<( const NUM_ADDITIONAL_ACCOUNTS: usize = 2; let (mut additional_accounts, additional_participants) = gen_accounts(&worker, NUM_ADDITIONAL_ACCOUNTS).await; - let additional_uids = additional_participants.get_node_ids(); + let additional_uids = additional_participants.get_public_keys(); submit_tee_attestations(&contract, &mut additional_accounts, &additional_uids).await?; // Verify we have TEE data for all accounts before cleanup diff --git a/crates/node/src/cli.rs b/crates/node/src/cli.rs index 4e84a4391..60a68b1e6 100644 --- a/crates/node/src/cli.rs +++ b/crates/node/src/cli.rs @@ -239,7 +239,7 @@ impl StartCmd { home_dir.clone(), config.indexer.clone(), config.my_near_account_id.clone(), - persistent_secrets.near_signer_key.clone(), + persistent_secrets.node_signing_key.clone(), respond_config, web_contract_sender, indexer_exit_sender, @@ -325,13 +325,13 @@ impl StartCmd { let (debug_request_sender, _) = tokio::sync::broadcast::channel(10); - let tls_public_key = secrets + let node_public_key = secrets .persistent_secrets - .p2p_private_key + .node_signing_key .verifying_key() .to_near_sdk_public_key()?; - let report_data = ReportData::new(tls_public_key); + let report_data = ReportData::new(node_public_key); let tee_authority = TeeAuthority::try_from(self.tee_authority)?; let attestation = tee_authority.generate_attestation(report_data).await?; let web_server = start_web_server( @@ -366,7 +366,7 @@ impl StartCmd { // submit remote attestation { - let account_public_key = secrets.persistent_secrets.near_signer_key.verifying_key(); + let account_public_key = secrets.persistent_secrets.node_signing_key.verifying_key(); submit_remote_attestation( indexer_api.txn_sender.clone(), @@ -472,7 +472,7 @@ impl Cli { &subdir, desired_responder_keys_per_participant, ) - .map(|secret| secret.p2p_private_key) + .map(|secret| secret.node_signing_key) }) .collect::, _>>()?; let configs = generate_test_p2p_configs( diff --git a/crates/node/src/config.rs b/crates/node/src/config.rs index 12ce7fbd4..7937cc841 100644 --- a/crates/node/src/config.rs +++ b/crates/node/src/config.rs @@ -240,8 +240,7 @@ impl SecretsConfig { /// from outside. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PersistentSecrets { - pub p2p_private_key: SigningKey, - pub near_signer_key: SigningKey, + pub node_signing_key: SigningKey, pub near_responder_keys: Vec, } @@ -270,15 +269,13 @@ impl PersistentSecrets { let mut os_rng = rand::rngs::OsRng; let p2p_secret = SigningKey::generate(&mut os_rng); - let near_signer_key = SigningKey::generate(&mut os_rng); let near_responder_keys = (0..number_of_responder_keys) .map(|_| SigningKey::generate(&mut os_rng)) .collect::>(); let secrets = PersistentSecrets { - p2p_private_key: p2p_secret, - near_signer_key, + node_signing_key: p2p_secret, near_responder_keys, }; @@ -374,8 +371,8 @@ mod tests { let actual_secrets = PersistentSecrets::generate_or_get_existing(temp_dir_path, 424)?; assert_eq!( - actual_secrets.p2p_private_key, - expected_secrets.p2p_private_key + actual_secrets.node_signing_key, + expected_secrets.node_signing_key ); assert_eq!( actual_secrets.near_signer_key, diff --git a/crates/node/src/coordinator.rs b/crates/node/src/coordinator.rs index 718a82716..d8c30cc51 100644 --- a/crates/node/src/coordinator.rs +++ b/crates/node/src/coordinator.rs @@ -268,7 +268,7 @@ where chain_txn_sender: TransactionSender, key_event_receiver: watch::Receiver, ) -> anyhow::Result { - let p2p_key = &secrets.persistent_secrets.p2p_private_key; + let p2p_key = &secrets.persistent_secrets.node_signing_key; let Some(mpc_config) = MpcConfig::from_participants_with_near_account_id( participants, &config_file.my_near_account_id, @@ -359,7 +359,7 @@ where .participants .retain(|p| participants_config.participants.contains(p)); - let p2p_key = &secrets.persistent_secrets.p2p_private_key; + let p2p_key = &secrets.persistent_secrets.node_signing_key; let Some(mpc_config) = MpcConfig::from_participants_with_near_account_id( participants_config, &config_file.my_near_account_id, diff --git a/crates/node/src/indexer/real.rs b/crates/node/src/indexer/real.rs index 4ba133945..a590bb46c 100644 --- a/crates/node/src/indexer/real.rs +++ b/crates/node/src/indexer/real.rs @@ -46,7 +46,7 @@ pub fn spawn_real_indexer( home_dir: PathBuf, indexer_config: IndexerConfig, my_near_account_id: AccountId, - account_secret_key: SigningKey, + node_signing_key: SigningKey, respond_config: RespondConfig, protocol_state_sender: watch::Sender, indexer_exit_sender: oneshot::Sender>, @@ -81,7 +81,7 @@ pub fn spawn_real_indexer( let txn_sender_result = TransactionProcessorHandle::start_transaction_processor( my_near_account_id_clone, - account_secret_key.clone(), + node_signing_key.clone(), respond_config_clone, Arc::clone(&indexer_state), ); diff --git a/crates/node/src/tests.rs b/crates/node/src/tests.rs index d6622259e..eca80e77d 100644 --- a/crates/node/src/tests.rs +++ b/crates/node/src/tests.rs @@ -351,7 +351,7 @@ impl IntegrationTestSetup { }; let secrets = SecretsConfig { persistent_secrets: PersistentSecrets { - p2p_private_key: p2p_key, + node_signing_key: p2p_key, near_signer_key: ed25519_dalek::SigningKey::generate(&mut OsRng), near_responder_keys: vec![ed25519_dalek::SigningKey::generate(&mut OsRng)], }, diff --git a/crates/node/src/web.rs b/crates/node/src/web.rs index 7c2fa2229..c477c2e42 100644 --- a/crates/node/src/web.rs +++ b/crates/node/src/web.rs @@ -122,27 +122,22 @@ async fn third_party_licenses() -> Html<&'static str> { #[derive(Clone, Serialize)] pub struct StaticWebData { - pub near_signer_public_key: VerifyingKey, - pub near_p2p_public_key: VerifyingKey, + pub node_public_key: VerifyingKey, pub near_responder_public_keys: Vec, pub tee_participant_info: Option, } struct PublicKeys { - near_signer_public_key: VerifyingKey, - near_p2p_public_key: VerifyingKey, + node_public_key: VerifyingKey, near_responder_public_keys: Vec, } fn get_public_keys(secrets_config: &SecretsConfig) -> PublicKeys { - let near_signer_public_key = secrets_config + let node_verifying_key = secrets_config .persistent_secrets - .near_signer_key - .verifying_key(); - let near_p2p_public_key = secrets_config - .persistent_secrets - .p2p_private_key + .node_signing_key .verifying_key(); + let near_responder_public_keys = secrets_config .persistent_secrets .near_responder_keys @@ -151,8 +146,7 @@ fn get_public_keys(secrets_config: &SecretsConfig) -> PublicKeys { .collect(); PublicKeys { - near_signer_public_key, - near_p2p_public_key, + node_public_key: node_verifying_key, near_responder_public_keys, } } @@ -161,8 +155,7 @@ impl StaticWebData { pub fn new(value: &SecretsConfig, tee_participant_info: Option) -> Self { let public_keys = get_public_keys(value); Self { - near_signer_public_key: public_keys.near_signer_public_key, - near_p2p_public_key: public_keys.near_p2p_public_key, + node_public_key: public_keys.node_public_key, near_responder_public_keys: public_keys.near_responder_public_keys, tee_participant_info, }