diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index 819bf8bae..b7e2f81d4 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -17,10 +17,11 @@ use crate::{ }; use entropy_testing_utils::{ constants::{TEST_PROGRAM_WASM_BYTECODE, TSS_ACCOUNTS}, - helpers::{derive_mock_pck_verifying_key, encode_verifying_key}, - jump_start_network, spawn_testing_validators, + helpers::{ + derive_mock_pck_verifying_key, encode_verifying_key, spawn_tss_nodes_and_start_chain, + }, substrate_context::test_context_stationary, - test_node_process_testing_state, ChainSpecType, + ChainSpecType, }; use serial_test::serial; use sp_core::{sr25519, Pair, H256}; @@ -134,18 +135,8 @@ async fn test_store_and_remove_program() { async fn test_remove_program_reference_counter() { let program_owner = AccountKeyring::Ferdie.pair(); - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // Jumpstart the network - let alice = AccountKeyring::Alice; - let signer = PairSigner::::new(alice.clone().into()); - jump_start_network(&api, &rpc, &signer).await; + let (_ctx, api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; // Store a program let program_pointer = store_program( diff --git a/crates/protocol/src/execute_protocol.rs b/crates/protocol/src/execute_protocol.rs index 835f3c673..0e2731f67 100644 --- a/crates/protocol/src/execute_protocol.rs +++ b/crates/protocol/src/execute_protocol.rs @@ -377,7 +377,7 @@ pub async fn execute_reshare( (ThresholdKeyShare, AuxInfo), ProtocolExecutionErr, > { - tracing::debug!("Executing proactive refresh"); + tracing::info!("Executing reshare"); tracing::debug!("Signing with {:?}", &threshold_pair.public()); let pair = PairWrapper(threshold_pair.clone()); @@ -394,9 +394,13 @@ pub async fn execute_reshare( .map_err(ProtocolExecutionErr::SessionCreation)?; let (new_key_share, chans) = execute_protocol_generic(chans, session, session_id_hash).await?; + + tracing::info!("Completed reshare protocol"); + let aux_info = if let Some(aux_info) = aux_info_option { aux_info } else { + tracing::info!("Executing aux gen session as part of reshare"); // Now run an aux gen session let session_id_hash_aux_data = session_id.blake2(Some(Subsession::AuxGen))?; let session = make_aux_gen_session( diff --git a/crates/shared/src/constants.rs b/crates/shared/src/constants.rs index 2b21c31ef..f4c710355 100644 --- a/crates/shared/src/constants.rs +++ b/crates/shared/src/constants.rs @@ -75,7 +75,7 @@ pub const MAX_SIGNERS: u8 = 15; pub const SIGNER_THRESHOLD: u8 = 2; /// For testing to line up chain mock data and reshare_test -pub const TEST_RESHARE_BLOCK_NUMBER: u32 = 22; +pub const TEST_RESHARE_BLOCK_NUMBER: u32 = 10; /// Program version number, must be incremented if version number changes pub const PROGRAM_VERSION_NUMBER: u8 = 0; diff --git a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/production/alice/keyshare-held-by-bob.keyshare deleted file mode 100644 index b0f17866c..000000000 Binary files a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/production/alice/keyshare-held-by-charlie.keyshare deleted file mode 100644 index 263030a0a..000000000 Binary files a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/production/alice/keyshare-held-by-dave.keyshare deleted file mode 100644 index 5ae742022..000000000 Binary files a/crates/testing-utils/keyshares/production/alice/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/production/bob/keyshare-held-by-alice.keyshare deleted file mode 100644 index 9a9121711..000000000 Binary files a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/production/bob/keyshare-held-by-charlie.keyshare deleted file mode 100644 index 4d086ba38..000000000 Binary files a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/production/bob/keyshare-held-by-dave.keyshare deleted file mode 100644 index 1d9d09043..000000000 Binary files a/crates/testing-utils/keyshares/production/bob/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-alice.keyshare deleted file mode 100644 index 2ee39900d..000000000 Binary files a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-bob.keyshare deleted file mode 100644 index 81c53944d..000000000 Binary files a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-dave.keyshare deleted file mode 100644 index 4bf9c7731..000000000 Binary files a/crates/testing-utils/keyshares/production/charlie/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/production/dave/keyshare-held-by-alice.keyshare deleted file mode 100644 index 4fc811e38..000000000 Binary files a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/production/dave/keyshare-held-by-bob.keyshare deleted file mode 100644 index 7eb75b726..000000000 Binary files a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/production/dave/keyshare-held-by-charlie.keyshare deleted file mode 100644 index e56bd6bdd..000000000 Binary files a/crates/testing-utils/keyshares/production/dave/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/production/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/production/keyshare-held-by-alice.keyshare new file mode 100644 index 000000000..bf4eddd15 Binary files /dev/null and b/crates/testing-utils/keyshares/production/keyshare-held-by-alice.keyshare differ diff --git a/crates/testing-utils/keyshares/production/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/production/keyshare-held-by-bob.keyshare new file mode 100644 index 000000000..32a400848 Binary files /dev/null and b/crates/testing-utils/keyshares/production/keyshare-held-by-bob.keyshare differ diff --git a/crates/testing-utils/keyshares/production/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/production/keyshare-held-by-charlie.keyshare new file mode 100644 index 000000000..dbcf40f53 Binary files /dev/null and b/crates/testing-utils/keyshares/production/keyshare-held-by-charlie.keyshare differ diff --git a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/test/alice/keyshare-held-by-bob.keyshare deleted file mode 100644 index b778dde64..000000000 Binary files a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/test/alice/keyshare-held-by-charlie.keyshare deleted file mode 100644 index 574820780..000000000 Binary files a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/test/alice/keyshare-held-by-dave.keyshare deleted file mode 100644 index 416898213..000000000 Binary files a/crates/testing-utils/keyshares/test/alice/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/test/bob/keyshare-held-by-alice.keyshare deleted file mode 100644 index c4741402a..000000000 Binary files a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/test/bob/keyshare-held-by-charlie.keyshare deleted file mode 100644 index 2b7f3bc90..000000000 Binary files a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/test/bob/keyshare-held-by-dave.keyshare deleted file mode 100644 index fbd5288bd..000000000 Binary files a/crates/testing-utils/keyshares/test/bob/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-alice.keyshare deleted file mode 100644 index d7d2ebd1f..000000000 Binary files a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-bob.keyshare deleted file mode 100644 index c801391de..000000000 Binary files a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-dave.keyshare b/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-dave.keyshare deleted file mode 100644 index 04df936fe..000000000 Binary files a/crates/testing-utils/keyshares/test/charlie/keyshare-held-by-dave.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/test/dave/keyshare-held-by-alice.keyshare deleted file mode 100644 index 1432a0152..000000000 Binary files a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-alice.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/test/dave/keyshare-held-by-bob.keyshare deleted file mode 100644 index e680f5b8d..000000000 Binary files a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-bob.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/test/dave/keyshare-held-by-charlie.keyshare deleted file mode 100644 index 090e697fa..000000000 Binary files a/crates/testing-utils/keyshares/test/dave/keyshare-held-by-charlie.keyshare and /dev/null differ diff --git a/crates/testing-utils/keyshares/test/keyshare-held-by-alice.keyshare b/crates/testing-utils/keyshares/test/keyshare-held-by-alice.keyshare new file mode 100644 index 000000000..c2a187223 Binary files /dev/null and b/crates/testing-utils/keyshares/test/keyshare-held-by-alice.keyshare differ diff --git a/crates/testing-utils/keyshares/test/keyshare-held-by-bob.keyshare b/crates/testing-utils/keyshares/test/keyshare-held-by-bob.keyshare new file mode 100644 index 000000000..69fc750de Binary files /dev/null and b/crates/testing-utils/keyshares/test/keyshare-held-by-bob.keyshare differ diff --git a/crates/testing-utils/keyshares/test/keyshare-held-by-charlie.keyshare b/crates/testing-utils/keyshares/test/keyshare-held-by-charlie.keyshare new file mode 100644 index 000000000..d4a1ea866 Binary files /dev/null and b/crates/testing-utils/keyshares/test/keyshare-held-by-charlie.keyshare differ diff --git a/crates/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 9da5745e3..54ccd8a18 100644 --- a/crates/testing-utils/src/helpers.rs +++ b/crates/testing-utils/src/helpers.rs @@ -16,8 +16,8 @@ use crate::{ chain_api::{get_api, get_rpc, EntropyConfig}, spawn_testing_validators, - substrate_context::{test_context_stationary, test_node_process_testing_state}, - ChainSpecType, + substrate_context::test_node_process_testing_state, + ChainSpecType, TestNodeProcess, }; use entropy_protocol::PartyId; use rand::{rngs::StdRng, SeedableRng}; @@ -26,31 +26,28 @@ pub use tdx_quote::encode_verifying_key; /// A helper for setting up tests which starts both a set of TS servers and a chain node and returns /// the chain API as well as IP addresses and PartyId of the started validators +/// +/// Note that since this function does not reside in entropy-tss, cfg(test) will be false when the +/// TSS nodes are set up, meaning the unsafe API will not be enabled pub async fn spawn_tss_nodes_and_start_chain( chain_spec_type: ChainSpecType, -) -> (OnlineClient, LegacyRpcMethods, Vec, Vec) { +) -> ( + Vec>, + OnlineClient, + LegacyRpcMethods, + Vec, + Vec, +) { let (validator_ips, validator_ids) = spawn_testing_validators(chain_spec_type).await; - let (api, rpc) = match chain_spec_type { - ChainSpecType::Development => { - let substrate_context = test_context_stationary().await; - ( - get_api(&substrate_context.node_proc.ws_url).await.unwrap(), - get_rpc(&substrate_context.node_proc.ws_url).await.unwrap(), - ) - }, - ChainSpecType::Integration => { - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &&test_node_process_testing_state(force_authoring).await[0]; - ( - get_api(&substrate_context.ws_url).await.unwrap(), - get_rpc(&substrate_context.ws_url).await.unwrap(), - ) - }, - }; - (api, rpc, validator_ips, validator_ids) + // Here we need to force authoring otherwise we won't be able to get our chain in the right + // state to be jump started. + let force_authoring = true; + let substrate_context = test_node_process_testing_state(chain_spec_type, force_authoring).await; + let api = get_api(&substrate_context[0].ws_url).await.unwrap(); + let rpc = get_rpc(&substrate_context[0].ws_url).await.unwrap(); + + (substrate_context, api, rpc, validator_ips, validator_ids) } /// Get the mock PCK that will be used for a given TSS account ID diff --git a/crates/testing-utils/src/lib.rs b/crates/testing-utils/src/lib.rs index 22329cb1c..812792b1e 100644 --- a/crates/testing-utils/src/lib.rs +++ b/crates/testing-utils/src/lib.rs @@ -22,8 +22,6 @@ pub mod create_test_keyshares; pub mod helpers; mod node_proc; pub mod substrate_context; -pub use entropy_tss::helpers::tests::{ - jump_start_network_with_signer as jump_start_network, spawn_testing_validators, ChainSpecType, -}; +pub use entropy_tss::helpers::tests::{spawn_testing_validators, ChainSpecType}; pub use node_proc::TestNodeProcess; pub use substrate_context::*; diff --git a/crates/testing-utils/src/substrate_context.rs b/crates/testing-utils/src/substrate_context.rs index 787ebec10..a8d19e90c 100644 --- a/crates/testing-utils/src/substrate_context.rs +++ b/crates/testing-utils/src/substrate_context.rs @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use entropy_tss::helpers::tests::ChainSpecType; use sp_keyring::AccountKeyring; use subxt::{config::substrate::SubstrateExtrinsicParams, OnlineClient}; @@ -110,6 +111,7 @@ pub async fn test_node_process_stationary() -> TestNodeProcess { /// /// Allowing `force_authoring` will produce blocks. pub async fn test_node_process_testing_state( + chain_spec_type: ChainSpecType, force_authoring: bool, ) -> Vec> { let alice_bootnode = Some( @@ -118,14 +120,14 @@ pub async fn test_node_process_testing_state( ); let result = test_node( AccountKeyring::Alice, - "--chain=integration-tests".to_string(), + format!("--chain={}", chain_spec_type), force_authoring, None, ) .await; let result_bob = test_node_process_with( AccountKeyring::Bob, - "--chain=integration-tests".to_string(), + format!("--chain={}", chain_spec_type), force_authoring, alice_bootnode.clone(), Some("http://127.0.0.1:3002".into()), @@ -133,7 +135,7 @@ pub async fn test_node_process_testing_state( .await; let result_charlie = test_node_process_with( AccountKeyring::Charlie, - "--chain=integration-tests".to_string(), + format!("--chain={}", chain_spec_type), force_authoring, alice_bootnode.clone(), Some("http://127.0.0.1:3003".into()), @@ -141,7 +143,7 @@ pub async fn test_node_process_testing_state( .await; let result_dave = test_node_process_with( AccountKeyring::Dave, - "--chain=integration-tests".to_string(), + format!("--chain={}", chain_spec_type), force_authoring, alice_bootnode.clone(), Some("http://127.0.0.1:3004".into()), diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index fc1eff9f8..4f59f8e0c 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -18,12 +18,14 @@ // 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, + self, + runtime_types::pallet_staking_extension::pallet::{JumpStartStatus, ServerInfo}, }, EntropyConfig, }, @@ -35,9 +37,7 @@ use crate::{ }, logger::{Instrumentation, Logger}, substrate::submit_transaction, - validator::get_signer_and_x25519_secret_from_mnemonic, }, - r#unsafe::api::UnsafeQuery, signing_client::ListenerState, AppState, }; @@ -47,8 +47,8 @@ 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 std::time::Duration; +use entropy_shared::NETWORK_PARENT_KEY; +use std::{fmt, time::Duration}; use subxt::{ backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, utils::AccountId32 as SubxtAccountId32, Config, OnlineClient, @@ -128,25 +128,33 @@ pub async fn create_clients( /// A way to specify which chainspec to use in testing #[derive(Copy, Clone, PartialEq)] pub enum ChainSpecType { - /// The development chainspec, which has 3 TSS nodes - Development, /// The integration test chainspec, which has 4 TSS nodes Integration, + /// The integration test chainspec, starting in a pre-jumpstarted state + IntegrationJumpStarted, +} + +impl fmt::Display for ChainSpecType { + /// This is used when specifying the chainspec type as a command line argument when starting the + /// Entropy chain for testing + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + ChainSpecType::Integration => "integration-tests", + ChainSpecType::IntegrationJumpStarted => "integration-tests-jumpstarted", + }, + ) + } } -/// Spawn either 3 or 4 TSS nodes depending on chain configuration, adding pre-stored keyshares if +/// Spawn 4 TSS nodes depending on chain configuration, adding pre-stored keyshares if /// desired pub async fn spawn_testing_validators( chain_spec_type: ChainSpecType, ) -> (Vec, Vec) { - let add_fourth_server = chain_spec_type == ChainSpecType::Integration; - - // spawn threshold servers - let mut ports = vec![3001i64, 3002, 3003]; - - if add_fourth_server { - ports.push(3004); - } + let ports = [3001i64, 3002, 3003, 3004]; let (alice_axum, alice_kv) = create_clients("validator1".to_string(), vec![], vec![], &Some(ValidatorName::Alice)).await; @@ -190,21 +198,24 @@ pub async fn spawn_testing_validators( axum::serve(listener_charlie, charlie_axum).await.unwrap(); }); - if add_fourth_server { - let (dave_axum, dave_kv) = - create_clients("validator4".to_string(), vec![], vec![], &Some(ValidatorName::Dave)) - .await; + let (dave_axum, dave_kv) = + create_clients("validator4".to_string(), vec![], vec![], &Some(ValidatorName::Dave)).await; - let listener_dave = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", ports[3])) - .await - .expect("Unable to bind to given server address."); - tokio::spawn(async move { - axum::serve(listener_dave, dave_axum).await.unwrap(); - }); - let dave_id = PartyId::new(SubxtAccountId32( - *get_signer(&dave_kv).await.unwrap().account_id().clone().as_ref(), - )); - ids.push(dave_id); + let listener_dave = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", ports[3])) + .await + .expect("Unable to bind to given server address."); + tokio::spawn(async move { + axum::serve(listener_dave, dave_axum).await.unwrap(); + }); + let dave_id = PartyId::new(SubxtAccountId32( + *get_signer(&dave_kv).await.unwrap().account_id().clone().as_ref(), + )); + ids.push(dave_id); + + if chain_spec_type == ChainSpecType::IntegrationJumpStarted { + put_keyshares_in_db(ValidatorName::Alice, alice_kv).await; + put_keyshares_in_db(ValidatorName::Bob, bob_kv).await; + put_keyshares_in_db(ValidatorName::Charlie, charlie_kv).await; } tokio::time::sleep(Duration::from_secs(1)).await; @@ -214,29 +225,18 @@ pub async fn spawn_testing_validators( } /// Add the pre-generated test keyshares to a kvdb -async fn put_keyshares_in_db(non_signer_name: ValidatorName, validator_name: ValidatorName) { +pub async fn put_keyshares_in_db(validator_name: ValidatorName, kvdb: KvManager) { let keyshare_bytes = { let project_root = project_root::get_project_root().expect("Error obtaining project root."); let file_path = project_root.join(format!( - "crates/testing-utils/keyshares/production/{}/keyshare-held-by-{}.keyshare", - non_signer_name, validator_name + "crates/testing-utils/keyshares/production/keyshare-held-by-{}.keyshare", + validator_name )); - println!("File path {:?}", file_path); std::fs::read(file_path).unwrap() }; - let unsafe_put = UnsafeQuery { key: hex::encode(NETWORK_PARENT_KEY), value: keyshare_bytes }; - let unsafe_put = serde_json::to_string(&unsafe_put).unwrap(); - - let port = 3001 + (validator_name as usize); - let http_client = reqwest::Client::new(); - http_client - .post(format!("http://127.0.0.1:{port}/unsafe/put")) - .header("Content-Type", "application/json") - .body(unsafe_put.clone()) - .send() - .await - .unwrap(); + let reservation = kvdb.kv().reserve_key(hex::encode(NETWORK_PARENT_KEY)).await.unwrap(); + kvdb.kv().put(reservation, keyshare_bytes).await.unwrap(); } /// Removes the program at the program hash @@ -275,47 +275,6 @@ pub async fn unsafe_get(client: &reqwest::Client, query_key: String, port: u32) get_result.bytes().await.unwrap().into() } -/// Mock the network being jump started by confirming a jump start even though no DKG took place, -/// so that we can use pre-store parent keyshares for testing -pub async fn jump_start_network_with_signer( - api: &OnlineClient, - rpc: &LegacyRpcMethods, - signer: &PairSigner, -) -> Option { - let jump_start_request = entropy::tx().registry().jump_start_network(); - let _result = submit_transaction(api, rpc, signer, &jump_start_request, None).await.unwrap(); - - let validators_names = - vec![ValidatorName::Alice, ValidatorName::Bob, ValidatorName::Charlie, ValidatorName::Dave]; - let mut non_signer = None; - for validator_name in validators_names.clone() { - let mnemonic = development_mnemonic(&Some(validator_name)); - let (tss_signer, _static_secret) = - get_signer_and_x25519_secret_from_mnemonic(&mnemonic.to_string()).unwrap(); - let jump_start_confirm_request = - entropy::tx().registry().confirm_jump_start(BoundedVec(EVE_VERIFYING_KEY.to_vec())); - - // Ignore the error as one confirmation will fail - if submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None) - .await - .is_err() - { - non_signer = Some(validator_name); - } - } - if let Some(non_signer) = non_signer { - for validator_name in validators_names { - if non_signer != validator_name { - put_keyshares_in_db(non_signer, validator_name).await; - } - } - } else { - tracing::error!("Missing non-signer - not storing pre-generated keyshares"); - } - - non_signer -} - /// Helper to store a program and register a user. Returns the verify key and program hash. #[cfg(test)] pub async fn store_program_and_register( @@ -403,3 +362,8 @@ async fn put_jumpstart_request_on_chain( let registering_tx = entropy::tx().registry().jump_start_network(); submit_transaction(api, rpc, &account, ®istering_tx, None).await.unwrap(); } + +/// Given a ServerInfo, get the port number +pub fn get_port(server_info: &ServerInfo) -> u32 { + std::str::from_utf8(&server_info.endpoint).unwrap().split(":").last().unwrap().parse().unwrap() +} diff --git a/crates/threshold-signature-server/src/signing_client/tests.rs b/crates/threshold-signature-server/src/signing_client/tests.rs index d5b9d454d..5ed3be78f 100644 --- a/crates/threshold-signature-server/src/signing_client/tests.rs +++ b/crates/threshold-signature-server/src/signing_client/tests.rs @@ -20,7 +20,7 @@ use crate::{ launch::LATEST_BLOCK_NUMBER_PROACTIVE_REFRESH, tests::{ initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, - unsafe_get, ChainSpecType, + unsafe_get, }, }, }; @@ -32,6 +32,7 @@ use entropy_shared::{ use entropy_testing_utils::{ constants::{TSS_ACCOUNTS, X25519_PUBLIC_KEYS}, substrate_context::{test_context_stationary, test_node_process_testing_state}, + ChainSpecType, }; use futures::future::join_all; use parity_scale_codec::Encode; @@ -44,9 +45,12 @@ use sp_keyring::AccountKeyring; async fn test_proactive_refresh() { initialize_test_logger().await; clean_tests(); - let _cxt = &test_node_process_testing_state(false).await[0]; + let _cxt = + &test_node_process_testing_state(ChainSpecType::IntegrationJumpStarted, false).await[0]; - let (validator_ips, _ids) = spawn_testing_validators(ChainSpecType::Integration).await; + let (validator_ips, _ids) = + 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/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 111efb54f..2f740428b 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -41,9 +41,9 @@ use entropy_testing_utils::{ PREIMAGE_SHOULD_SUCCEED, TEST_BASIC_TRANSACTION, TEST_INFINITE_LOOP_BYTECODE, TEST_PROGRAM_CUSTOM_HASH, TEST_PROGRAM_WASM_BYTECODE, X25519_PUBLIC_KEYS, }, - substrate_context::{ - test_context_stationary, test_node_process_testing_state, testing_context, - }, + helpers::spawn_tss_nodes_and_start_chain, + substrate_context::{test_context_stationary, testing_context}, + test_node_process_testing_state, ChainSpecType, }; use more_asserts as ma; use parity_scale_codec::{Decode, Encode}; @@ -69,7 +69,6 @@ use synedrion::k256::ecdsa::{RecoveryId, Signature as k256Signature, VerifyingKe use synedrion::{ecdsa::VerifyingKey as SynedrionVerifyingKey, DeriveChildKey}; use tokio_tungstenite::connect_async; -use crate::helpers::tests::do_jump_start; use crate::{ chain_api::{ entropy, entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec, @@ -87,8 +86,8 @@ use crate::{ signing::Hasher, substrate::{get_oracle_data, get_signers_from_chain, query_chain, submit_transaction}, tests::{ - initialize_test_logger, jump_start_network_with_signer, run_to_block, setup_client, - spawn_testing_validators, store_program_and_register, unsafe_get, ChainSpecType, + do_jump_start, get_port, initialize_test_logger, run_to_block, setup_client, + spawn_testing_validators, store_program_and_register, unsafe_get, }, user::compute_hash, validator::get_signer_and_x25519_secret_from_mnemonic, @@ -130,22 +129,13 @@ async fn test_signature_requests_fail_on_different_conditions() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + let (_ctx, entropy_api, rpc, validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = get_signer_and_x25519_secret_from_mnemonic(&mnemonic.to_string()).unwrap(); - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // We first need to jump start the network and grab the resulting network wide verifying key - // for later - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; @@ -366,22 +356,13 @@ async fn test_signature_requests_fail_validator_info_wrong() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = get_signer_and_x25519_secret_from_mnemonic(&mnemonic.to_string()).unwrap(); - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // We first need to jump start the network and grab the resulting network wide verifying key - // for later - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, tss_account) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; @@ -446,21 +427,11 @@ async fn signature_request_with_derived_account_works() { let bob = AccountKeyring::Bob; let charlie = AccountKeyring::Charlie; - let (_idsvalidator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // We first need to jump start the network and grab the resulting network wide verifying key - // for later - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); let (relayer_ip_and_key, _) = - validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; + validator_name_to_relayer_info(ValidatorName::Dave, &entropy_api, &rpc).await; // Register the user with a test program let (verifying_key, _program_hash) = @@ -493,16 +464,10 @@ async fn test_signing_fails_if_wrong_participants_are_used() { let one = AccountKeyring::Dave; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; let relayer_url = format!("http://{}/user/relay_tx", relayer_ip_and_key.0.clone()); @@ -612,15 +577,17 @@ async fn test_request_limit_are_updated_during_signing() { let two = AccountKeyring::Two; let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + let context = + test_node_process_testing_state(ChainSpecType::IntegrationJumpStarted, force_authoring) + .await; + let entropy_api = get_api(&context[0].ws_url).await.unwrap(); + let rpc = get_rpc(&context[0].ws_url).await.unwrap(); - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; // Register the user with a test program @@ -716,16 +683,10 @@ async fn test_fails_to_sign_if_non_signing_group_participants_are_used() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; // Register the user with a test program let (verifying_key, _program_hash) = store_program_and_register(&entropy_api, &rpc, &one.pair(), &two.pair()).await; @@ -816,16 +777,10 @@ async fn test_program_with_config() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; @@ -903,28 +858,38 @@ async fn test_jumpstart_network() { clean_tests(); let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).await; let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + let context = + test_node_process_testing_state(ChainSpecType::Integration, force_authoring).await; + let api = get_api(&context[0].ws_url).await.unwrap(); + let rpc = get_rpc(&context[0].ws_url).await.unwrap(); do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; + let signer_query = entropy::storage().staking_extension().signers(); + let signer_stash_accounts = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); let client = reqwest::Client::new(); - let response_key = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), 3001).await; + let mut verifying_key = Vec::new(); + for signer in signer_stash_accounts.iter() { + let query = entropy::storage().staking_extension().threshold_servers(signer); + let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); + let response_key = + unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&server_info)).await; + + // check to make sure keyshare is correct + let key_share: Option = + entropy_kvdb::kv_manager::helpers::deserialize(&response_key); + assert!(key_share.is_some()); + + verifying_key = + key_share.unwrap().0.verifying_key().to_encoded_point(true).as_bytes().to_vec(); + } - // check to make sure keyshare is correct - let key_share: Option = - entropy_kvdb::kv_manager::helpers::deserialize(&response_key); - assert_eq!(key_share.is_some(), true); let jump_start_progress_query = entropy::storage().staking_extension().jump_start_progress(); let jump_start_progress = query_chain(&api, &rpc, jump_start_progress_query, None).await.unwrap().unwrap(); - let verifying_key = - key_share.unwrap().0.verifying_key().to_encoded_point(true).as_bytes().to_vec(); assert_eq!(jump_start_progress.verifying_key.unwrap().0, verifying_key); clean_tests(); @@ -1052,20 +1017,13 @@ async fn test_fail_infinite_program() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - + let (_ctx, api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = get_signer_and_x25519_secret_from_mnemonic(&mnemonic.to_string()).unwrap(); - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - let non_signer = jump_start_network(&api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &api, &rpc).await; let program_hash = test_client::store_program( @@ -1150,19 +1108,10 @@ async fn test_device_key_proxy() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - // We first need to jump start the network and grab the resulting network wide verifying key - // for later - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; @@ -1288,13 +1237,10 @@ async fn test_faucet() { let two = AccountKeyring::Eve; let alice = AccountKeyring::Alice; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - let substrate_context = &test_node_process_testing_state(true).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); + let non_signer = ValidatorName::Dave; let (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &entropy_api, &rpc).await; @@ -1454,19 +1400,8 @@ async fn test_registration_flow() { let bob = AccountKeyring::Bob; let charlie = AccountKeyring::Charlie; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - // Here we need to use `--chain=integration-tests` and force authoring otherwise we won't be - // able to get our chain in the right state to be jump started. - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // We first need to jump start the network and grab the resulting network wide verifying key - // for later - jump_start_network(&entropy_api, &rpc).await; + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; let jump_start_progress_query = entropy::storage().staking_extension().jump_start_progress(); let jump_start_progress = @@ -1691,16 +1626,6 @@ pub async fn get_sign_tx_data( (validators_info, signature_request, validator_ips_and_keys) } -/// Mock jump starting the network -pub async fn jump_start_network( - api: &OnlineClient, - rpc: &LegacyRpcMethods, -) -> Option { - let alice = AccountKeyring::Alice; - let signer = PairSigner::::new(alice.clone().into()); - jump_start_network_with_signer(api, rpc, &signer).await -} - /// Takes a validator name and returns relayer info needed for tests pub async fn validator_name_to_relayer_info( validator_name: ValidatorName, diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index aff360cb5..179342402 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,22 +62,50 @@ 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) + let parent_key_details = query_chain(api, rpc, verifying_key_query, None) .await? .ok_or_else(|| ValidatorErr::ChainFetch("Parent verifying key error"))?; @@ -92,16 +121,7 @@ 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()) + let my_stash_address = get_stash_address(api, rpc, signer.account_id()) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; @@ -122,7 +142,7 @@ pub async fn new_reshare( validators_info.iter().cloned().map(|x| PartyId::new(x.tss_account)).collect(); // old holders -> next_signers - new_signers (will be at least t) let old_holders = - &prune_old_holders(&api, &rpc, data.new_signers, validators_info.clone()).await?; + &prune_old_holders(api, rpc, data.new_signers, validators_info.clone()).await?; let old_holders: BTreeSet = old_holders.iter().map(|x| PartyId::new(x.tss_account.clone())).collect(); @@ -132,7 +152,7 @@ pub async fn new_reshare( old_holders, }; let key_info_query = entropy::storage().parameters().signers_info(); - let threshold = query_chain(&api, &rpc, key_info_query, None) + let threshold = query_chain(api, rpc, key_info_query, None) .await? .ok_or_else(|| ValidatorErr::ChainFetch("Failed to get signers info"))? .threshold; @@ -158,16 +178,16 @@ 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, account_id, &session_id, &signer, - &x25519_secret_key, + x25519_secret_key, ) .await?; - let (new_key_share, aux_info) = execute_reshare(session_id.clone(), channels, signer.signer(), inputs, &new_holders, None) .await?; @@ -184,8 +204,8 @@ pub async fn new_reshare( app_state.kv_store.kv().put(reservation, serialized_key_share.clone()).await?; // TODO: Error handling really complex needs to be thought about. - confirm_key_reshare(&api, &rpc, &signer).await?; - Ok(StatusCode::OK) + confirm_key_reshare(api, rpc, &signer).await?; + Ok(()) } /// HTTP POST endpoint called by the off-chain worker (propagation pallet) after a network key reshare. @@ -198,6 +218,7 @@ 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?; + validate_rotate_network_key(&api, &rpc).await?; let (signer, _) = get_signer_and_x25519_secret(&app_state.kv_store) @@ -223,7 +244,7 @@ pub async fn rotate_network_key( 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); diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 37997f106..9f23897a4 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - // Copyright (C) 2023 Entropy Cryptography Inc. // // This program is free software: you can redistribute it and/or modify @@ -19,20 +17,16 @@ use crate::{ helpers::{ launch::{FORBIDDEN_KEYS, LATEST_BLOCK_NUMBER_RESHARE}, tests::{ - initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, - unsafe_get, ChainSpecType, + get_port, initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, + unsafe_get, }, }, - user::tests::jump_start_network, validator::{ api::{is_signer_or_delete_parent_key, prune_old_holders, validate_new_reshare}, errors::ValidatorErr, }, }; -use entropy_client::{ - self as test_client, - chain_api::entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, -}; +use entropy_client::{self as test_client}; use entropy_client::{ chain_api::{ entropy, entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec, @@ -41,26 +35,21 @@ 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_testing_utils::constants::{ - AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, TEST_PROGRAM_WASM_BYTECODE, -}; +use entropy_kvdb::clean_tests; +use entropy_shared::{OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY}; use entropy_testing_utils::{ - constants::{ALICE_STASH_ADDRESS, RANDOM_ACCOUNT}, + constants::{ + ALICE_STASH_ADDRESS, AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, RANDOM_ACCOUNT, + TEST_PROGRAM_WASM_BYTECODE, + }, substrate_context::{test_node_process_testing_state, testing_context}, - test_context_stationary, + test_context_stationary, ChainSpecType, }; use parity_scale_codec::Encode; use serial_test::serial; use sp_core::Pair; use sp_keyring::AccountKeyring; +use std::collections::HashSet; use subxt::utils::AccountId32; use synedrion::k256::ecdsa::VerifyingKey; @@ -70,22 +59,24 @@ async fn test_reshare_basic() { initialize_test_logger().await; clean_tests(); - let cxt = &test_node_process_testing_state(true).await[0]; - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; - // let validator_ports = vec![3002, 3003, 3004]; - let api = get_api(&cxt.ws_url).await.unwrap(); - let rpc = get_rpc(&cxt.ws_url).await.unwrap(); + let force_authoring = true; + let context = + test_node_process_testing_state(ChainSpecType::IntegrationJumpStarted, force_authoring) + .await; + let api = get_api(&context[0].ws_url).await.unwrap(); + let rpc = get_rpc(&context[0].ws_url).await.unwrap(); let client = reqwest::Client::new(); - jump_start_network(&api, &rpc).await; - // Get current signers let signer_query = entropy::storage().staking_extension().signers(); let signer_stash_accounts = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); + let old_signer_ids: HashSet<[u8; 32]> = + HashSet::from_iter(signer_stash_accounts.clone().into_iter().map(|id| id.0)); let mut signers = Vec::new(); for signer in signer_stash_accounts.iter() { let query = entropy::storage().staking_extension().threshold_servers(signer); @@ -93,145 +84,32 @@ async fn test_reshare_basic() { signers.push(server_info); } - // 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); - key_shares_before.insert( - signer.tss_account.0, - unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await, - ); + let key_share = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; + assert!(!key_share.is_empty()); } - // Get all validators - // let validators_query = entropy::storage().session().validators(); - // let all_validators = query_chain(&api, &rpc, validators_query, None).await.unwrap().unwrap(); - - // Get stash account of a non-signer, to become the new signer - // Since we only have 4 nodes in our test setup, this will be the same one the chain chooses - // let new_signer = all_validators.iter().find(|v| !signer_stash_accounts.contains(v)).unwrap(); - - // let onchain_reshare_request = OcwMessageReshare { - // new_signers: vec![new_signer.0.to_vec()], - // block_number: block_number - 1, - // }; - // - let block_number = TEST_RESHARE_BLOCK_NUMBER; - run_to_block(&rpc, block_number + 1).await; - // Send the OCW message to all TS servers who don't have a chain node - // let response_results = join_all( - // validator_ports - // .iter() - // .map(|port| { - // client - // .post(format!("http://127.0.0.1:{}/validator/reshare", port)) - // .body(onchain_reshare_request.clone().encode()) - // .send() - // }) - // .collect::>(), - // ) - // .await; - // for response_result in response_results { - // assert_eq!(response_result.unwrap().text().await.unwrap(), ""); - // } - - 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); + let mut i = 0; + 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 Ok(new_signer_ids); } - signers - }; - - 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() - ); + if i > 120 { + break Err("Timed out waiting for reshare"); } - - // 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() - ); + i += 1; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } + .unwrap(); - 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"); + // At this point the signing set has changed on-chain, but the keyshares haven't been rotated + // but by the time we have stored a program and registered, the rotation should have happened // Now test signing a message with the new keyshare set let account_owner = AccountKeyring::Ferdie.pair(); @@ -285,6 +163,15 @@ async fn test_reshare_basic() { verifying_key.to_vec(), recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() ); + + // 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()); + } clean_tests(); } @@ -294,10 +181,12 @@ async fn test_reshare_none_called() { initialize_test_logger().await; clean_tests(); - let _cxt = test_node_process_testing_state(true).await; + let force_authoring = true; + let _context = + test_node_process_testing_state(ChainSpecType::Integration, force_authoring).await; let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).await; let validator_ports = vec![3001, 3002, 3003, 3004]; @@ -321,7 +210,8 @@ async fn test_reshare_validation_fail() { clean_tests(); let dave = AccountKeyring::Dave; - let cxt = &test_node_process_testing_state(true).await[0]; + + let cxt = &test_node_process_testing_state(ChainSpecType::Integration, true).await[0]; let api = get_api(&cxt.ws_url).await.unwrap(); let rpc = get_rpc(&cxt.ws_url).await.unwrap(); let kv = setup_client().await; @@ -451,8 +341,3 @@ async fn test_deletes_key() { assert!(!has_key); clean_tests(); } - -/// Given a ServerInfo, get the port number -fn get_port(server_info: &ServerInfo) -> u32 { - std::str::from_utf8(&server_info.endpoint).unwrap().split(":").last().unwrap().parse().unwrap() -} diff --git a/crates/threshold-signature-server/tests/jumpstart_register_sign.rs b/crates/threshold-signature-server/tests/jumpstart_register_sign.rs new file mode 100644 index 000000000..df418153d --- /dev/null +++ b/crates/threshold-signature-server/tests/jumpstart_register_sign.rs @@ -0,0 +1,103 @@ +// Copyright (C) 2023 Entropy Cryptography Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use entropy_client::{ + chain_api::{ + entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec, + entropy::runtime_types::pallet_registry::pallet::ProgramInstance, + }, + client as test_client, Hasher, +}; +use entropy_kvdb::clean_tests; +use entropy_testing_utils::{ + constants::{ + AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, TEST_PROGRAM_WASM_BYTECODE, + }, + helpers::spawn_tss_nodes_and_start_chain, + ChainSpecType, +}; +use entropy_tss::helpers::tests::{do_jump_start, initialize_test_logger}; +use serial_test::serial; +use sp_core::Pair; +use sp_keyring::AccountKeyring; +use subxt::utils::AccountId32; +use synedrion::k256::ecdsa::VerifyingKey; + +#[tokio::test] +#[serial] +async fn integration_test_register_sign() { + initialize_test_logger().await; + clean_tests(); + + let (_ctx, api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + + // First jumpstart the network + do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; + + // Now register an account + 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() + ); +} diff --git a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs b/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs deleted file mode 100644 index cdea78df2..000000000 --- a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (C) 2023 Entropy Cryptography Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -use std::collections::HashSet; - -use entropy_client::{ - chain_api::{ - entropy, entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec, - entropy::runtime_types::pallet_registry::pallet::ProgramInstance, get_api, get_rpc, - EntropyConfig, - }, - client as test_client, - substrate::query_chain, - Hasher, -}; -use entropy_kvdb::clean_tests; -use entropy_shared::TEST_RESHARE_BLOCK_NUMBER; -use entropy_testing_utils::{ - constants::{ - AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, TEST_PROGRAM_WASM_BYTECODE, - }, - spawn_testing_validators, test_node_process_testing_state, ChainSpecType, -}; -use entropy_tss::helpers::tests::{do_jump_start, initialize_test_logger, run_to_block}; -use serial_test::serial; -use sp_core::Pair; -use sp_keyring::AccountKeyring; -use subxt::{backend::legacy::LegacyRpcMethods, utils::AccountId32, OnlineClient}; -use synedrion::k256::ecdsa::VerifyingKey; - -#[tokio::test] -#[serial] -async fn integration_test_register_sign_reshare_sign() { - initialize_test_logger().await; - clean_tests(); - - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); - - // First jumpstart the network - do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; - - let initial_signers = { - let signer_query = entropy::storage().staking_extension().signers(); - query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap() - }; - - // Do a reshare - wait_for_reshare(&api, &rpc, initial_signers).await; - - // Now register an account - 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() - ); - - // Sign a message again - 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() - ); -} - -async fn wait_for_reshare( - api: &OnlineClient, - rpc: &LegacyRpcMethods, - initial_signers: Vec, -) { - let reshare_data_query = entropy::storage().staking_extension().reshare_data(); - let reshare_data = query_chain(&api, &rpc, reshare_data_query, None).await.unwrap().unwrap(); - let block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; - dbg!(block_number); - println!("reshare_data {reshare_data:?}"); - - // let block_number = TEST_RESHARE_BLOCK_NUMBER + 20; - // run_to_block(&rpc, block_number).await; - - // 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 - // }; - - // Tell TS servers who do not have an associated chain node to rotate their keyshare. - // This is called by the chain on getting confirmation of the reshare from all of the new - // signing group. - // for signer in new_signers { - // let _ = client - // .post(format!( - // "http://{}/rotate_network_key", - // std::str::from_utf8(&signer.endpoint).unwrap() - // )) - // .send() - // .await - // .unwrap(); - // } - - let initial_signers_set: HashSet<[u8; 32]> = initial_signers.iter().map(|s| s.0).collect(); - loop { - // Check that the signers have changed since before the reshare - let signer_query = entropy::storage().staking_extension().signers(); - let new_signer_stash_accounts = - query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); - let new_signers_set: HashSet<[u8; 32]> = - new_signer_stash_accounts.iter().map(|s| s.0).collect(); - // assert_ne!(initial_signers_set, new); - if initial_signers_set != new_signers_set { - break; - } - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } -} diff --git a/crates/threshold-signature-server/tests/sign_eth_tx.rs b/crates/threshold-signature-server/tests/sign_eth_tx.rs index e8564065f..5dde76391 100644 --- a/crates/threshold-signature-server/tests/sign_eth_tx.rs +++ b/crates/threshold-signature-server/tests/sign_eth_tx.rs @@ -16,7 +16,7 @@ use entropy_client::{ chain_api::{ entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec, - entropy::runtime_types::pallet_registry::pallet::ProgramInstance, get_api, get_rpc, + entropy::runtime_types::pallet_registry::pallet::ProgramInstance, }, client as test_client, Hasher, }; @@ -24,7 +24,8 @@ use entropy_kvdb::clean_tests; use entropy_protocol::{decode_verifying_key, RecoverableSignature}; use entropy_testing_utils::{ constants::{AUXILARY_DATA_SHOULD_SUCCEED, TEST_PROGRAM_WASM_BYTECODE}, - spawn_testing_validators, test_node_process_testing_state, ChainSpecType, + helpers::spawn_tss_nodes_and_start_chain, + ChainSpecType, }; use entropy_tss::helpers::tests::{do_jump_start, initialize_test_logger}; use ethers_core::{ @@ -49,14 +50,8 @@ async fn integration_test_sign_eth_tx() { initialize_test_logger().await; clean_tests(); - let (_validator_ips, _validator_ids) = - spawn_testing_validators(ChainSpecType::Integration).await; - - let force_authoring = true; - let substrate_context = &test_node_process_testing_state(force_authoring).await[0]; - - let api = get_api(&substrate_context.ws_url).await.unwrap(); - let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + let (_ctx, api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // First jumpstart the network do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; diff --git a/node/cli/src/chain_spec/dev.rs b/node/cli/src/chain_spec/dev.rs index 9fdc553e0..bdbdcf4a4 100644 --- a/node/cli/src/chain_spec/dev.rs +++ b/node/cli/src/chain_spec/dev.rs @@ -251,6 +251,7 @@ pub fn development_genesis_config( .collect::>(), proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/node/cli/src/chain_spec/integration_tests.rs b/node/cli/src/chain_spec/integration_tests.rs index b7b912f62..cdcc5ea08 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -39,11 +39,20 @@ use sp_runtime::{BoundedVec, Perbill}; /// The configuration used for the Threshold Signature Scheme server integration tests. /// -/// Since Entropy requires at least two signing groups to work properly we spin up this network with -/// two validators, Alice and Bob. +/// Since Entropy requires at least four nodes to work properly we spin up this network with +/// four validators, Alice, Bob, Charlie, and Dave. /// -/// There are also some changes around the proactive refresh validators. -pub fn integration_tests_config() -> ChainSpec { +/// There are also some changes around the reshare validators. +pub fn integration_tests_config(jumpstarted: bool) -> ChainSpec { + let jump_started_signers = if jumpstarted { + Some(vec![ + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + ]) + } else { + None + }; ChainSpec::builder(wasm_binary_unwrap(), Default::default()) .with_name("Integration Test") .with_id("integration_tests") @@ -59,8 +68,9 @@ pub fn integration_tests_config() -> ChainSpec { get_account_id_from_seed::("Alice"), vec![ get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Charlie//stash"), ], + jump_started_signers, )) .build() } @@ -78,6 +88,7 @@ pub fn integration_tests_genesis_config( initial_nominators: Vec, root_key: AccountId, mock_signer_rotate_data: Vec, + jump_started_signers: Option>, ) -> serde_json::Value { // Note that any endowed_accounts added here will be included in the `elections` and // `technical_committee` genesis configs. If you don't want that, don't push those accounts to @@ -218,7 +229,8 @@ pub fn integration_tests_genesis_config( ], vec![EVE_VERIFYING_KEY.to_vec(), DAVE_VERIFYING_KEY.to_vec()], ), - mock_signer_rotate: (true, mock_signer_rotate_data, vec![get_account_id_from_seed::("Charlie//stash")]), + mock_signer_rotate: (true, mock_signer_rotate_data, vec![get_account_id_from_seed::("Dave//stash")]), + jump_started_signers, }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/node/cli/src/chain_spec/testnet.rs b/node/cli/src/chain_spec/testnet.rs index 9dd3c1f5f..590e14be3 100644 --- a/node/cli/src/chain_spec/testnet.rs +++ b/node/cli/src/chain_spec/testnet.rs @@ -428,6 +428,7 @@ pub fn testnet_genesis_config( .collect::>(), proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/node/cli/src/command.rs b/node/cli/src/command.rs index af0467e5f..b206b5ee5 100644 --- a/node/cli/src/command.rs +++ b/node/cli/src/command.rs @@ -79,7 +79,12 @@ impl SubstrateCli for Cli { "" | "dev" => Box::new(chain_spec::dev::development_config()), "devnet-local" => Box::new(chain_spec::dev::devnet_local_four_node_config()), "integration-tests" => { - Box::new(chain_spec::integration_tests::integration_tests_config()) + let jumpstarted = false; + Box::new(chain_spec::integration_tests::integration_tests_config(jumpstarted)) + }, + "integration-tests-jumpstarted" => { + let jumpstarted = true; + Box::new(chain_spec::integration_tests::integration_tests_config(jumpstarted)) }, "testnet-local" => Box::new(chain_spec::testnet::testnet_local_config()), "testnet" => Box::new(chain_spec::testnet::testnet_config()), diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 679164938..6e56cd631 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -369,7 +369,7 @@ pub fn new_full_base( ); offchain_db.local_storage_set( sp_core::offchain::StorageKind::PERSISTENT, - b"rotate_keyshares", + b"rotate_network_key", &format!("{}/rotate_network_key", endpoint).into_bytes(), ); offchain_db.local_storage_set( diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index c546530e4..32cd7369d 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -361,6 +361,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ], proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }; pallet_staking_extension.assimilate_storage(&mut t).unwrap(); diff --git a/pallets/propagation/src/lib.rs b/pallets/propagation/src/lib.rs index 36c8dbcec..fcea66d47 100644 --- a/pallets/propagation/src/lib.rs +++ b/pallets/propagation/src/lib.rs @@ -160,7 +160,7 @@ pub mod pallet { /// Submits a request to do a key refresh on the signers parent key. pub fn post_reshare(block_number: BlockNumberFor) -> Result<(), http::Error> { let reshare_data = pallet_staking_extension::Pallet::::reshare_data(); - if reshare_data.block_number + sp_runtime::traits::One::one() != block_number { + if reshare_data.block_number != block_number { return Ok(()); } diff --git a/pallets/propagation/src/mock.rs b/pallets/propagation/src/mock.rs index 424010c8b..2b227bcea 100644 --- a/pallets/propagation/src/mock.rs +++ b/pallets/propagation/src/mock.rs @@ -392,6 +392,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ], proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }; pallet_staking_extension.assimilate_storage(&mut t).unwrap(); diff --git a/pallets/registry/src/mock.rs b/pallets/registry/src/mock.rs index ad5b26c3b..b9451ff8b 100644 --- a/pallets/registry/src/mock.rs +++ b/pallets/registry/src/mock.rs @@ -379,6 +379,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ], proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }; pallet_staking_extension.assimilate_storage(&mut t).unwrap(); diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 6337a51a8..4f7f854de 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -60,7 +60,7 @@ use sp_staking::SessionIndex; #[frame_support::pallet] pub mod pallet { use entropy_shared::{ - ValidatorInfo, X25519PublicKey, MAX_SIGNERS, TEST_RESHARE_BLOCK_NUMBER, + ValidatorInfo, X25519PublicKey, EVE_VERIFYING_KEY, MAX_SIGNERS, TEST_RESHARE_BLOCK_NUMBER, VERIFICATION_KEY_LENGTH, }; use frame_support::{ @@ -264,6 +264,9 @@ pub mod pallet { pub proactive_refresh_data: (Vec, Vec>), /// validator info and account new signer to take part in a reshare pub mock_signer_rotate: (bool, Vec, Vec), + /// Whether to begin in an already jumpstarted state in order to be able to test signing + /// using pre-generated keyshares + pub jump_started_signers: Option>, } #[pallet::genesis_build] @@ -308,8 +311,19 @@ pub mod pallet { new_signers, }) } + + if let Some(jump_started_signers) = &self.jump_started_signers { + Signers::::put(jump_started_signers.clone()); + JumpStartProgress::::put(JumpStartDetails { + jump_start_status: JumpStartStatus::Done, + confirmations: jump_started_signers.clone(), + verifying_key: Some(BoundedVec::try_from(EVE_VERIFYING_KEY.to_vec()).unwrap()), + parent_key_threshold: 2, + }); + } } } + // Errors inform users that something went wrong. #[pallet::error] pub enum Error { @@ -779,7 +793,7 @@ pub mod pallet { // trigger reshare at next block let current_block_number = >::block_number(); let reshare_info = ReshareInfo { - block_number: current_block_number - sp_runtime::traits::One::one(), + block_number: current_block_number + sp_runtime::traits::One::one(), new_signers, }; diff --git a/pallets/staking/src/mock.rs b/pallets/staking/src/mock.rs index 6dea3171c..2ff3f8dd1 100644 --- a/pallets/staking/src/mock.rs +++ b/pallets/staking/src/mock.rs @@ -440,6 +440,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ], proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), + jump_started_signers: None, }; pallet_balances.assimilate_storage(&mut t).unwrap(); pallet_staking_extension.assimilate_storage(&mut t).unwrap(); diff --git a/scripts/create-test-keyshares/README.md b/scripts/create-test-keyshares/README.md index ecefbdceb..f9e99cdca 100644 --- a/scripts/create-test-keyshares/README.md +++ b/scripts/create-test-keyshares/README.md @@ -3,15 +3,5 @@ This is used to create sets of pre-generated keyshares. These are used in some of the `entropy-tss` tests to speed up the test by not needing to run a distributed key generation during the test. -Since keyshares are linked to the identities of the holders, and the initial signer set is selected -randomly during the test, there is one keyshare set generated per possible combination of initial -signers. - -Since we have 4 nodes, and 3 signers, we refer to each set by the name of the node who is **not** in -the signer set (which is the one who will act as the relayer node). - -So set 'alice' consists of ['bob', 'charlie', 'dave'] and set 'bob' consists of ['alice', 'charlie', -dave'], etc. - -There are also different keyshare sets for 'test' or 'production' parameters used by Synedrion. Test +There are different keyshare sets for 'test' or 'production' parameters used by Synedrion. Test parameters are less secure but mean that the protocols run much faster. diff --git a/scripts/create-test-keyshares/src/main.rs b/scripts/create-test-keyshares/src/main.rs index 0dbd2bbca..27644b9a9 100644 --- a/scripts/create-test-keyshares/src/main.rs +++ b/scripts/create-test-keyshares/src/main.rs @@ -22,7 +22,6 @@ use entropy_testing_utils::create_test_keyshares::create_test_keyshares; use entropy_tss::helpers::{ launch::{ ValidatorName, DEFAULT_ALICE_MNEMONIC, DEFAULT_BOB_MNEMONIC, DEFAULT_CHARLIE_MNEMONIC, - DEFAULT_DAVE_MNEMONIC, }, validator::get_signer_and_x25519_secret_from_mnemonic, }; @@ -38,7 +37,6 @@ async fn main() { (DEFAULT_ALICE_MNEMONIC, ValidatorName::Alice), (DEFAULT_BOB_MNEMONIC, ValidatorName::Bob), (DEFAULT_CHARLIE_MNEMONIC, ValidatorName::Charlie), - (DEFAULT_DAVE_MNEMONIC, ValidatorName::Dave), ] .into_iter() .map(|(mnemonic, name)| { @@ -49,41 +47,31 @@ async fn main() { let secret_key = *DETERMINISTIC_KEY_SHARE_EVE; - for (_keypair, name) in keypairs_and_names.iter() { - let (keypairs_this_time, names_this_time): (Vec, Vec) = - keypairs_and_names.iter().filter(|(_, n)| n != name).cloned().unzip(); + let (keypairs, names): (Vec, Vec) = + keypairs_and_names.iter().cloned().unzip(); - let keypairs_this_time: [sr25519::Pair; 3] = keypairs_this_time - .try_into() - .map_err(|_| "Cannot convert keypair vector to array") - .unwrap(); + let keypairs: [sr25519::Pair; 3] = + keypairs.try_into().map_err(|_| "Cannot convert keypair vector to array").unwrap(); - // Create and write test keyshares - let test_keyshares = - create_test_keyshares::(secret_key, keypairs_this_time.clone()).await; - let test_keyshares_serialized: Vec<_> = - test_keyshares.iter().map(|k| serialize(k).unwrap()).collect(); - let keyshares_and_names = zip(test_keyshares_serialized, names_this_time.clone()).collect(); - write_keyshares(base_path.join("test"), name, keyshares_and_names).await; + // Create and write test keyshares + let test_keyshares = create_test_keyshares::(secret_key, keypairs.clone()).await; + let test_keyshares_serialized: Vec<_> = + test_keyshares.iter().map(|k| serialize(k).unwrap()).collect(); + let keyshares_and_names = zip(test_keyshares_serialized, names.clone()).collect(); + write_keyshares(base_path.join("test"), keyshares_and_names).await; - // Create and write production keyshares - let production_keyshares = - create_test_keyshares::(secret_key, keypairs_this_time.clone()).await; - let production_keyshres_serialized: Vec<_> = - production_keyshares.iter().map(|k| serialize(k).unwrap()).collect(); - let keyshares_and_names = zip(production_keyshres_serialized, names_this_time).collect(); - write_keyshares(base_path.join("production"), name, keyshares_and_names).await; - } + // Create and write production keyshares + let production_keyshares = + create_test_keyshares::(secret_key, keypairs.clone()).await; + let production_keyshres_serialized: Vec<_> = + production_keyshares.iter().map(|k| serialize(k).unwrap()).collect(); + let keyshares_and_names = zip(production_keyshres_serialized, names).collect(); + write_keyshares(base_path.join("production"), keyshares_and_names).await; } -async fn write_keyshares( - base_path: PathBuf, - name_of_excluded: &ValidatorName, - keyshares_and_names: Vec<(Vec, ValidatorName)>, -) { +async fn write_keyshares(base_path: PathBuf, keyshares_and_names: Vec<(Vec, ValidatorName)>) { for (keyshare, name) in keyshares_and_names { let mut filepath = base_path.clone(); - filepath.push(name_of_excluded.to_string()); let filename = format!("keyshare-held-by-{}.keyshare", name); filepath.push(filename); println!("Writing keyshare file: {:?}", filepath);