diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index 033611a1c..bea6463de 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -18,13 +18,12 @@ // only compile when testing or when the test_helpers feature is enabled #![cfg(any(test, feature = "test_helpers"))] +#[cfg(test)] +use crate::helpers::tests::entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec; use crate::{ app, chain_api::{ - entropy::{ - self, runtime_types::bounded_collections::bounded_vec::BoundedVec, - runtime_types::pallet_staking_extension::pallet::JumpStartStatus, - }, + entropy::{self, runtime_types::pallet_staking_extension::pallet::JumpStartStatus}, EntropyConfig, }, get_signer, @@ -35,7 +34,6 @@ use crate::{ }, logger::{Instrumentation, Logger}, substrate::submit_transaction, - validator::get_signer_and_x25519_secret_from_mnemonic, }, signing_client::ListenerState, AppState, @@ -46,7 +44,7 @@ use entropy_kvdb::{encrypted_sled::PasswordMethod, get_db_path, kv_manager::KvMa use entropy_protocol::PartyId; #[cfg(test)] use entropy_shared::EncodedVerifyingKey; -use entropy_shared::{EVE_VERIFYING_KEY, NETWORK_PARENT_KEY}; +use entropy_shared::NETWORK_PARENT_KEY; use std::{fmt, time::Duration}; use subxt::{ backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, diff --git a/crates/threshold-signature-server/src/signing_client/tests.rs b/crates/threshold-signature-server/src/signing_client/tests.rs index bf2165fab..5ed3be78f 100644 --- a/crates/threshold-signature-server/src/signing_client/tests.rs +++ b/crates/threshold-signature-server/src/signing_client/tests.rs @@ -45,10 +45,12 @@ use sp_keyring::AccountKeyring; async fn test_proactive_refresh() { initialize_test_logger().await; clean_tests(); - let _cxt = &test_node_process_testing_state(ChainSpecType::Integration, false).await[0]; + let _cxt = + &test_node_process_testing_state(ChainSpecType::IntegrationJumpStarted, false).await[0]; let (validator_ips, _ids) = - spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; let signing_committee_ips = &validator_ips[..3].to_vec(); let client = reqwest::Client::new(); diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index d8ae28a9d..19284fc78 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -44,6 +44,7 @@ use subxt::{ OnlineClient, }; use synedrion::{KeyResharingInputs, NewHolder, OldHolder}; +use x25519_dalek::StaticSecret; /// HTTP POST endpoint called by the off-chain worker (propagation pallet) during network reshare. /// @@ -61,20 +62,48 @@ pub async fn new_reshare( let rpc = get_rpc(&app_state.configuration.endpoint).await?; validate_new_reshare(&api, &rpc, &data, &app_state.kv_store).await?; + let (signer, x25519_secret_key) = get_signer_and_x25519_secret(&app_state.kv_store) + .await + .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let next_signers_query = entropy::storage().staking_extension().next_signers(); let next_signers = query_chain(&api, &rpc, next_signers_query, None) .await? .ok_or_else(|| ValidatorErr::ChainFetch("Error getting next signers"))? .next_signers; - let validators_info = get_validators_info(&api, &rpc, next_signers) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; - let (signer, x25519_secret_key) = get_signer_and_x25519_secret(&app_state.kv_store) - .await - .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let is_proper_signer = validators_info + .iter() + .any(|validator_info| validator_info.tss_account == *signer.account_id()); + + if !is_proper_signer { + return Ok(StatusCode::MISDIRECTED_REQUEST); + } + + // Do reshare in a separate task so we can already respond + tokio::spawn(async move { + if let Err(err) = + do_reshare(api, &rpc, signer, &x25519_secret_key, data, validators_info, app_state) + .await + { + tracing::error!("Error during reshare: {err}"); + } + }); + Ok(StatusCode::OK) +} +async fn do_reshare( + api: OnlineClient, + rpc: &LegacyRpcMethods, + signer: PairSigner, + x25519_secret_key: &StaticSecret, + data: OcwMessageReshare, + validators_info: Vec, + app_state: AppState, +) -> Result<(), ValidatorErr> { let verifying_key_query = entropy::storage().staking_extension().jump_start_progress(); let parent_key_details = query_chain(&api, &rpc, verifying_key_query, None) .await? @@ -92,15 +121,6 @@ pub async fn new_reshare( .map_err(|_| ValidatorErr::Conversion("Verifying key conversion"))?, ) .map_err(|e| ValidatorErr::VerifyingKeyError(e.to_string()))?; - - let is_proper_signer = validators_info - .iter() - .any(|validator_info| validator_info.tss_account == *signer.account_id()); - - if !is_proper_signer { - return Ok(StatusCode::MISDIRECTED_REQUEST); - } - let my_stash_address = get_stash_address(&api, &rpc, signer.account_id()) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; @@ -159,6 +179,7 @@ pub async fn new_reshare( converted_validator_info.push(validator_info.clone()); tss_accounts.push(validator_info.tss_account.clone()); } + let channels = get_channels( &app_state.listener_state, converted_validator_info, @@ -168,7 +189,6 @@ pub async fn new_reshare( &x25519_secret_key, ) .await?; - let (new_key_share, aux_info) = execute_reshare(session_id.clone(), channels, signer.signer(), inputs, &new_holders, None) .await?; @@ -186,7 +206,7 @@ pub async fn new_reshare( // TODO: Error handling really complex needs to be thought about. confirm_key_reshare(&api, &rpc, &signer).await?; - Ok(StatusCode::OK) + Ok(()) } /// HTTP POST endpoint called by the off-chain worker (propagation pallet) after a network key reshare. @@ -199,7 +219,10 @@ pub async fn rotate_network_key( // validate from chain let api = get_api(&app_state.configuration.endpoint).await?; let rpc = get_rpc(&app_state.configuration.endpoint).await?; + + tracing::info!("Rotate network key called"); validate_rotate_network_key(&api, &rpc).await?; + tracing::info!("Rotate network key validated"); let (signer, _) = get_signer_and_x25519_secret(&app_state.kv_store) .await @@ -221,10 +244,12 @@ pub async fn rotate_network_key( ) .await?; + tracing::info!("Proper signer: {is_proper_signer}"); + if !is_proper_signer { return Ok(StatusCode::MISDIRECTED_REQUEST); } - + tracing::info!("Rotating network key"); let network_parent_key_heading = hex::encode(NETWORK_PARENT_KEY); let next_network_parent_key_heading = hex::encode(NEXT_NETWORK_PARENT_KEY); @@ -238,6 +263,7 @@ pub async fn rotate_network_key( let reservation = app_state.kv_store.kv().reserve_key(network_parent_key_heading).await?; app_state.kv_store.kv().put(reservation, new_parent_key).await?; + tracing::info!("Rotated network key"); Ok(StatusCode::OK) } diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 142a6bdd6..a1bbf8846 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -28,9 +28,7 @@ use crate::{ }; use entropy_client::{ self as test_client, - chain_api::{ - entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, EntropyConfig, - }, + chain_api::entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, }; use entropy_client::{ chain_api::{ @@ -40,14 +38,8 @@ use entropy_client::{ substrate::query_chain, Hasher, }; -use entropy_kvdb::{ - clean_tests, - kv_manager::helpers::{deserialize, serialize}, -}; -use entropy_protocol::KeyShareWithAuxInfo; -use entropy_shared::{ - OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, -}; +use entropy_kvdb::clean_tests; +use entropy_shared::{OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY}; use entropy_testing_utils::{ constants::{ ALICE_STASH_ADDRESS, AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, RANDOM_ACCOUNT, @@ -60,7 +52,7 @@ use parity_scale_codec::Encode; use serial_test::serial; use sp_core::Pair; use sp_keyring::AccountKeyring; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use subxt::utils::AccountId32; use synedrion::k256::ecdsa::VerifyingKey; @@ -94,181 +86,88 @@ async fn test_reshare_basic() { let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); signers.push(server_info); } - println!("Signers {:?}", signers); - // A map of account IDs to serialized keyshares before the reshare - let mut key_shares_before = HashMap::new(); for signer in signers.iter() { let port = get_port(signer); let key_share = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; assert!(!key_share.is_empty()); - key_shares_before.insert(signer.tss_account.0, key_share); } - // let block_number = TEST_RESHARE_BLOCK_NUMBER; - // run_to_block(&rpc, block_number + 1).await; - - // for (tss_account, key_share_and_aux_before) in key_shares_before.iter() { - // let (key_share_before, aux_info_before): KeyShareWithAuxInfo = - // deserialize(key_share_and_aux_before).unwrap(); - // - // let port = get_port(signers.iter().find(|s| s.tss_account.0 == *tss_account).unwrap()); - // let key_share_and_aux_after = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; - // let (key_share_after, aux_info_after): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_after).unwrap(); - // - // // Check key share has not yet changed - // assert_eq!(serialize(&key_share_before).unwrap(), serialize(&key_share_after).unwrap()); - // // Check aux info has not yet changed - // assert_eq!(serialize(&aux_info_before).unwrap(), serialize(&aux_info_after).unwrap()); - // } - - // let new_signers = { - // let signer_query = entropy::storage().staking_extension().signers(); - // let signer_ids = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); - // let mut signers = Vec::new(); - // for signer in signer_ids { - // let query = entropy::storage().staking_extension().threshold_servers(signer); - // let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); - // signers.push(server_info); - // } - // signers - // }; - - loop { + let _new_signer_ids = loop { let new_signer_ids: HashSet<[u8; 32]> = { let signer_query = entropy::storage().staking_extension().signers(); let signer_ids = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); HashSet::from_iter(signer_ids.into_iter().map(|id| id.0)) }; if new_signer_ids != old_signer_ids { - break; + println!("Signers have changed"); + break new_signer_ids; } tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - // println!("Signers {:?}", signers); - // println!("NEW Signers {:?}", new_signers); - - // for signer in new_signers { - // let _ = client - // .post(format!( - // "http://{}/rotate_network_key", - // std::str::from_utf8(&signer.endpoint).unwrap() - // )) - // .send() - // .await - // .unwrap(); - // - // let key_share_and_aux_data_after_rotate = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - // let (key_share_after_rotate, aux_info_after_rotate): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_data_after_rotate).unwrap(); - // - // if let Some(key_share_and_aux_before) = key_shares_before.get(&signer.tss_account.0) { - // let (key_share_before, aux_info_before): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_before).unwrap(); - // // Check key share has changed - // assert_ne!( - // serialize(&key_share_before).unwrap(), - // serialize(&key_share_after_rotate).unwrap() - // ); - // // Check aux info has changed - // assert_ne!( - // serialize(&aux_info_before).unwrap(), - // serialize(&aux_info_after_rotate).unwrap() - // ); - // } - // - // // calling twice doesn't do anything - // let response = client - // .post(format!( - // "http://{}/rotate_network_key", - // std::str::from_utf8(&signer.endpoint).unwrap() - // )) - // .send() - // .await - // .unwrap(); - // - // assert_eq!(response.text().await.unwrap(), "Kv error: Recv Error: channel closed"); - // let key_share_and_aux_data_after_rotate_twice = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - // let (key_share_after_rotate_twice, aux_info_after_rotate_twice): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_data_after_rotate_twice).unwrap(); - // - // // Check key share has not changed - // assert_eq!( - // serialize(&key_share_after_rotate_twice).unwrap(), - // serialize(&key_share_after_rotate).unwrap() - // ); - // // Check aux info has not changed - // assert_eq!( - // serialize(&aux_info_after_rotate_twice).unwrap(), - // serialize(&aux_info_after_rotate).unwrap() - // ); + }; + + // Check that the new signers have keyshares + // for signer in new_signer_ids { + // let query = entropy::storage().staking_extension().threshold_servers(AccountId32(signer)); + // let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); + // let port = get_port(&server_info); + // let key_share = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; + // assert!(!key_share.is_empty()); // } - // - // let current_block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; - // // Check that rotating the network key wont work again later - // run_to_block(&rpc, current_block_number + 3).await; - // - // let response_stale = - // client.post("http://127.0.0.1:3001/rotate_network_key").send().await.unwrap(); - // - // assert_eq!(response_stale.text().await.unwrap(), "Data is stale"); - // - // // Now test signing a message with the new keyshare set - // let account_owner = AccountKeyring::Ferdie.pair(); - // let signature_request_author = AccountKeyring::One; - // // Store a program - // let program_pointer = test_client::store_program( - // &api, - // &rpc, - // &account_owner, - // TEST_PROGRAM_WASM_BYTECODE.to_owned(), - // vec![], - // vec![], - // vec![], - // 0u8, - // ) - // .await - // .unwrap(); - // - // // Register, using that program - // let (verifying_key, _registered_info) = test_client::register( - // &api, - // &rpc, - // account_owner.clone(), - // AccountId32(account_owner.public().0), - // BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), - // ) - // .await - // .unwrap(); - // - // // Sign a message - // let recoverable_signature = test_client::sign( - // &api, - // &rpc, - // signature_request_author.pair(), - // verifying_key, - // PREIMAGE_SHOULD_SUCCEED.to_vec(), - // Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), - // ) - // .await - // .unwrap(); - // - // // Check the signature - // let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); - // let recovery_key_from_sig = VerifyingKey::recover_from_prehash( - // &message_should_succeed_hash, - // &recoverable_signature.signature, - // recoverable_signature.recovery_id, - // ) - // .unwrap(); - // assert_eq!( - // verifying_key.to_vec(), - // recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() - // ); + + // Now test signing a message with the new keyshare set + let account_owner = AccountKeyring::Ferdie.pair(); + let signature_request_author = AccountKeyring::One; + // Store a program + let program_pointer = test_client::store_program( + &api, + &rpc, + &account_owner, + TEST_PROGRAM_WASM_BYTECODE.to_owned(), + vec![], + vec![], + vec![], + 0u8, + ) + .await + .unwrap(); + dbg!("stored program"); + // Register, using that program + let (verifying_key, _registered_info) = test_client::register( + &api, + &rpc, + account_owner.clone(), + AccountId32(account_owner.public().0), + BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), + ) + .await + .unwrap(); + + dbg!("registered"); + // Sign a message + let recoverable_signature = test_client::sign( + &api, + &rpc, + signature_request_author.pair(), + verifying_key, + PREIMAGE_SHOULD_SUCCEED.to_vec(), + Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), + ) + .await + .unwrap(); + + // Check the signature + let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); + let recovery_key_from_sig = VerifyingKey::recover_from_prehash( + &message_should_succeed_hash, + &recoverable_signature.signature, + recoverable_signature.recovery_id, + ) + .unwrap(); + assert_eq!( + verifying_key.to_vec(), + recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() + ); clean_tests(); }