diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ec179f7..f64774980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ multiple oracle inputs, this means any programs that were compiled and used need runtime - In [#1179](https://github.com/entropyxyz/entropy-core/pull/1179) the format of TDX Quote input data has been changed. +- In [#1147](https://github.com/entropyxyz/entropy-core/pull/1147) a field is added to the + chainspec: `jump_started_signers` which allows the chain to be started in a pre-jumpstarted state + for testing. If this is not desired it should be set to `None`. ### Added - Protocol message versioning ([#1140](https://github.com/entropyxyz/entropy-core/pull/1140)) @@ -40,6 +43,7 @@ runtime - Remove declare synced ([#1134](https://github.com/entropyxyz/entropy-core/pull/1134/)) - Update programs to accept multiple oracle data ([#1153](https://github.com/entropyxyz/entropy-core/pull/1153/)) - Use context, not block number in TDX quote input data ([#1179](https://github.com/entropyxyz/entropy-core/pull/1179)) +- Allow offchain worker requests to all TSS nodes in entropy-tss test environment ([#1147](https://github.com/entropyxyz/entropy-core/pull/1147)) ### Fixed diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index 1663dbedb..52abd3329 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -9,7 +9,7 @@ use crate::{ }, staking_extension::events, }, - get_api, get_rpc, EntropyConfig, + get_api, get_rpc, }, change_endpoint, change_threshold_accounts, get_oracle_headings, register, remove_program, request_attestation, store_program, @@ -20,8 +20,7 @@ use crate::{ use entropy_shared::{QuoteContext, QuoteInputData}; use entropy_testing_utils::{ constants::{TEST_PROGRAM_WASM_BYTECODE, TSS_ACCOUNTS, X25519_PUBLIC_KEYS}, - helpers::encode_verifying_key, - jump_start_network, spawn_testing_validators, + helpers::{encode_verifying_key, spawn_tss_nodes_and_start_chain}, substrate_context::test_context_stationary, test_node_process_testing_state, ChainSpecType, }; @@ -32,7 +31,7 @@ use rand::{ use serial_test::serial; use sp_core::{sr25519, Pair, H256}; use sp_keyring::AccountKeyring; -use subxt::{tx::PairSigner, utils::AccountId32}; +use subxt::utils::AccountId32; #[tokio::test] #[serial] @@ -211,18 +210,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( @@ -274,9 +263,11 @@ async fn test_remove_program_reference_counter() { #[serial] async fn test_get_oracle_headings() { 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::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 mut current_block = 0; while current_block < 2 { diff --git a/crates/protocol/src/execute_protocol.rs b/crates/protocol/src/execute_protocol.rs index c8eb4c0f1..d2112e883 100644 --- a/crates/protocol/src/execute_protocol.rs +++ b/crates/protocol/src/execute_protocol.rs @@ -383,7 +383,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()); @@ -400,9 +400,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 634882b8f..ea5badf27 100644 --- a/crates/shared/src/constants.rs +++ b/crates/shared/src/constants.rs @@ -12,8 +12,8 @@ pub const DAVE_VERIFYING_KEY: EncodedVerifyingKey = [ 3, 42, 97, 187, 199, 208, 95, 166, 102, 15, 38, 146, 173, 111, 175, 123, 62, 132, 178, 237, 150, 199, 194, 240, 153, 30, 113, 104, 57, 63, 54, 2, 65, ]; -// This key is associated with a constant key share generation from DETERMINISTIC_KEY_SHARE_EVE -pub const EVE_VERIFYING_KEY: EncodedVerifyingKey = [ +// This key is associated with a constant key share generation from DETERMINISTIC_KEY_SHARE_NETWORK +pub const PREGENERATED_NETWORK_VERIFYING_KEY: EncodedVerifyingKey = [ 2, 78, 59, 129, 175, 156, 34, 52, 202, 208, 157, 103, 156, 230, 3, 94, 209, 57, 35, 71, 206, 100, 206, 64, 95, 93, 205, 54, 34, 138, 37, 222, 110, ]; @@ -22,7 +22,7 @@ pub const DEFAULT_VERIFYING_KEY: EncodedVerifyingKey = [0; VERIFICATION_KEY_LENG lazy_static! { // key used to create a deterministic key share for EVE taken from here https://docs.rs/k256/latest/k256/ecdsa/index.html - pub static ref DETERMINISTIC_KEY_SHARE_EVE: [u8; 32] = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"); + pub static ref DETERMINISTIC_KEY_SHARE_NETWORK: [u8; 32] = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"); // key used to create a deterministic key for DAVE - this is random 32 bytes pub static ref DETERMINISTIC_KEY_SHARE_DAVE: [u8; 32] = hex!("06b07fd12cdfb94fbde3ff2098e9f19bb11b00959680cfbd15c914b025f298d7"); // hash used to find DEVICE_KEY_PROXY onchain @@ -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 = 11; +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 38f6efa96..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 c3a059845..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 2207e5ca2..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 26900db6a..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 3e49026d1..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 0554541e9..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 f1a64c0ca..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 a7564d56d..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 2bb75e772..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 eddd95ec7..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 ef23f707f..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 07f57c3b6..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..5bfdf562c 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..3b99543ae 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..1af50b17d 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 d07e32733..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 ac5140fe7..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 4c3501c12..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 9a0c37798..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 20689aebb..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 f76daee80..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 ccd7ef61f..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 f2451db3a..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 cd3344da5..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 41fe5f378..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 7dbfb7833..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 f5422bcdf..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..65e89b906 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..2877283cc 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..d2559b0c5 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 435cd9afa..8fea4daa3 100644 --- a/crates/testing-utils/src/lib.rs +++ b/crates/testing-utils/src/lib.rs @@ -22,9 +22,7 @@ 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 7571e3c09..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,43 +111,42 @@ 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( "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWM7EoKJfwgzAR1nAVmYRuuFq2f3GpJPLrdfhQaRsKjn38" .to_string(), ); - // reduses message from chain to same TSS cleaning up a lot of logging - let fuck_off_tss_ip = Some("127.0.0.1:4010".to_string()); 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(), - fuck_off_tss_ip.clone(), + Some("http://127.0.0.1:3002".into()), ) .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(), - fuck_off_tss_ip.clone(), + Some("http://127.0.0.1:3003".into()), ) .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(), - fuck_off_tss_ip.clone(), + Some("http://127.0.0.1:3004".into()), ) .await; diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index ef3b37c7e..029ddcf4c 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,10 +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::{OcwMessageDkg, EVE_VERIFYING_KEY, NETWORK_PARENT_KEY}; -use futures::future::join_all; -use parity_scale_codec::Encode; -use std::time::Duration; +use entropy_shared::NETWORK_PARENT_KEY; +use std::{fmt, net::SocketAddr, str, time::Duration}; use subxt::{ backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, utils::AccountId32 as SubxtAccountId32, Config, OnlineClient, @@ -130,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, } -/// Spawn either 3 or 4 TSS nodes depending on chain configuration, adding pre-stored keyshares if +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 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; @@ -192,21 +198,25 @@ 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; + // Dave does not get a keyshare as there are only 3 parties in the signing group } tokio::time::sleep(Duration::from_secs(1)).await; @@ -216,29 +226,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 @@ -277,47 +276,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( @@ -371,28 +329,6 @@ pub async fn do_jump_start( run_to_block(rpc, block_number + 1).await; - let selected_validators_query = entropy::storage().registry().jumpstart_dkg(block_number); - let validators_info = - query_chain(api, rpc, selected_validators_query, None).await.unwrap().unwrap(); - let validators_info: Vec<_> = validators_info.into_iter().map(|v| v.0).collect(); - let onchain_user_request = - OcwMessageDkg { block_number, validators_info: validators_info.clone() }; - - let client = reqwest::Client::new(); - - let mut results = vec![]; - for validator_info in validators_info { - let url = format!( - "http://{}/generate_network_key", - std::str::from_utf8(&validator_info.ip_address.clone()).unwrap() - ); - if url != *"http://127.0.0.1:3001/generate_network_key" { - results.push(client.post(url).body(onchain_user_request.clone().encode()).send()) - } - } - - let response_results = join_all(results).await; - let jump_start_status_query = entropy::storage().staking_extension().jump_start_progress(); let mut jump_start_status = query_chain(api, rpc, jump_start_status_query.clone(), None) .await @@ -414,9 +350,6 @@ pub async fn do_jump_start( } assert_eq!(format!("{:?}", jump_start_status), format!("{:?}", JumpStartStatus::Done)); - for response_result in response_results { - assert_eq!(response_result.unwrap().text().await.unwrap(), ""); - } } /// Submit a jumpstart extrinsic @@ -430,3 +363,10 @@ 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 { + let socket_address: SocketAddr = + str::from_utf8(&server_info.endpoint).unwrap().parse().unwrap(); + socket_address.port().into() +} diff --git a/crates/threshold-signature-server/src/signing_client/tests.rs b/crates/threshold-signature-server/src/signing_client/tests.rs index d5b9d454d..ba2550ccf 100644 --- a/crates/threshold-signature-server/src/signing_client/tests.rs +++ b/crates/threshold-signature-server/src/signing_client/tests.rs @@ -20,18 +20,19 @@ 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, }, }, }; use entropy_kvdb::clean_tests; use entropy_shared::{ - constants::{DAVE_VERIFYING_KEY, EVE_VERIFYING_KEY}, + constants::{DAVE_VERIFYING_KEY, PREGENERATED_NETWORK_VERIFYING_KEY}, OcwMessageProactiveRefresh, }; 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,15 +45,19 @@ 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(); // check get key before proactive refresh - let key_before_eve = unsafe_get(&client, hex::encode(EVE_VERIFYING_KEY), 3001).await; + let key_before_network = + unsafe_get(&client, hex::encode(PREGENERATED_NETWORK_VERIFYING_KEY), 3001).await; let key_before_dave = unsafe_get(&client, hex::encode(DAVE_VERIFYING_KEY), 3001).await; let validators_info = vec![ @@ -75,7 +80,10 @@ async fn test_proactive_refresh() { let mut ocw_message = OcwMessageProactiveRefresh { validators_info, - proactive_refresh_keys: vec![EVE_VERIFYING_KEY.to_vec(), DAVE_VERIFYING_KEY.to_vec()], + proactive_refresh_keys: vec![ + PREGENERATED_NETWORK_VERIFYING_KEY.to_vec(), + DAVE_VERIFYING_KEY.to_vec(), + ], block_number: 0, }; @@ -93,11 +101,12 @@ async fn test_proactive_refresh() { assert_eq!(res.unwrap().text().await.unwrap(), ""); } - let key_after_eve = unsafe_get(&client, hex::encode(EVE_VERIFYING_KEY), 3001).await; + let key_after_network = + unsafe_get(&client, hex::encode(PREGENERATED_NETWORK_VERIFYING_KEY), 3001).await; let key_after_dave = unsafe_get(&client, hex::encode(DAVE_VERIFYING_KEY), 3001).await; // make sure private keyshares are changed - assert_ne!(key_before_eve, key_after_eve); + assert_ne!(key_before_network, key_after_network); assert_ne!(key_before_dave, key_after_dave); let alice = AccountKeyring::Alice; diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 0290d4fef..d2620049c 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -42,9 +42,9 @@ use entropy_testing_utils::{ TEST_ORACLE_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}; @@ -70,7 +70,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, @@ -88,8 +87,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, @@ -131,22 +130,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; @@ -367,22 +357,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; @@ -447,21 +428,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) = @@ -494,16 +465,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 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; let relayer_url = format!("http://{}/user/relay_tx", relayer_ip_and_key.0.clone()); @@ -613,15 +578,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 @@ -717,16 +684,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 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; - 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; @@ -817,16 +778,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 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; @@ -904,28 +859,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(); @@ -1053,20 +1018,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( @@ -1127,20 +1085,14 @@ async fn test_oracle_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( @@ -1214,19 +1166,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; @@ -1352,13 +1295,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; @@ -1518,19 +1458,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 = @@ -1759,16 +1688,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 49bdec642..179342402 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -218,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) @@ -243,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 9d631c486..faf73f645 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,23 +35,16 @@ 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 futures::future::join_all; use parity_scale_codec::Encode; use serial_test::serial; use sp_core::Pair; @@ -68,26 +55,28 @@ use synedrion::k256::ecdsa::VerifyingKey; #[tokio::test] #[serial] -async fn test_reshare() { +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); @@ -95,160 +84,33 @@ async fn test_reshare() { 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 block_number = TEST_RESHARE_BLOCK_NUMBER; - let onchain_reshare_request = OcwMessageReshare { - new_signers: vec![new_signer.0.to_vec()], - block_number: block_number - 1, - }; - - run_to_block(&rpc, block_number).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(), ""); - } - - // Now wait until signers have changed - let old_signer_ids = HashSet::from_iter(signer_stash_accounts.into_iter().map(|id| id.0)); - let start = std::time::Instant::now(); - loop { + let mut i = 0; + // Wait up to 2min for reshare to complete: check once every second if we have a new set of signers. + 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; + break Ok(new_signer_ids); } - if start.elapsed() > std::time::Duration::from_secs(60) { - panic!("Timed out waiting for reshare protocol to finish successfully"); + if i > 120 { + break Err("Timed out waiting for reshare"); } + i += 1; tokio::time::sleep(std::time::Duration::from_secs(1)).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); - } - 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() - ); - } - - 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(); @@ -302,6 +164,15 @@ async fn test_reshare() { 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(); } @@ -311,10 +182,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]; @@ -338,7 +211,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; @@ -468,8 +342,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 c7ab1342a..000000000 --- a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs +++ /dev/null @@ -1,213 +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::{OcwMessageReshare, 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 futures::future::join_all; -use serial_test::serial; -use sp_core::{Encode, 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; - - // 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() - ); - - // Do a reshare - do_reshare(&api, &rpc).await; - - // 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 do_reshare(api: &OnlineClient, rpc: &LegacyRpcMethods) { - // 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 mut signers = 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(); - signers.push(server_info); - } - - 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 = TEST_RESHARE_BLOCK_NUMBER; - let onchain_reshare_request = OcwMessageReshare { - new_signers: reshare_data.new_signers.into_iter().map(|s| s.to_vec()).collect(), - block_number: block_number - 1, - }; - - run_to_block(&rpc, block_number).await; - // Send the OCW message to all TS servers who don't have a chain node - let client = reqwest::Client::new(); - let response_results = join_all( - [3002, 3003, 3004] - .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(), ""); - } - - // Wait for the reshare protocol to finish - let old_signer_ids = HashSet::from_iter(signer_stash_accounts.into_iter().map(|id| id.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 new_signer_ids; - } - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - }; - - // 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_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 _ = client - .post(format!( - "http://{}/rotate_network_key", - std::str::from_utf8(&server_info.endpoint).unwrap() - )) - .send() - .await - .unwrap(); - } -} 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..18a9523ea 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -25,8 +25,8 @@ use entropy_runtime::{ use entropy_runtime::{AccountId, Balance}; use entropy_shared::{ DAVE_VERIFYING_KEY, DEVICE_KEY_AUX_DATA_TYPE, DEVICE_KEY_CONFIG_TYPE, DEVICE_KEY_HASH, - DEVICE_KEY_PROXY, EVE_VERIFYING_KEY, INITIAL_MAX_INSTRUCTIONS_PER_PROGRAM, SIGNER_THRESHOLD, - TOTAL_SIGNERS, + DEVICE_KEY_PROXY, INITIAL_MAX_INSTRUCTIONS_PER_PROGRAM, PREGENERATED_NETWORK_VERIFYING_KEY, + SIGNER_THRESHOLD, TOTAL_SIGNERS, }; use grandpa_primitives::AuthorityId as GrandpaId; use itertools::Itertools; @@ -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 @@ -216,9 +227,10 @@ pub fn integration_tests_genesis_config( x25519_public_key: crate::chain_spec::tss_x25519_public_key::CHARLIE, }, ], - vec![EVE_VERIFYING_KEY.to_vec(), DAVE_VERIFYING_KEY.to_vec()], + vec![PREGENERATED_NETWORK_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/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/mock.rs b/pallets/propagation/src/mock.rs index 0478e0197..cde2bb725 100644 --- a/pallets/propagation/src/mock.rs +++ b/pallets/propagation/src/mock.rs @@ -394,6 +394,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 c46aa88e8..85c5c1fe6 100644 --- a/pallets/registry/src/mock.rs +++ b/pallets/registry/src/mock.rs @@ -381,6 +381,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 cec07911e..88bf6cbec 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -60,8 +60,8 @@ use sp_staking::SessionIndex; #[frame_support::pallet] pub mod pallet { use entropy_shared::{ - QuoteContext, ValidatorInfo, X25519PublicKey, MAX_SIGNERS, TEST_RESHARE_BLOCK_NUMBER, - VERIFICATION_KEY_LENGTH, + QuoteContext, ValidatorInfo, X25519PublicKey, MAX_SIGNERS, + PREGENERATED_NETWORK_VERIFYING_KEY, TEST_RESHARE_BLOCK_NUMBER, VERIFICATION_KEY_LENGTH, }; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo}, @@ -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,21 @@ 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(PREGENERATED_NETWORK_VERIFYING_KEY.to_vec()).unwrap(), + ), + parent_key_threshold: 2, + }); + } } } + // Errors inform users that something went wrong. #[pallet::error] pub enum Error { diff --git a/pallets/staking/src/mock.rs b/pallets/staking/src/mock.rs index d2009939b..21a35e06e 100644 --- a/pallets/staking/src/mock.rs +++ b/pallets/staking/src/mock.rs @@ -442,6 +442,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..e63c70885 100644 --- a/scripts/create-test-keyshares/src/main.rs +++ b/scripts/create-test-keyshares/src/main.rs @@ -17,12 +17,11 @@ //! The base path for where to store keyshares is given as a single command line argument //! If it is not given, the current working directory is used use entropy_kvdb::kv_manager::helpers::serialize; -use entropy_shared::DETERMINISTIC_KEY_SHARE_EVE; +use entropy_shared::DETERMINISTIC_KEY_SHARE_NETWORK; 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)| { @@ -47,43 +45,33 @@ async fn main() { }) .collect(); - let secret_key = *DETERMINISTIC_KEY_SHARE_EVE; + let secret_key = *DETERMINISTIC_KEY_SHARE_NETWORK; - 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);