From 6327119d13a5048c6c78fc184172716e2622c77f Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 12 Nov 2024 08:17:02 +0100 Subject: [PATCH 01/28] Add pre-jumpstart option to chainspec and helper fn, update tests --- crates/client/entropy_metadata.scale | Bin 209698 -> 0 bytes crates/testing-utils/src/helpers.rs | 5 +- crates/testing-utils/src/substrate_context.rs | 10 +- .../src/helpers/tests.rs | 18 +- .../src/signing_client/tests.rs | 8 +- .../src/user/tests.rs | 131 ++------- .../src/validator/tests.rs | 275 +++++++++--------- .../tests/register_sign_reshare_sign.rs | 16 +- .../tests/sign_eth_tx.rs | 15 +- node/cli/src/chain_spec/dev.rs | 1 + node/cli/src/chain_spec/integration_tests.rs | 36 +++ node/cli/src/chain_spec/testnet.rs | 1 + node/cli/src/command.rs | 3 + pallets/staking/src/lib.rs | 25 +- 14 files changed, 268 insertions(+), 276 deletions(-) diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index fcfbd8800746eaa47293aebd17012d87cefbfd01..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 209698 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%0A^l|0;E;Z*XmCjXTG8N; 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 diff --git a/crates/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 9da5745e3..16e618385 100644 --- a/crates/testing-utils/src/helpers.rs +++ b/crates/testing-utils/src/helpers.rs @@ -39,11 +39,12 @@ pub async fn spawn_tss_nodes_and_start_chain( 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]; + let substrate_context = + &&test_node_process_testing_state(chain_spec_type, force_authoring).await[0]; ( get_api(&substrate_context.ws_url).await.unwrap(), get_rpc(&substrate_context.ws_url).await.unwrap(), 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..63441997c 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -48,7 +48,7 @@ use entropy_protocol::PartyId; #[cfg(test)] use entropy_shared::EncodedVerifyingKey; use entropy_shared::{EVE_VERIFYING_KEY, NETWORK_PARENT_KEY}; -use std::time::Duration; +use std::{fmt, time::Duration}; use subxt::{ backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, utils::AccountId32 as SubxtAccountId32, Config, OnlineClient, @@ -132,6 +132,22 @@ pub enum ChainSpecType { 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + ChainSpecType::Development => "dev", + 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 diff --git a/crates/threshold-signature-server/src/signing_client/tests.rs b/crates/threshold-signature-server/src/signing_client/tests.rs index d5b9d454d..bf2165fab 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,10 @@ 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::Integration, false).await[0]; - let (validator_ips, _ids) = spawn_testing_validators(ChainSpecType::Integration).await; + let (validator_ips, _ids) = + spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).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..86f1ac544 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}, + ChainSpecType, }; use more_asserts as ma; use parity_scale_codec::{Decode, Encode}; @@ -88,7 +88,7 @@ use crate::{ 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, + store_program_and_register, unsafe_get, }, user::compute_hash, validator::get_signer_and_x25519_secret_from_mnemonic, @@ -130,19 +130,12 @@ 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 (entropy_api, rpc, validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).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(); @@ -366,19 +359,12 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).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(); @@ -446,15 +432,8 @@ 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; - - // 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key // for later @@ -493,14 +472,8 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); let (relayer_ip_and_key, _) = @@ -611,14 +584,8 @@ async fn test_request_limit_are_updated_during_signing() { 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); let (relayer_ip_and_key, _) = @@ -716,14 +683,8 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); // Register the user with a test program @@ -816,14 +777,8 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); let (relayer_ip_and_key, _) = @@ -902,14 +857,8 @@ async fn test_jumpstart_network() { 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 (api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; @@ -1052,19 +1001,12 @@ 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 (api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).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 (relayer_ip_and_key, _) = validator_name_to_relayer_info(non_signer, &api, &rpc).await; @@ -1150,15 +1092,8 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key // for later @@ -1288,11 +1223,8 @@ 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 (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); let (relayer_ip_and_key, _) = @@ -1454,15 +1386,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(); + let (entropy_api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key // for later diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 37997f106..8c0dc9520 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 @@ -20,7 +18,7 @@ use crate::{ launch::{FORBIDDEN_KEYS, LATEST_BLOCK_NUMBER_RESHARE}, tests::{ initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, - unsafe_get, ChainSpecType, + unsafe_get, }, }, user::tests::jump_start_network, @@ -49,18 +47,20 @@ 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_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, + }, + helpers::spawn_tss_nodes_and_start_chain, 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::HashMap; use subxt::utils::AccountId32; use synedrion::k256::ecdsa::VerifyingKey; @@ -70,19 +70,13 @@ 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; - - // 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 (api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + let current_block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; + println!("Block number {}", current_block_number); 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(); @@ -166,125 +160,125 @@ async fn test_reshare_basic() { println!("Signers {:?}", signers); println!("NEW Signers {:?}", new_signers); - for signer in new_signers { - let _ = client - .post(format!( - "http://{}/rotate_network_key", - std::str::from_utf8(&signer.endpoint).unwrap() - )) - .send() - .await - .unwrap(); - - let key_share_and_aux_data_after_rotate = - unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - let (key_share_after_rotate, aux_info_after_rotate): KeyShareWithAuxInfo = - deserialize(&key_share_and_aux_data_after_rotate).unwrap(); - - if let Some(key_share_and_aux_before) = key_shares_before.get(&signer.tss_account.0) { - let (key_share_before, aux_info_before): KeyShareWithAuxInfo = - deserialize(&key_share_and_aux_before).unwrap(); - // Check key share has changed - assert_ne!( - serialize(&key_share_before).unwrap(), - serialize(&key_share_after_rotate).unwrap() - ); - // Check aux info has changed - assert_ne!( - serialize(&aux_info_before).unwrap(), - serialize(&aux_info_after_rotate).unwrap() - ); - } - - // calling twice doesn't do anything - let response = client - .post(format!( - "http://{}/rotate_network_key", - std::str::from_utf8(&signer.endpoint).unwrap() - )) - .send() - .await - .unwrap(); - - assert_eq!(response.text().await.unwrap(), "Kv error: Recv Error: channel closed"); - let key_share_and_aux_data_after_rotate_twice = - unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - let (key_share_after_rotate_twice, aux_info_after_rotate_twice): KeyShareWithAuxInfo = - deserialize(&key_share_and_aux_data_after_rotate_twice).unwrap(); - - // Check key share has not changed - assert_eq!( - serialize(&key_share_after_rotate_twice).unwrap(), - serialize(&key_share_after_rotate).unwrap() - ); - // Check aux info has not changed - assert_eq!( - serialize(&aux_info_after_rotate_twice).unwrap(), - serialize(&aux_info_after_rotate).unwrap() - ); - } - - let current_block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; - // Check that rotating the network key wont work again later - run_to_block(&rpc, current_block_number + 3).await; - - let response_stale = - client.post("http://127.0.0.1:3001/rotate_network_key").send().await.unwrap(); - - assert_eq!(response_stale.text().await.unwrap(), "Data is stale"); - - // Now test signing a message with the new keyshare set - let account_owner = AccountKeyring::Ferdie.pair(); - let signature_request_author = AccountKeyring::One; - // Store a program - let program_pointer = test_client::store_program( - &api, - &rpc, - &account_owner, - TEST_PROGRAM_WASM_BYTECODE.to_owned(), - vec![], - vec![], - vec![], - 0u8, - ) - .await - .unwrap(); - - // Register, using that program - let (verifying_key, _registered_info) = test_client::register( - &api, - &rpc, - account_owner.clone(), - AccountId32(account_owner.public().0), - BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), - ) - .await - .unwrap(); - - // Sign a message - let recoverable_signature = test_client::sign( - &api, - &rpc, - signature_request_author.pair(), - verifying_key, - PREIMAGE_SHOULD_SUCCEED.to_vec(), - Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), - ) - .await - .unwrap(); - - // Check the signature - let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); - let recovery_key_from_sig = VerifyingKey::recover_from_prehash( - &message_should_succeed_hash, - &recoverable_signature.signature, - recoverable_signature.recovery_id, - ) - .unwrap(); - assert_eq!( - verifying_key.to_vec(), - recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() - ); + // 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"); + // + // // Now test signing a message with the new keyshare set + // let account_owner = AccountKeyring::Ferdie.pair(); + // let signature_request_author = AccountKeyring::One; + // // Store a program + // let program_pointer = test_client::store_program( + // &api, + // &rpc, + // &account_owner, + // TEST_PROGRAM_WASM_BYTECODE.to_owned(), + // vec![], + // vec![], + // vec![], + // 0u8, + // ) + // .await + // .unwrap(); + // + // // Register, using that program + // let (verifying_key, _registered_info) = test_client::register( + // &api, + // &rpc, + // account_owner.clone(), + // AccountId32(account_owner.public().0), + // BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), + // ) + // .await + // .unwrap(); + // + // // Sign a message + // let recoverable_signature = test_client::sign( + // &api, + // &rpc, + // signature_request_author.pair(), + // verifying_key, + // PREIMAGE_SHOULD_SUCCEED.to_vec(), + // Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), + // ) + // .await + // .unwrap(); + // + // // Check the signature + // let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); + // let recovery_key_from_sig = VerifyingKey::recover_from_prehash( + // &message_should_succeed_hash, + // &recoverable_signature.signature, + // recoverable_signature.recovery_id, + // ) + // .unwrap(); + // assert_eq!( + // verifying_key.to_vec(), + // recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() + // ); clean_tests(); } @@ -294,10 +288,10 @@ async fn test_reshare_none_called() { initialize_test_logger().await; clean_tests(); - let _cxt = test_node_process_testing_state(true).await; + // let _cxt = test_node_process_testing_state(true).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 +315,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; diff --git a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs b/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs index e1e717c48..0e8384187 100644 --- a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs +++ b/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs @@ -18,8 +18,7 @@ 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, + entropy::runtime_types::pallet_registry::pallet::ProgramInstance, EntropyConfig, }, client as test_client, substrate::query_chain, @@ -31,7 +30,8 @@ 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, + helpers::spawn_tss_nodes_and_start_chain, + ChainSpecType, }; use entropy_tss::helpers::tests::{do_jump_start, initialize_test_logger, run_to_block}; use serial_test::serial; @@ -46,14 +46,8 @@ 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(); + let (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/crates/threshold-signature-server/tests/sign_eth_tx.rs b/crates/threshold-signature-server/tests/sign_eth_tx.rs index e8564065f..a356172c5 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 (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..61ab0c448 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_start_state: 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..d8e1d8764 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -31,6 +31,7 @@ use entropy_shared::{ use grandpa_primitives::AuthorityId as GrandpaId; use itertools::Itertools; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; +use pallet_staking_extension::{JumpStartDetails, JumpStartStatus}; use sc_service::ChainType; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; @@ -61,6 +62,39 @@ pub fn integration_tests_config() -> ChainSpec { get_account_id_from_seed::("Bob//stash"), get_account_id_from_seed::("Dave//stash"), ], + None, + )) + .build() +} + +pub fn integration_tests_jumpstarted_config() -> ChainSpec { + ChainSpec::builder(wasm_binary_unwrap(), Default::default()) + .with_name("Integration Test") + .with_id("integration_tests") + .with_chain_type(ChainType::Development) + .with_genesis_config_patch(integration_tests_genesis_config( + vec![ + crate::chain_spec::authority_keys_from_seed("Alice"), + crate::chain_spec::authority_keys_from_seed("Bob"), + crate::chain_spec::authority_keys_from_seed("Charlie"), + crate::chain_spec::authority_keys_from_seed("Dave"), + ], + vec![], + get_account_id_from_seed::("Alice"), + vec![ + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Dave//stash"), + ], + Some(JumpStartDetails { + jump_start_status: JumpStartStatus::Done, + confirmations: vec![ + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + ], + verifying_key: Some(BoundedVec::try_from(EVE_VERIFYING_KEY.to_vec()).unwrap()), + parent_key_threshold: 2, // TODO use constant + }), )) .build() } @@ -78,6 +112,7 @@ pub fn integration_tests_genesis_config( initial_nominators: Vec, root_key: AccountId, mock_signer_rotate_data: Vec, + jump_start_state: 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 @@ -219,6 +254,7 @@ 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")]), + jump_start_state, }, "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..22319646a 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_start_state: None, }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/node/cli/src/command.rs b/node/cli/src/command.rs index af0467e5f..4215ff7da 100644 --- a/node/cli/src/command.rs +++ b/node/cli/src/command.rs @@ -81,6 +81,9 @@ impl SubstrateCli for Cli { "integration-tests" => { Box::new(chain_spec::integration_tests::integration_tests_config()) }, + "integration-tests-jumpstarted" => { + Box::new(chain_spec::integration_tests::integration_tests_jumpstarted_config()) + }, "testnet-local" => Box::new(chain_spec::testnet::testnet_local_config()), "testnet" => Box::new(chain_spec::testnet::testnet_config()), path => { diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index fcd37e88a..c4aa00268 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -36,7 +36,7 @@ use core::convert::TryInto; pub use pallet::*; use pallet_staking::{MaxNominationsOf, ValidatorPrefs}; -#[cfg(feature = "std")] +// #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; pub use crate::weights::WeightInfo; @@ -191,7 +191,17 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, T::AccountId, T::ValidatorId, OptionQuery>; #[derive( - Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default, + Encode, + Decode, + Clone, + PartialEq, + Eq, + RuntimeDebug, + TypeInfo, + MaxEncodedLen, + Default, + Serialize, + Deserialize, )] pub enum JumpStartStatus { #[default] @@ -211,6 +221,8 @@ pub mod pallet { RuntimeDebug, TypeInfo, frame_support::DefaultNoBound, + Serialize, + Deserialize, )] #[scale_info(skip_type_params(T))] pub struct JumpStartDetails { @@ -264,6 +276,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_start_state: Option>, } #[pallet::genesis_build] @@ -308,8 +323,14 @@ pub mod pallet { new_signers, }) } + + if let Some(jump_start_details) = &self.jump_start_state { + Signers::::put(jump_start_details.confirmations.clone()); + JumpStartProgress::::put(jump_start_details); + } } } + // Errors inform users that something went wrong. #[pallet::error] pub enum Error { From 406a305b514e81c1cad158f2e2ede2a14a923739 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 13 Nov 2024 09:36:54 +0100 Subject: [PATCH 02/28] Dont include JumpStartDetails in staking pallet config, only ids of the jumpstarted signer set --- crates/client/entropy_metadata.scale | Bin 0 -> 209698 bytes node/cli/src/chain_spec/dev.rs | 2 +- node/cli/src/chain_spec/integration_tests.rs | 20 +++++------- node/cli/src/chain_spec/testnet.rs | 2 +- pallets/staking/src/lib.rs | 31 +++++++------------ pallets/staking/src/mock.rs | 1 + 6 files changed, 22 insertions(+), 34 deletions(-) diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fcfbd8800746eaa47293aebd17012d87cefbfd01 100644 GIT binary patch literal 209698 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%0A^l|0;E;Z*XmCjXTG8N; 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 literal 0 HcmV?d00001 diff --git a/node/cli/src/chain_spec/dev.rs b/node/cli/src/chain_spec/dev.rs index 61ab0c448..bdbdcf4a4 100644 --- a/node/cli/src/chain_spec/dev.rs +++ b/node/cli/src/chain_spec/dev.rs @@ -251,7 +251,7 @@ pub fn development_genesis_config( .collect::>(), proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), - jump_start_state: None, + 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 d8e1d8764..859099fc6 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -31,7 +31,6 @@ use entropy_shared::{ use grandpa_primitives::AuthorityId as GrandpaId; use itertools::Itertools; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; -use pallet_staking_extension::{JumpStartDetails, JumpStartStatus}; use sc_service::ChainType; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_babe::AuthorityId as BabeId; @@ -85,16 +84,11 @@ pub fn integration_tests_jumpstarted_config() -> ChainSpec { get_account_id_from_seed::("Bob//stash"), get_account_id_from_seed::("Dave//stash"), ], - Some(JumpStartDetails { - jump_start_status: JumpStartStatus::Done, - confirmations: vec![ - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - ], - verifying_key: Some(BoundedVec::try_from(EVE_VERIFYING_KEY.to_vec()).unwrap()), - parent_key_threshold: 2, // TODO use constant - }), + Some(vec![ + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + ]), )) .build() } @@ -112,7 +106,7 @@ pub fn integration_tests_genesis_config( initial_nominators: Vec, root_key: AccountId, mock_signer_rotate_data: Vec, - jump_start_state: Option>, + 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 @@ -254,7 +248,7 @@ 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")]), - jump_start_state, + 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 22319646a..590e14be3 100644 --- a/node/cli/src/chain_spec/testnet.rs +++ b/node/cli/src/chain_spec/testnet.rs @@ -428,7 +428,7 @@ pub fn testnet_genesis_config( .collect::>(), proactive_refresh_data: (vec![], vec![]), mock_signer_rotate: (false, vec![], vec![]), - jump_start_state: None, + jump_started_signers: None, }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index c4aa00268..c567030b0 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -36,7 +36,7 @@ use core::convert::TryInto; pub use pallet::*; use pallet_staking::{MaxNominationsOf, ValidatorPrefs}; -// #[cfg(feature = "std")] +#[cfg(feature = "std")] use serde::{Deserialize, Serialize}; pub use crate::weights::WeightInfo; @@ -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::{ @@ -191,17 +191,7 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, T::AccountId, T::ValidatorId, OptionQuery>; #[derive( - Encode, - Decode, - Clone, - PartialEq, - Eq, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - Default, - Serialize, - Deserialize, + Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default, )] pub enum JumpStartStatus { #[default] @@ -221,8 +211,6 @@ pub mod pallet { RuntimeDebug, TypeInfo, frame_support::DefaultNoBound, - Serialize, - Deserialize, )] #[scale_info(skip_type_params(T))] pub struct JumpStartDetails { @@ -278,7 +266,7 @@ pub mod pallet { 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_start_state: Option>, + pub jump_started_signers: Option>, } #[pallet::genesis_build] @@ -324,9 +312,14 @@ pub mod pallet { }) } - if let Some(jump_start_details) = &self.jump_start_state { - Signers::::put(jump_start_details.confirmations.clone()); - JumpStartProgress::::put(jump_start_details); + 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, // TODO use constant + }); } } } 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(); From a4dcf5eb7ebf1a9c7ae1f93321fa3419f9758ea9 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 13 Nov 2024 09:56:48 +0100 Subject: [PATCH 03/28] Update mock config for propagation and registry pallets --- pallets/propagation/src/mock.rs | 1 + pallets/registry/src/mock.rs | 1 + 2 files changed, 2 insertions(+) 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(); From 1d4eb587598aaec9b9bc3d4e946f3b36f35bbda3 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 13 Nov 2024 10:09:42 +0100 Subject: [PATCH 04/28] Comments, tidy --- crates/testing-utils/src/helpers.rs | 4 ++-- crates/threshold-signature-server/src/helpers/tests.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 16e618385..5c174a15d 100644 --- a/crates/testing-utils/src/helpers.rs +++ b/crates/testing-utils/src/helpers.rs @@ -40,8 +40,8 @@ pub async fn spawn_tss_nodes_and_start_chain( ) }, _ => { - // 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. + // 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[0]; diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index 63441997c..be15f664e 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -137,6 +137,8 @@ pub enum ChainSpecType { } 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, From ebe7280656cdd1317734d7908dd1ed18fd76f418 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 14 Nov 2024 09:14:17 +0100 Subject: [PATCH 05/28] Add new field to attestation pallet mock --- pallets/attestation/src/mock.rs | 1 + 1 file changed, 1 insertion(+) 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(); From 3489e4a305435fd415bc06f529c6f059aaeba385 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 14 Nov 2024 09:44:32 +0100 Subject: [PATCH 06/28] Update client tests --- crates/client/src/tests.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index 819bf8bae..99c94ca72 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -17,10 +17,12 @@ 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, + }, + jump_start_network, substrate_context::test_context_stationary, - test_node_process_testing_state, ChainSpecType, + ChainSpecType, }; use serial_test::serial; use sp_core::{sr25519, Pair, H256}; @@ -134,13 +136,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(); + let (api, rpc, _validator_ips, _validator_ids) = + spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // Jumpstart the network let alice = AccountKeyring::Alice; From 5052741e1d56ee4d15dbc51d9b4591e7c9eb92ac Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 14 Nov 2024 12:52:02 +0100 Subject: [PATCH 07/28] Fix issue with test helper fn --- crates/client/src/tests.rs | 2 +- crates/testing-utils/src/helpers.rs | 40 ++++++++----------- .../src/helpers/tests.rs | 3 -- .../src/user/tests.rs | 24 +++++------ .../src/validator/tests.rs | 12 +++--- .../tests/register_sign_reshare_sign.rs | 2 +- .../tests/sign_eth_tx.rs | 2 +- 7 files changed, 37 insertions(+), 48 deletions(-) diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index 99c94ca72..11b116ff4 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -136,7 +136,7 @@ async fn test_store_and_remove_program() { async fn test_remove_program_reference_counter() { let program_owner = AccountKeyring::Ferdie.pair(); - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // Jumpstart the network diff --git a/crates/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 5c174a15d..4d6332ae3 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}; @@ -28,30 +28,22 @@ pub use tdx_quote::encode_verifying_key; /// the chain API as well as IP addresses and PartyId of the started validators 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(), - ) - }, - _ => { - // 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[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/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index be15f664e..e16fbe6fe 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -128,8 +128,6 @@ 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 @@ -144,7 +142,6 @@ impl fmt::Display for ChainSpecType { f, "{}", match self { - ChainSpecType::Development => "dev", ChainSpecType::Integration => "integration-tests", ChainSpecType::IntegrationJumpStarted => "integration-tests-jumpstarted", }, diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 86f1ac544..9e07f4c1c 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -130,7 +130,7 @@ async fn test_signature_requests_fail_on_different_conditions() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = @@ -359,7 +359,7 @@ async fn test_signature_requests_fail_validator_info_wrong() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = @@ -432,7 +432,7 @@ async fn signature_request_with_derived_account_works() { let bob = AccountKeyring::Bob; let charlie = AccountKeyring::Charlie; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key @@ -472,7 +472,7 @@ async fn test_signing_fails_if_wrong_participants_are_used() { let one = AccountKeyring::Dave; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); @@ -584,7 +584,7 @@ async fn test_request_limit_are_updated_during_signing() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); @@ -683,7 +683,7 @@ async fn test_fails_to_sign_if_non_signing_group_participants_are_used() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); @@ -777,7 +777,7 @@ async fn test_program_with_config() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); @@ -857,7 +857,7 @@ async fn test_jumpstart_network() { initialize_test_logger().await; clean_tests(); - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; do_jump_start(&api, &rpc, AccountKeyring::Alice.pair()).await; @@ -1001,7 +1001,7 @@ async fn test_fail_infinite_program() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let mnemonic = development_mnemonic(&Some(ValidatorName::Alice)); let (tss_signer, _static_secret) = @@ -1092,7 +1092,7 @@ async fn test_device_key_proxy() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key @@ -1223,7 +1223,7 @@ async fn test_faucet() { let two = AccountKeyring::Eve; let alice = AccountKeyring::Alice; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; let non_signer = jump_start_network(&entropy_api, &rpc).await.unwrap(); @@ -1386,7 +1386,7 @@ async fn test_registration_flow() { let bob = AccountKeyring::Bob; let charlie = AccountKeyring::Charlie; - let (entropy_api, rpc, _validator_ips, _validator_ids) = + let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // We first need to jump start the network and grab the resulting network wide verifying key diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 8c0dc9520..3338d9dad 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -29,7 +29,9 @@ use crate::{ }; use entropy_client::{ self as test_client, - chain_api::entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, + chain_api::{ + entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, EntropyConfig, + }, }; use entropy_client::{ chain_api::{ @@ -43,7 +45,7 @@ use entropy_kvdb::{ clean_tests, kv_manager::helpers::{deserialize, serialize}, }; -use entropy_protocol::KeyShareWithAuxInfo; +use entropy_protocol::{KeyShareWithAuxInfo, PartyId}; use entropy_shared::{ OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, }; @@ -54,7 +56,7 @@ use entropy_testing_utils::{ }, helpers::spawn_tss_nodes_and_start_chain, substrate_context::{test_node_process_testing_state, testing_context}, - test_context_stationary, ChainSpecType, + test_context_stationary, ChainSpecType, TestNodeProcess, }; use parity_scale_codec::Encode; use serial_test::serial; @@ -70,11 +72,9 @@ async fn test_reshare_basic() { initialize_test_logger().await; clean_tests(); - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; - let current_block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; - println!("Block number {}", current_block_number); let client = reqwest::Client::new(); // Get current signers diff --git a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs b/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs index 0e8384187..4ff18c2e7 100644 --- a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs +++ b/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs @@ -46,7 +46,7 @@ async fn integration_test_register_sign_reshare_sign() { initialize_test_logger().await; clean_tests(); - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // First jumpstart the network diff --git a/crates/threshold-signature-server/tests/sign_eth_tx.rs b/crates/threshold-signature-server/tests/sign_eth_tx.rs index a356172c5..5dde76391 100644 --- a/crates/threshold-signature-server/tests/sign_eth_tx.rs +++ b/crates/threshold-signature-server/tests/sign_eth_tx.rs @@ -50,7 +50,7 @@ async fn integration_test_sign_eth_tx() { initialize_test_logger().await; clean_tests(); - let (api, rpc, _validator_ips, _validator_ids) = + let (_ctx, api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; // First jumpstart the network From ff676d2e8162b3f89d42fc68d48aec14fd873ca8 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 14 Nov 2024 16:24:57 +0100 Subject: [PATCH 08/28] Add pre-generated keyshares directly to the kvdb, rather than using the unsafe API --- crates/testing-utils/src/helpers.rs | 2 + .../src/helpers/tests.rs | 95 ++++++++++--------- .../src/unsafe/api.rs | 1 + .../src/validator/api.rs | 1 + .../src/validator/tests.rs | 13 +-- 5 files changed, 60 insertions(+), 52 deletions(-) diff --git a/crates/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 4d6332ae3..4f3b62322 100644 --- a/crates/testing-utils/src/helpers.rs +++ b/crates/testing-utils/src/helpers.rs @@ -20,6 +20,7 @@ use crate::{ ChainSpecType, TestNodeProcess, }; use entropy_protocol::PartyId; +use entropy_tss::{helpers::tests::put_keyshares_in_db, launch::ValidatorName}; use rand::{rngs::StdRng, SeedableRng}; use subxt::{backend::legacy::LegacyRpcMethods, utils::AccountId32, OnlineClient}; pub use tdx_quote::encode_verifying_key; @@ -43,6 +44,7 @@ pub async fn spawn_tss_nodes_and_start_chain( 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) } diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index e16fbe6fe..a7e9b7f2a 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -37,7 +37,6 @@ use crate::{ substrate::submit_transaction, validator::get_signer_and_x25519_secret_from_mnemonic, }, - r#unsafe::api::UnsafeQuery, signing_client::ListenerState, AppState, }; @@ -149,19 +148,12 @@ impl fmt::Display for ChainSpecType { } } -/// 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 = vec![3001i64, 3002, 3003, 3004]; let (alice_axum, alice_kv) = create_clients("validator1".to_string(), vec![], vec![], &Some(ValidatorName::Alice)).await; @@ -205,21 +197,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::Dave, ValidatorName::Alice, alice_kv).await; + put_keyshares_in_db(ValidatorName::Dave, ValidatorName::Bob, bob_kv).await; + put_keyshares_in_db(ValidatorName::Dave, ValidatorName::Charlie, charlie_kv).await; } tokio::time::sleep(Duration::from_secs(1)).await; @@ -229,7 +224,11 @@ 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( + non_signer_name: ValidatorName, + 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!( @@ -240,18 +239,22 @@ async fn put_keyshares_in_db(non_signer_name: ValidatorName, validator_name: Val 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(); + + // 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(); + // let response = http_client + // .post(format!("http://127.0.0.1:{port}/unsafe/put")) + // .header("Content-Type", "application/json") + // .body(unsafe_put.clone()) + // .send() + // .await + // .unwrap(); + // println!("Response: {:?}", response); } /// Removes the program at the program hash @@ -318,15 +321,15 @@ pub async fn jump_start_network_with_signer( 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"); - } + // 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 } diff --git a/crates/threshold-signature-server/src/unsafe/api.rs b/crates/threshold-signature-server/src/unsafe/api.rs index 350e962cb..fe30d19de 100644 --- a/crates/threshold-signature-server/src/unsafe/api.rs +++ b/crates/threshold-signature-server/src/unsafe/api.rs @@ -80,6 +80,7 @@ pub async fn unsafe_get( )] pub async fn put(State(app_state): State, Json(key): Json) -> StatusCode { tracing::trace!("Attempting to write value {:?} to database", &key.value); + println!("WRITING VALUE {:?}", key.value); match app_state.kv_store.kv().exists(&key.key.to_owned()).await { Ok(v) => { if v { diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index aff360cb5..d8ae28a9d 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -109,6 +109,7 @@ pub async fn new_reshare( if data.new_signers.contains(&my_stash_address.encode()) { None } else { + println!("Loading keyshare"); let kvdb_result = app_state.kv_store.kv().get(&hex::encode(NETWORK_PARENT_KEY)).await?; let key_share: KeyShareWithAuxInfo = entropy_kvdb::kv_manager::helpers::deserialize(&kvdb_result) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 3338d9dad..72fd9c565 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -45,7 +45,7 @@ use entropy_kvdb::{ clean_tests, kv_manager::helpers::{deserialize, serialize}, }; -use entropy_protocol::{KeyShareWithAuxInfo, PartyId}; +use entropy_protocol::KeyShareWithAuxInfo; use entropy_shared::{ OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, }; @@ -73,7 +73,7 @@ async fn test_reshare_basic() { clean_tests(); let (_ctx, api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; let client = reqwest::Client::new(); @@ -86,17 +86,18 @@ async fn test_reshare_basic() { let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); signers.push(server_info); } + println!("Signers {:?}", signers); // A map of account IDs to serialized keyshares before the reshare let mut key_shares_before = HashMap::new(); for signer in signers.iter() { let port = get_port(signer); - 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()); + key_shares_before.insert(signer.tss_account.0, key_share); } + println!("Keyshares before {:?}", key_shares_before); // Get all validators // let validators_query = entropy::storage().session().validators(); // let all_validators = query_chain(&api, &rpc, validators_query, None).await.unwrap().unwrap(); From 082fac3b92cbc87528053844a057e8ac6fd4fbc3 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 14 Nov 2024 16:54:00 +0100 Subject: [PATCH 09/28] Update integratiion test chain spec --- node/cli/src/chain_spec/integration_tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/node/cli/src/chain_spec/integration_tests.rs b/node/cli/src/chain_spec/integration_tests.rs index 859099fc6..9903be058 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -59,7 +59,7 @@ 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"), ], None, )) @@ -82,7 +82,7 @@ pub fn integration_tests_jumpstarted_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"), ], Some(vec![ get_account_id_from_seed::("Alice//stash"), @@ -247,7 +247,7 @@ 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 { From 8ead21b3a9df7c57d2334a916fbd233eb51cf4f1 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 15 Nov 2024 09:30:28 +0100 Subject: [PATCH 10/28] Fixes for pre-jumpstarted state in tests - mocking jump start no longer works --- crates/protocol/src/execute_protocol.rs | 3 +- crates/testing-utils/src/helpers.rs | 4 +- crates/testing-utils/src/lib.rs | 4 +- .../src/helpers/tests.rs | 69 ++----------------- .../src/user/tests.rs | 69 +++++++------------ .../src/validator/tests.rs | 17 +++-- node/cli/src/chain_spec/integration_tests.rs | 46 ++++--------- node/cli/src/command.rs | 6 +- 8 files changed, 65 insertions(+), 153 deletions(-) diff --git a/crates/protocol/src/execute_protocol.rs b/crates/protocol/src/execute_protocol.rs index 835f3c673..0e1d86e5b 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()); @@ -397,6 +397,7 @@ pub async fn execute_reshare( 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/testing-utils/src/helpers.rs b/crates/testing-utils/src/helpers.rs index 4f3b62322..54ccd8a18 100644 --- a/crates/testing-utils/src/helpers.rs +++ b/crates/testing-utils/src/helpers.rs @@ -20,13 +20,15 @@ use crate::{ ChainSpecType, TestNodeProcess, }; use entropy_protocol::PartyId; -use entropy_tss::{helpers::tests::put_keyshares_in_db, launch::ValidatorName}; use rand::{rngs::StdRng, SeedableRng}; use subxt::{backend::legacy::LegacyRpcMethods, utils::AccountId32, OnlineClient}; 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, ) -> ( 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/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index a7e9b7f2a..033611a1c 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -212,9 +212,9 @@ pub async fn spawn_testing_validators( ids.push(dave_id); if chain_spec_type == ChainSpecType::IntegrationJumpStarted { - put_keyshares_in_db(ValidatorName::Dave, ValidatorName::Alice, alice_kv).await; - put_keyshares_in_db(ValidatorName::Dave, ValidatorName::Bob, bob_kv).await; - put_keyshares_in_db(ValidatorName::Dave, ValidatorName::Charlie, charlie_kv).await; + 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; @@ -224,37 +224,19 @@ pub async fn spawn_testing_validators( } /// Add the pre-generated test keyshares to a kvdb -pub async fn put_keyshares_in_db( - non_signer_name: ValidatorName, - validator_name: ValidatorName, - kvdb: KvManager, -) { +pub async fn put_keyshares_in_db(validator_name: ValidatorName, kvdb: KvManager) { + let non_signer_name = ValidatorName::Dave; 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 )); - println!("File path {:?}", file_path); std::fs::read(file_path).unwrap() }; let reservation = kvdb.kv().reserve_key(hex::encode(NETWORK_PARENT_KEY)).await.unwrap(); kvdb.kv().put(reservation, keyshare_bytes).await.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(); - // let response = http_client - // .post(format!("http://127.0.0.1:{port}/unsafe/put")) - // .header("Content-Type", "application/json") - // .body(unsafe_put.clone()) - // .send() - // .await - // .unwrap(); - // println!("Response: {:?}", response); } /// Removes the program at the program hash @@ -293,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( diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 9e07f4c1c..3903dd975 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -87,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, - store_program_and_register, unsafe_get, + initialize_test_logger, run_to_block, setup_client, store_program_and_register, + unsafe_get, }, user::compute_hash, validator::get_signer_and_x25519_secret_from_mnemonic, @@ -131,14 +131,12 @@ async fn test_signature_requests_fail_on_different_conditions() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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(); - // 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; @@ -360,14 +358,12 @@ async fn test_signature_requests_fail_validator_info_wrong() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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(); - // 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; @@ -433,13 +429,10 @@ async fn signature_request_with_derived_account_works() { let charlie = AccountKeyring::Charlie; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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 (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) = @@ -473,9 +466,9 @@ async fn test_signing_fails_if_wrong_participants_are_used() { let one = AccountKeyring::Dave; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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()); @@ -585,9 +578,9 @@ async fn test_request_limit_are_updated_during_signing() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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; // Register the user with a test program @@ -684,9 +677,9 @@ async fn test_fails_to_sign_if_non_signing_group_participants_are_used() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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; @@ -778,9 +771,9 @@ async fn test_program_with_config() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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; @@ -1002,12 +995,12 @@ async fn test_fail_infinite_program() { let two = AccountKeyring::Two; let (_ctx, api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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 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( @@ -1093,11 +1086,9 @@ async fn test_device_key_proxy() { let two = AccountKeyring::Two; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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; @@ -1224,9 +1215,9 @@ async fn test_faucet() { let alice = AccountKeyring::Alice; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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; @@ -1387,11 +1378,9 @@ async fn test_registration_flow() { let charlie = AccountKeyring::Charlie; let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + 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 - jump_start_network(&entropy_api, &rpc).await; + let non_signer = ValidatorName::Dave; let jump_start_progress_query = entropy::storage().staking_extension().jump_start_progress(); let jump_start_progress = @@ -1616,16 +1605,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/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 72fd9c565..8ee1c8e51 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -21,7 +21,6 @@ use crate::{ unsafe_get, }, }, - user::tests::jump_start_network, validator::{ api::{is_signer_or_delete_parent_key, prune_old_holders, validate_new_reshare}, errors::ValidatorErr, @@ -54,9 +53,8 @@ use entropy_testing_utils::{ ALICE_STASH_ADDRESS, AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, RANDOM_ACCOUNT, TEST_PROGRAM_WASM_BYTECODE, }, - helpers::spawn_tss_nodes_and_start_chain, substrate_context::{test_node_process_testing_state, testing_context}, - test_context_stationary, ChainSpecType, TestNodeProcess, + test_context_stationary, ChainSpecType, }; use parity_scale_codec::Encode; use serial_test::serial; @@ -72,8 +70,17 @@ async fn test_reshare_basic() { initialize_test_logger().await; clean_tests(); - let (_ctx, api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; + let chain_spec_type = ChainSpecType::IntegrationJumpStarted; + let (validator_ips, validator_ids) = + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; + + 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(); diff --git a/node/cli/src/chain_spec/integration_tests.rs b/node/cli/src/chain_spec/integration_tests.rs index 9903be058..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") @@ -61,34 +70,7 @@ pub fn integration_tests_config() -> ChainSpec { get_account_id_from_seed::("Bob//stash"), get_account_id_from_seed::("Charlie//stash"), ], - None, - )) - .build() -} - -pub fn integration_tests_jumpstarted_config() -> ChainSpec { - ChainSpec::builder(wasm_binary_unwrap(), Default::default()) - .with_name("Integration Test") - .with_id("integration_tests") - .with_chain_type(ChainType::Development) - .with_genesis_config_patch(integration_tests_genesis_config( - vec![ - crate::chain_spec::authority_keys_from_seed("Alice"), - crate::chain_spec::authority_keys_from_seed("Bob"), - crate::chain_spec::authority_keys_from_seed("Charlie"), - crate::chain_spec::authority_keys_from_seed("Dave"), - ], - vec![], - get_account_id_from_seed::("Alice"), - vec![ - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - ], - Some(vec![ - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - ]), + jump_started_signers, )) .build() } diff --git a/node/cli/src/command.rs b/node/cli/src/command.rs index 4215ff7da..1ac8a7cc2 100644 --- a/node/cli/src/command.rs +++ b/node/cli/src/command.rs @@ -79,10 +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" => { - Box::new(chain_spec::integration_tests::integration_tests_jumpstarted_config()) + 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()), From e94c4e33e1bd08f441059843d5fa56428e5ca305 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 15 Nov 2024 12:29:57 +0100 Subject: [PATCH 11/28] Update reshare test --- .../src/validator/tests.rs | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 8ee1c8e51..b74980486 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -60,7 +60,7 @@ use parity_scale_codec::Encode; use serial_test::serial; use sp_core::Pair; use sp_keyring::AccountKeyring; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use subxt::utils::AccountId32; use synedrion::k256::ecdsa::VerifyingKey; @@ -87,6 +87,8 @@ async fn test_reshare_basic() { // 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); @@ -104,7 +106,6 @@ async fn test_reshare_basic() { key_shares_before.insert(signer.tss_account.0, key_share); } - println!("Keyshares before {:?}", key_shares_before); // Get all validators // let validators_query = entropy::storage().session().validators(); // let all_validators = query_chain(&api, &rpc, validators_query, None).await.unwrap().unwrap(); @@ -137,36 +138,47 @@ async fn test_reshare_basic() { // 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(); + // 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()); + // } - // 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 + // }; - 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); + 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; } - signers - }; - - println!("Signers {:?}", signers); - println!("NEW Signers {:?}", new_signers); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + // println!("Signers {:?}", signers); + // println!("NEW Signers {:?}", new_signers); // for signer in new_signers { // let _ = client From 02147a823b5b23e3c5fe994668699c389be9bcbd Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 15 Nov 2024 13:46:56 +0100 Subject: [PATCH 12/28] Update client tests --- crates/client/src/tests.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index 11b116ff4..b7e2f81d4 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -20,7 +20,6 @@ use entropy_testing_utils::{ helpers::{ derive_mock_pck_verifying_key, encode_verifying_key, spawn_tss_nodes_and_start_chain, }, - jump_start_network, substrate_context::test_context_stationary, ChainSpecType, }; @@ -137,12 +136,7 @@ async fn test_remove_program_reference_counter() { let program_owner = AccountKeyring::Ferdie.pair(); let (_ctx, api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; - - // Jumpstart the network - let alice = AccountKeyring::Alice; - let signer = PairSigner::::new(alice.clone().into()); - jump_start_network(&api, &rpc, &signer).await; + spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; // Store a program let program_pointer = store_program( From c80a8f0abc12706b121a4a7b4478e6c8712349b7 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 15 Nov 2024 15:09:06 +0100 Subject: [PATCH 13/28] Typo --- node/cli/src/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/cli/src/command.rs b/node/cli/src/command.rs index 1ac8a7cc2..b206b5ee5 100644 --- a/node/cli/src/command.rs +++ b/node/cli/src/command.rs @@ -83,7 +83,7 @@ impl SubstrateCli for Cli { Box::new(chain_spec::integration_tests::integration_tests_config(jumpstarted)) }, "integration-tests-jumpstarted" => { - let jumpstarted = true + let jumpstarted = true; Box::new(chain_spec::integration_tests::integration_tests_config(jumpstarted)) }, "testnet-local" => Box::new(chain_spec::testnet::testnet_local_config()), From 17d4b02befc5201da5ef1a9f2cd413872692fa48 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 22 Nov 2024 08:32:55 +0100 Subject: [PATCH 14/28] Tidy test --- .../src/validator/tests.rs | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index b74980486..142a6bdd6 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -70,8 +70,7 @@ async fn test_reshare_basic() { initialize_test_logger().await; clean_tests(); - let chain_spec_type = ChainSpecType::IntegrationJumpStarted; - let (validator_ips, validator_ids) = + let (_validator_ips, _validator_ids) = spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) .await; @@ -106,37 +105,8 @@ async fn test_reshare_basic() { key_shares_before.insert(signer.tss_account.0, key_share); } - // 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(), ""); - // } + // let block_number = TEST_RESHARE_BLOCK_NUMBER; + // run_to_block(&rpc, block_number + 1).await; // for (tss_account, key_share_and_aux_before) in key_shares_before.iter() { // let (key_share_before, aux_info_before): KeyShareWithAuxInfo = From 0204c837bbcef2d318dcbc2f4a1a766a498282b9 Mon Sep 17 00:00:00 2001 From: peg Date: Fri, 22 Nov 2024 08:33:03 +0100 Subject: [PATCH 15/28] Tidy test --- crates/protocol/src/execute_protocol.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/protocol/src/execute_protocol.rs b/crates/protocol/src/execute_protocol.rs index 0e1d86e5b..0e2731f67 100644 --- a/crates/protocol/src/execute_protocol.rs +++ b/crates/protocol/src/execute_protocol.rs @@ -394,6 +394,9 @@ 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 { From 4381c761c67933d7b357e569f7ff9e27c458eb11 Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 10:35:45 +0100 Subject: [PATCH 16/28] Fix for deadline issue --- .../src/helpers/tests.rs | 10 +- .../src/signing_client/tests.rs | 6 +- .../src/validator/api.rs | 58 +++-- .../src/validator/tests.rs | 241 +++++------------- 4 files changed, 120 insertions(+), 195 deletions(-) diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index 033611a1c..bea6463de 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -18,13 +18,12 @@ // only compile when testing or when the test_helpers feature is enabled #![cfg(any(test, feature = "test_helpers"))] +#[cfg(test)] +use crate::helpers::tests::entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec; use crate::{ app, chain_api::{ - entropy::{ - self, runtime_types::bounded_collections::bounded_vec::BoundedVec, - runtime_types::pallet_staking_extension::pallet::JumpStartStatus, - }, + entropy::{self, runtime_types::pallet_staking_extension::pallet::JumpStartStatus}, EntropyConfig, }, get_signer, @@ -35,7 +34,6 @@ use crate::{ }, logger::{Instrumentation, Logger}, substrate::submit_transaction, - validator::get_signer_and_x25519_secret_from_mnemonic, }, signing_client::ListenerState, AppState, @@ -46,7 +44,7 @@ use entropy_kvdb::{encrypted_sled::PasswordMethod, get_db_path, kv_manager::KvMa use entropy_protocol::PartyId; #[cfg(test)] use entropy_shared::EncodedVerifyingKey; -use entropy_shared::{EVE_VERIFYING_KEY, NETWORK_PARENT_KEY}; +use entropy_shared::NETWORK_PARENT_KEY; use std::{fmt, time::Duration}; use subxt::{ backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, diff --git a/crates/threshold-signature-server/src/signing_client/tests.rs b/crates/threshold-signature-server/src/signing_client/tests.rs index bf2165fab..5ed3be78f 100644 --- a/crates/threshold-signature-server/src/signing_client/tests.rs +++ b/crates/threshold-signature-server/src/signing_client/tests.rs @@ -45,10 +45,12 @@ use sp_keyring::AccountKeyring; async fn test_proactive_refresh() { initialize_test_logger().await; clean_tests(); - let _cxt = &test_node_process_testing_state(ChainSpecType::Integration, false).await[0]; + let _cxt = + &test_node_process_testing_state(ChainSpecType::IntegrationJumpStarted, false).await[0]; let (validator_ips, _ids) = - spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).await; + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; let signing_committee_ips = &validator_ips[..3].to_vec(); let client = reqwest::Client::new(); diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index d8ae28a9d..19284fc78 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -44,6 +44,7 @@ use subxt::{ OnlineClient, }; use synedrion::{KeyResharingInputs, NewHolder, OldHolder}; +use x25519_dalek::StaticSecret; /// HTTP POST endpoint called by the off-chain worker (propagation pallet) during network reshare. /// @@ -61,20 +62,48 @@ pub async fn new_reshare( let rpc = get_rpc(&app_state.configuration.endpoint).await?; validate_new_reshare(&api, &rpc, &data, &app_state.kv_store).await?; + let (signer, x25519_secret_key) = get_signer_and_x25519_secret(&app_state.kv_store) + .await + .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let next_signers_query = entropy::storage().staking_extension().next_signers(); let next_signers = query_chain(&api, &rpc, next_signers_query, None) .await? .ok_or_else(|| ValidatorErr::ChainFetch("Error getting next signers"))? .next_signers; - let validators_info = get_validators_info(&api, &rpc, next_signers) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; - let (signer, x25519_secret_key) = get_signer_and_x25519_secret(&app_state.kv_store) - .await - .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let is_proper_signer = validators_info + .iter() + .any(|validator_info| validator_info.tss_account == *signer.account_id()); + + if !is_proper_signer { + return Ok(StatusCode::MISDIRECTED_REQUEST); + } + + // Do reshare in a separate task so we can already respond + tokio::spawn(async move { + if let Err(err) = + do_reshare(api, &rpc, signer, &x25519_secret_key, data, validators_info, app_state) + .await + { + tracing::error!("Error during reshare: {err}"); + } + }); + Ok(StatusCode::OK) +} +async fn do_reshare( + api: OnlineClient, + rpc: &LegacyRpcMethods, + signer: PairSigner, + x25519_secret_key: &StaticSecret, + data: OcwMessageReshare, + validators_info: Vec, + app_state: AppState, +) -> Result<(), ValidatorErr> { let verifying_key_query = entropy::storage().staking_extension().jump_start_progress(); let parent_key_details = query_chain(&api, &rpc, verifying_key_query, None) .await? @@ -92,15 +121,6 @@ pub async fn new_reshare( .map_err(|_| ValidatorErr::Conversion("Verifying key conversion"))?, ) .map_err(|e| ValidatorErr::VerifyingKeyError(e.to_string()))?; - - let is_proper_signer = validators_info - .iter() - .any(|validator_info| validator_info.tss_account == *signer.account_id()); - - if !is_proper_signer { - return Ok(StatusCode::MISDIRECTED_REQUEST); - } - let my_stash_address = get_stash_address(&api, &rpc, signer.account_id()) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; @@ -159,6 +179,7 @@ pub async fn new_reshare( converted_validator_info.push(validator_info.clone()); tss_accounts.push(validator_info.tss_account.clone()); } + let channels = get_channels( &app_state.listener_state, converted_validator_info, @@ -168,7 +189,6 @@ pub async fn new_reshare( &x25519_secret_key, ) .await?; - let (new_key_share, aux_info) = execute_reshare(session_id.clone(), channels, signer.signer(), inputs, &new_holders, None) .await?; @@ -186,7 +206,7 @@ pub async fn new_reshare( // TODO: Error handling really complex needs to be thought about. confirm_key_reshare(&api, &rpc, &signer).await?; - Ok(StatusCode::OK) + Ok(()) } /// HTTP POST endpoint called by the off-chain worker (propagation pallet) after a network key reshare. @@ -199,7 +219,10 @@ pub async fn rotate_network_key( // validate from chain let api = get_api(&app_state.configuration.endpoint).await?; let rpc = get_rpc(&app_state.configuration.endpoint).await?; + + tracing::info!("Rotate network key called"); validate_rotate_network_key(&api, &rpc).await?; + tracing::info!("Rotate network key validated"); let (signer, _) = get_signer_and_x25519_secret(&app_state.kv_store) .await @@ -221,10 +244,12 @@ pub async fn rotate_network_key( ) .await?; + tracing::info!("Proper signer: {is_proper_signer}"); + if !is_proper_signer { return Ok(StatusCode::MISDIRECTED_REQUEST); } - + tracing::info!("Rotating network key"); let network_parent_key_heading = hex::encode(NETWORK_PARENT_KEY); let next_network_parent_key_heading = hex::encode(NEXT_NETWORK_PARENT_KEY); @@ -238,6 +263,7 @@ pub async fn rotate_network_key( let reservation = app_state.kv_store.kv().reserve_key(network_parent_key_heading).await?; app_state.kv_store.kv().put(reservation, new_parent_key).await?; + tracing::info!("Rotated network key"); Ok(StatusCode::OK) } diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 142a6bdd6..a1bbf8846 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -28,9 +28,7 @@ use crate::{ }; use entropy_client::{ self as test_client, - chain_api::{ - entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, EntropyConfig, - }, + chain_api::entropy::runtime_types::pallet_staking_extension::pallet::ServerInfo, }; use entropy_client::{ chain_api::{ @@ -40,14 +38,8 @@ use entropy_client::{ substrate::query_chain, Hasher, }; -use entropy_kvdb::{ - clean_tests, - kv_manager::helpers::{deserialize, serialize}, -}; -use entropy_protocol::KeyShareWithAuxInfo; -use entropy_shared::{ - OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, -}; +use entropy_kvdb::clean_tests; +use entropy_shared::{OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY}; use entropy_testing_utils::{ constants::{ ALICE_STASH_ADDRESS, AUXILARY_DATA_SHOULD_SUCCEED, PREIMAGE_SHOULD_SUCCEED, RANDOM_ACCOUNT, @@ -60,7 +52,7 @@ use parity_scale_codec::Encode; use serial_test::serial; use sp_core::Pair; use sp_keyring::AccountKeyring; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use subxt::utils::AccountId32; use synedrion::k256::ecdsa::VerifyingKey; @@ -94,181 +86,88 @@ async fn test_reshare_basic() { let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); signers.push(server_info); } - println!("Signers {:?}", signers); - // A map of account IDs to serialized keyshares before the reshare - let mut key_shares_before = HashMap::new(); for signer in signers.iter() { let port = get_port(signer); let key_share = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; assert!(!key_share.is_empty()); - key_shares_before.insert(signer.tss_account.0, key_share); } - // let block_number = TEST_RESHARE_BLOCK_NUMBER; - // run_to_block(&rpc, block_number + 1).await; - - // for (tss_account, key_share_and_aux_before) in key_shares_before.iter() { - // let (key_share_before, aux_info_before): KeyShareWithAuxInfo = - // deserialize(key_share_and_aux_before).unwrap(); - // - // let port = get_port(signers.iter().find(|s| s.tss_account.0 == *tss_account).unwrap()); - // let key_share_and_aux_after = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; - // let (key_share_after, aux_info_after): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_after).unwrap(); - // - // // Check key share has not yet changed - // assert_eq!(serialize(&key_share_before).unwrap(), serialize(&key_share_after).unwrap()); - // // Check aux info has not yet changed - // assert_eq!(serialize(&aux_info_before).unwrap(), serialize(&aux_info_after).unwrap()); - // } - - // let new_signers = { - // let signer_query = entropy::storage().staking_extension().signers(); - // let signer_ids = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); - // let mut signers = Vec::new(); - // for signer in signer_ids { - // let query = entropy::storage().staking_extension().threshold_servers(signer); - // let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); - // signers.push(server_info); - // } - // signers - // }; - - loop { + let _new_signer_ids = loop { let new_signer_ids: HashSet<[u8; 32]> = { let signer_query = entropy::storage().staking_extension().signers(); let signer_ids = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); HashSet::from_iter(signer_ids.into_iter().map(|id| id.0)) }; if new_signer_ids != old_signer_ids { - break; + println!("Signers have changed"); + break new_signer_ids; } tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - // println!("Signers {:?}", signers); - // println!("NEW Signers {:?}", new_signers); - - // for signer in new_signers { - // let _ = client - // .post(format!( - // "http://{}/rotate_network_key", - // std::str::from_utf8(&signer.endpoint).unwrap() - // )) - // .send() - // .await - // .unwrap(); - // - // let key_share_and_aux_data_after_rotate = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - // let (key_share_after_rotate, aux_info_after_rotate): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_data_after_rotate).unwrap(); - // - // if let Some(key_share_and_aux_before) = key_shares_before.get(&signer.tss_account.0) { - // let (key_share_before, aux_info_before): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_before).unwrap(); - // // Check key share has changed - // assert_ne!( - // serialize(&key_share_before).unwrap(), - // serialize(&key_share_after_rotate).unwrap() - // ); - // // Check aux info has changed - // assert_ne!( - // serialize(&aux_info_before).unwrap(), - // serialize(&aux_info_after_rotate).unwrap() - // ); - // } - // - // // calling twice doesn't do anything - // let response = client - // .post(format!( - // "http://{}/rotate_network_key", - // std::str::from_utf8(&signer.endpoint).unwrap() - // )) - // .send() - // .await - // .unwrap(); - // - // assert_eq!(response.text().await.unwrap(), "Kv error: Recv Error: channel closed"); - // let key_share_and_aux_data_after_rotate_twice = - // unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), get_port(&signer)).await; - // let (key_share_after_rotate_twice, aux_info_after_rotate_twice): KeyShareWithAuxInfo = - // deserialize(&key_share_and_aux_data_after_rotate_twice).unwrap(); - // - // // Check key share has not changed - // assert_eq!( - // serialize(&key_share_after_rotate_twice).unwrap(), - // serialize(&key_share_after_rotate).unwrap() - // ); - // // Check aux info has not changed - // assert_eq!( - // serialize(&aux_info_after_rotate_twice).unwrap(), - // serialize(&aux_info_after_rotate).unwrap() - // ); + }; + + // Check that the new signers have keyshares + // for signer in new_signer_ids { + // let query = entropy::storage().staking_extension().threshold_servers(AccountId32(signer)); + // let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap(); + // let port = get_port(&server_info); + // let key_share = unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), port).await; + // assert!(!key_share.is_empty()); // } - // - // let current_block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; - // // Check that rotating the network key wont work again later - // run_to_block(&rpc, current_block_number + 3).await; - // - // let response_stale = - // client.post("http://127.0.0.1:3001/rotate_network_key").send().await.unwrap(); - // - // assert_eq!(response_stale.text().await.unwrap(), "Data is stale"); - // - // // Now test signing a message with the new keyshare set - // let account_owner = AccountKeyring::Ferdie.pair(); - // let signature_request_author = AccountKeyring::One; - // // Store a program - // let program_pointer = test_client::store_program( - // &api, - // &rpc, - // &account_owner, - // TEST_PROGRAM_WASM_BYTECODE.to_owned(), - // vec![], - // vec![], - // vec![], - // 0u8, - // ) - // .await - // .unwrap(); - // - // // Register, using that program - // let (verifying_key, _registered_info) = test_client::register( - // &api, - // &rpc, - // account_owner.clone(), - // AccountId32(account_owner.public().0), - // BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), - // ) - // .await - // .unwrap(); - // - // // Sign a message - // let recoverable_signature = test_client::sign( - // &api, - // &rpc, - // signature_request_author.pair(), - // verifying_key, - // PREIMAGE_SHOULD_SUCCEED.to_vec(), - // Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), - // ) - // .await - // .unwrap(); - // - // // Check the signature - // let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); - // let recovery_key_from_sig = VerifyingKey::recover_from_prehash( - // &message_should_succeed_hash, - // &recoverable_signature.signature, - // recoverable_signature.recovery_id, - // ) - // .unwrap(); - // assert_eq!( - // verifying_key.to_vec(), - // recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() - // ); + + // Now test signing a message with the new keyshare set + let account_owner = AccountKeyring::Ferdie.pair(); + let signature_request_author = AccountKeyring::One; + // Store a program + let program_pointer = test_client::store_program( + &api, + &rpc, + &account_owner, + TEST_PROGRAM_WASM_BYTECODE.to_owned(), + vec![], + vec![], + vec![], + 0u8, + ) + .await + .unwrap(); + dbg!("stored program"); + // Register, using that program + let (verifying_key, _registered_info) = test_client::register( + &api, + &rpc, + account_owner.clone(), + AccountId32(account_owner.public().0), + BoundedVec(vec![ProgramInstance { program_pointer, program_config: vec![] }]), + ) + .await + .unwrap(); + + dbg!("registered"); + // Sign a message + let recoverable_signature = test_client::sign( + &api, + &rpc, + signature_request_author.pair(), + verifying_key, + PREIMAGE_SHOULD_SUCCEED.to_vec(), + Some(AUXILARY_DATA_SHOULD_SUCCEED.to_vec()), + ) + .await + .unwrap(); + + // Check the signature + let message_should_succeed_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); + let recovery_key_from_sig = VerifyingKey::recover_from_prehash( + &message_should_succeed_hash, + &recoverable_signature.signature, + recoverable_signature.recovery_id, + ) + .unwrap(); + assert_eq!( + verifying_key.to_vec(), + recovery_key_from_sig.to_encoded_point(true).to_bytes().to_vec() + ); clean_tests(); } From 874a20680a0108b58eed81b37213282e5cdff496 Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 10:40:35 +0100 Subject: [PATCH 17/28] Rm unused variable --- crates/threshold-signature-server/src/user/tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 3903dd975..7402e3d02 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -1380,8 +1380,6 @@ async fn test_registration_flow() { let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; - let non_signer = ValidatorName::Dave; - let jump_start_progress_query = entropy::storage().staking_extension().jump_start_progress(); let jump_start_progress = query_chain(&entropy_api, &rpc, jump_start_progress_query, None).await.unwrap().unwrap(); From 202b0dfbab7fce2df836012bca095c9fe542fa14 Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 11:01:17 +0100 Subject: [PATCH 18/28] Fix local storage for rotate network key endpoint --- node/cli/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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( From dd7f7975526f4b4fa0a3f696b84c81af00830391 Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 11:20:06 +0100 Subject: [PATCH 19/28] Update test --- .../src/validator/api.rs | 5 ---- .../src/validator/tests.rs | 25 ++++++++++--------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index 19284fc78..f2a757dd1 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -220,9 +220,7 @@ pub async fn rotate_network_key( let api = get_api(&app_state.configuration.endpoint).await?; let rpc = get_rpc(&app_state.configuration.endpoint).await?; - tracing::info!("Rotate network key called"); validate_rotate_network_key(&api, &rpc).await?; - tracing::info!("Rotate network key validated"); let (signer, _) = get_signer_and_x25519_secret(&app_state.kv_store) .await @@ -244,8 +242,6 @@ pub async fn rotate_network_key( ) .await?; - tracing::info!("Proper signer: {is_proper_signer}"); - if !is_proper_signer { return Ok(StatusCode::MISDIRECTED_REQUEST); } @@ -263,7 +259,6 @@ pub async fn rotate_network_key( let reservation = app_state.kv_store.kv().reserve_key(network_parent_key_heading).await?; app_state.kv_store.kv().put(reservation, new_parent_key).await?; - tracing::info!("Rotated network key"); Ok(StatusCode::OK) } diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index a1bbf8846..3e217c232 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -93,7 +93,7 @@ async fn test_reshare_basic() { assert!(!key_share.is_empty()); } - let _new_signer_ids = loop { + let new_signer_ids = loop { let new_signer_ids: HashSet<[u8; 32]> = { let signer_query = entropy::storage().staking_extension().signers(); let signer_ids = query_chain(&api, &rpc, signer_query, None).await.unwrap().unwrap(); @@ -105,15 +105,8 @@ async fn test_reshare_basic() { } tokio::time::sleep(std::time::Duration::from_secs(1)).await; }; - - // 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()); - // } + // 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(); @@ -131,7 +124,7 @@ async fn test_reshare_basic() { ) .await .unwrap(); - dbg!("stored program"); + // Register, using that program let (verifying_key, _registered_info) = test_client::register( &api, @@ -143,7 +136,6 @@ async fn test_reshare_basic() { .await .unwrap(); - dbg!("registered"); // Sign a message let recoverable_signature = test_client::sign( &api, @@ -168,6 +160,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(); } From a1f4bca2754a6a4a5295f1f6bafdfc8a25e9e99a Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 11:44:47 +0100 Subject: [PATCH 20/28] Clippy --- .../src/validator/api.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index f2a757dd1..3c33d2041 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -86,7 +86,7 @@ pub async fn new_reshare( // 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) + do_reshare(&api, &rpc, signer, &x25519_secret_key, data, validators_info, app_state) .await { tracing::error!("Error during reshare: {err}"); @@ -96,7 +96,7 @@ pub async fn new_reshare( } async fn do_reshare( - api: OnlineClient, + api: &OnlineClient, rpc: &LegacyRpcMethods, signer: PairSigner, x25519_secret_key: &StaticSecret, @@ -105,7 +105,7 @@ async fn do_reshare( 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"))?; @@ -121,7 +121,7 @@ async fn do_reshare( .map_err(|_| ValidatorErr::Conversion("Verifying key conversion"))?, ) .map_err(|e| ValidatorErr::VerifyingKeyError(e.to_string()))?; - 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()))?; @@ -143,7 +143,7 @@ async fn do_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(); @@ -153,7 +153,7 @@ async fn do_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; @@ -186,7 +186,7 @@ async fn do_reshare( account_id, &session_id, &signer, - &x25519_secret_key, + x25519_secret_key, ) .await?; let (new_key_share, aux_info) = @@ -205,7 +205,7 @@ async fn do_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?; + confirm_key_reshare(api, rpc, &signer).await?; Ok(()) } From 170b5cc2f7f3d4cd9772798c1ed45c67a7e1abfe Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 12:01:47 +0100 Subject: [PATCH 21/28] Clippy --- crates/threshold-signature-server/src/helpers/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index bea6463de..536c67813 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -151,7 +151,7 @@ impl fmt::Display for ChainSpecType { pub async fn spawn_testing_validators( chain_spec_type: ChainSpecType, ) -> (Vec, Vec) { - let ports = vec![3001i64, 3002, 3003, 3004]; + let ports = [3001i64, 3002, 3003, 3004]; let (alice_axum, alice_kv) = create_clients("validator1".to_string(), vec![], vec![], &Some(ValidatorName::Alice)).await; From 04b83095b026b30bd7701e33ee2d29b1af7329d7 Mon Sep 17 00:00:00 2001 From: peg Date: Mon, 25 Nov 2024 13:19:38 +0100 Subject: [PATCH 22/28] Fix remaining tests --- .../src/helpers/tests.rs | 10 +++- .../src/user/tests.rs | 53 +++++++++++++------ .../src/validator/tests.rs | 16 ++---- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index 536c67813..b26f9a9ab 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -23,7 +23,10 @@ use crate::helpers::tests::entropy::runtime_types::bounded_collections::bounded_ use crate::{ app, chain_api::{ - entropy::{self, runtime_types::pallet_staking_extension::pallet::JumpStartStatus}, + entropy::{ + self, + runtime_types::pallet_staking_extension::pallet::{JumpStartStatus, ServerInfo}, + }, EntropyConfig, }, get_signer, @@ -360,3 +363,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/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 7402e3d02..2f740428b 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -43,7 +43,7 @@ use entropy_testing_utils::{ }, helpers::spawn_tss_nodes_and_start_chain, substrate_context::{test_context_stationary, testing_context}, - ChainSpecType, + 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, run_to_block, setup_client, store_program_and_register, - unsafe_get, + 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, @@ -577,8 +576,16 @@ async fn test_request_limit_are_updated_during_signing() { let one = AccountKeyring::One; let two = AccountKeyring::Two; - let (_ctx, entropy_api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::IntegrationJumpStarted).await; + let (_validator_ips, _validator_ids) = + spawn_testing_validators(crate::helpers::tests::ChainSpecType::IntegrationJumpStarted) + .await; + + let force_authoring = true; + 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 = ValidatorName::Dave; let (relayer_ip_and_key, _) = @@ -850,23 +857,39 @@ async fn test_jumpstart_network() { initialize_test_logger().await; clean_tests(); - let (_ctx, api, rpc, _validator_ips, _validator_ids) = - spawn_tss_nodes_and_start_chain(ChainSpecType::Integration).await; + let (_validator_ips, _validator_ids) = + spawn_testing_validators(crate::helpers::tests::ChainSpecType::Integration).await; + + let force_authoring = true; + 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(); diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 3e217c232..ff207bfb6 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -17,7 +17,7 @@ use crate::{ helpers::{ launch::{FORBIDDEN_KEYS, LATEST_BLOCK_NUMBER_RESHARE}, tests::{ - initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, + get_port, initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, unsafe_get, }, }, @@ -26,10 +26,7 @@ use crate::{ 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, @@ -178,7 +175,9 @@ 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(crate::helpers::tests::ChainSpecType::Integration).await; @@ -336,8 +335,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() -} From 94f13f761e5df0abae442b0229c614152806f480 Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 09:38:41 +0100 Subject: [PATCH 23/28] Fix tests, tidy --- .../src/validator/api.rs | 1 - .../src/validator/tests.rs | 12 +- .../tests/jumpstart_register_sign.rs | 103 ++++++++++ .../tests/register_sign_reshare_sign.rs | 190 ------------------ pallets/staking/src/lib.rs | 2 +- 5 files changed, 113 insertions(+), 195 deletions(-) create mode 100644 crates/threshold-signature-server/tests/jumpstart_register_sign.rs delete mode 100644 crates/threshold-signature-server/tests/register_sign_reshare_sign.rs diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index 3c33d2041..179342402 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -129,7 +129,6 @@ async fn do_reshare( if data.new_signers.contains(&my_stash_address.encode()) { None } else { - println!("Loading keyshare"); let kvdb_result = app_state.kv_store.kv().get(&hex::encode(NETWORK_PARENT_KEY)).await?; let key_share: KeyShareWithAuxInfo = entropy_kvdb::kv_manager::helpers::deserialize(&kvdb_result) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index ff207bfb6..9f23897a4 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -90,6 +90,7 @@ async fn test_reshare_basic() { assert!(!key_share.is_empty()); } + let mut i = 0; let new_signer_ids = loop { let new_signer_ids: HashSet<[u8; 32]> = { let signer_query = entropy::storage().staking_extension().signers(); @@ -97,11 +98,16 @@ async fn test_reshare_basic() { HashSet::from_iter(signer_ids.into_iter().map(|id| id.0)) }; if new_signer_ids != old_signer_ids { - println!("Signers have changed"); - break new_signer_ids; + break Ok(new_signer_ids); } + if i > 120 { + break Err("Timed out waiting for reshare"); + } + i += 1; tokio::time::sleep(std::time::Duration::from_secs(1)).await; - }; + } + .unwrap(); + // 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 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 4ff18c2e7..000000000 --- a/crates/threshold-signature-server/tests/register_sign_reshare_sign.rs +++ /dev/null @@ -1,190 +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, 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, - }, - helpers::spawn_tss_nodes_and_start_chain, - 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 (_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; - - 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(); - // } - - // 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 old: HashSet<[u8; 32]> = initial_signers.iter().map(|s| s.0).collect(); - let new: HashSet<[u8; 32]> = new_signer_stash_accounts.iter().map(|s| s.0).collect(); - assert_ne!(old, new); -} diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index c567030b0..4f7f854de 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -318,7 +318,7 @@ pub mod pallet { 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, // TODO use constant + parent_key_threshold: 2, }); } } From 895a1181fc56cbdac28e5a14f4888ba1eeabed2d Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 09:59:18 +0100 Subject: [PATCH 24/28] Update generated keyshares --- .../alice/keyshare-held-by-bob.keyshare | Bin 3472 -> 0 bytes .../alice/keyshare-held-by-charlie.keyshare | Bin 3472 -> 0 bytes .../alice/keyshare-held-by-dave.keyshare | Bin 3472 -> 0 bytes .../bob/keyshare-held-by-alice.keyshare | Bin 3472 -> 0 bytes .../bob/keyshare-held-by-charlie.keyshare | Bin 3472 -> 0 bytes .../bob/keyshare-held-by-dave.keyshare | Bin 3472 -> 0 bytes .../charlie/keyshare-held-by-alice.keyshare | Bin 3472 -> 0 bytes .../charlie/keyshare-held-by-bob.keyshare | Bin 3472 -> 0 bytes .../charlie/keyshare-held-by-dave.keyshare | Bin 3472 -> 0 bytes .../dave/keyshare-held-by-alice.keyshare | Bin 3472 -> 0 bytes .../dave/keyshare-held-by-bob.keyshare | Bin 3472 -> 0 bytes .../dave/keyshare-held-by-charlie.keyshare | Bin 3472 -> 0 bytes .../production/keyshare-held-by-alice.keyshare | Bin 0 -> 3472 bytes .../production/keyshare-held-by-bob.keyshare | Bin 0 -> 3472 bytes .../production/keyshare-held-by-charlie.keyshare | Bin 0 -> 3472 bytes .../test/alice/keyshare-held-by-bob.keyshare | Bin 2192 -> 0 bytes .../test/alice/keyshare-held-by-charlie.keyshare | Bin 2192 -> 0 bytes .../test/alice/keyshare-held-by-dave.keyshare | Bin 2192 -> 0 bytes .../test/bob/keyshare-held-by-alice.keyshare | Bin 2192 -> 0 bytes .../test/bob/keyshare-held-by-charlie.keyshare | Bin 2192 -> 0 bytes .../test/bob/keyshare-held-by-dave.keyshare | Bin 2192 -> 0 bytes .../test/charlie/keyshare-held-by-alice.keyshare | Bin 2192 -> 0 bytes .../test/charlie/keyshare-held-by-bob.keyshare | Bin 2192 -> 0 bytes .../test/charlie/keyshare-held-by-dave.keyshare | Bin 2192 -> 0 bytes .../test/dave/keyshare-held-by-alice.keyshare | Bin 2192 -> 0 bytes .../test/dave/keyshare-held-by-bob.keyshare | Bin 2192 -> 0 bytes .../test/dave/keyshare-held-by-charlie.keyshare | Bin 2192 -> 0 bytes .../test/keyshare-held-by-alice.keyshare | Bin 0 -> 2192 bytes .../keyshares/test/keyshare-held-by-bob.keyshare | Bin 0 -> 2192 bytes .../test/keyshare-held-by-charlie.keyshare | Bin 0 -> 2192 bytes .../src/helpers/tests.rs | 5 ++--- 31 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 crates/testing-utils/keyshares/production/alice/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/production/alice/keyshare-held-by-charlie.keyshare delete mode 100644 crates/testing-utils/keyshares/production/alice/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/production/bob/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/production/bob/keyshare-held-by-charlie.keyshare delete mode 100644 crates/testing-utils/keyshares/production/bob/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/production/charlie/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/production/charlie/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/production/charlie/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/production/dave/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/production/dave/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/production/dave/keyshare-held-by-charlie.keyshare create mode 100644 crates/testing-utils/keyshares/production/keyshare-held-by-alice.keyshare create mode 100644 crates/testing-utils/keyshares/production/keyshare-held-by-bob.keyshare create mode 100644 crates/testing-utils/keyshares/production/keyshare-held-by-charlie.keyshare delete mode 100644 crates/testing-utils/keyshares/test/alice/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/test/alice/keyshare-held-by-charlie.keyshare delete mode 100644 crates/testing-utils/keyshares/test/alice/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/test/bob/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/test/bob/keyshare-held-by-charlie.keyshare delete mode 100644 crates/testing-utils/keyshares/test/bob/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/test/charlie/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/test/charlie/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/test/charlie/keyshare-held-by-dave.keyshare delete mode 100644 crates/testing-utils/keyshares/test/dave/keyshare-held-by-alice.keyshare delete mode 100644 crates/testing-utils/keyshares/test/dave/keyshare-held-by-bob.keyshare delete mode 100644 crates/testing-utils/keyshares/test/dave/keyshare-held-by-charlie.keyshare create mode 100644 crates/testing-utils/keyshares/test/keyshare-held-by-alice.keyshare create mode 100644 crates/testing-utils/keyshares/test/keyshare-held-by-bob.keyshare create mode 100644 crates/testing-utils/keyshares/test/keyshare-held-by-charlie.keyshare 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 b0f17866c1449059e2a753deb0f071e8917abf42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 G?fyT)o+cy! 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 5ae742022253e5ce3660c661a12110b1c3120564..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 E5AA~&761SM 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 9a912171111a5bab2c222dcd6b1828a5cadc2fd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 IJR{4009kxBbpQYW 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 2ee39900df1f68d53e69b00730b9b8342b4e1dfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 Ruh8x@TA@yK;P223{{t;hDJ=j1 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 81c53944d1779cb4fe07ad3847e6532b888d40b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 KnuvE8#{U2#nKrfn 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 4bf9c77318385e076c264f2d5687065eb3dcf16d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 ME%rbk*@ENvA92AgQvd(} 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 4fc811e3889bcea39c40a6f9d795664e6a16b307..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 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 e56bd6bdd3de4dfaf85daa304e592d30977012b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3472 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 J#;w?!{{a~}AYuRj 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 0000000000000000000000000000000000000000..bf4eddd156cb005c9890b564b70324015c53f3c3 GIT binary patch literal 3472 zcmbW31y>Uc8-@ufM|a95E%B1lT>}OZkUJ&h`XG-9O+u_jS&5o^#788R+Wkn|eaww^85#dzhJrjj^GZpS!zJ7*g)G zg{hsVPXyRO!Af5hs;_5#-yBQ)A>nYapNFcosWsSC)y&H+1g2nsGK9*7c-=p;m;^+npb_ zU1N`h8{nN8Rb6L8%FhR%?JERffGz@ywtF^WZ>Nv0-s$%{NldfW5eT+tVox{ov7iCn zc@>}X^cnWHc42NhKFo&)@!SSH0l|Cwk7fX65kDfkiAhCbd0b)LX_dw+3<)AhAWdZ! z1g5L10@0adywb}Jk5jEF1F=3or>dN|m-Ngn z^mK?&{`ze)d!EfZN?*>gsF2_yt`pj`>Wt4D_l(>T&1ldv z(*9|c&;ua;z?27jLf48F@(in3Hg_OMZqM%Mu`~x=Dk#(^y$Dci>Lp*1aQ!uc-Qx4PnE9XCk(Sgj4=N* zzklMKloQ6Ib@0`)rME&!T?*hypLd`Ndz~SUL&w4A2lmTUC{7Uzqt3O&Ig9zK0zd=7 zs~0ilx-B5U8dgmQkS%_=4B%-wFI&vw*G<;mCF~}9bnxY2CO?o`JIpzMd^OdmgL$Ku@lgH)`#teG z^_aez$R);YFXu?F?_8@Un|<%i$S8T2oLv{UT5nb@6Q~Je>O=08FLw%ke6QxLEaXF1 zp6u^CWM7G$GHy!eN)$xq@w%&r2&r)18^vNP{3nw_p10K|(ti0C8Du(|(^Q6b+f>%7 zJBgB^cC(ds;+_U48>K54af>qc2qM=kq_9B&#ea@yvzh2y$0j5!Ded^KF-go?-7V2z zKa}WoSzV zbr&R*iV=43DJC)p>^>7*dtAh7{~$Pk6H*pj|Bl6X@<9Ugv3Q;A*cU0WVc!zUhIxMl zS3737?!<0n5}ICsXS<4`qc}~puiwt1Uoi9;)0NsihLtRO{0qRib~vx=mRaTtZnox} zk)(nQr!~snyVusp@sm8vfBmIE6bp#m{oK+3g4GD^db@~njNr~Ql-g#=r17z0T+`u` zqGmvEdQiU-Oj5DwouS~<7q()X3|=-1D4UNmF|An#Uu}ZWvo*Wf^G-dPT??wABqF{6 zHjcF{q~jaUd4myUOwj5j3{N+)ISURLy6?{z^bAg9p z8||kQ*9lSMuJ#5uUo`_vk*?>Fe_@U#u$b9~J-oo))Gp?Cr8oHCk0ZM_! zOt22|#kEHfTP7JoZ0&|Q5??=vCx&Re|4~`5hCza8k&zb#i=BhG&gdc(9J^rq)UUIw@S6%hELWbCOZ-qVL7 zflQqjh45RcEx1MJHPtWpTuq$XTEShAM6Kk;Bvhh?I$ikuCzsjZZwU%~?`K&_wk*(* z>UB7Y*5_C;=0$8Y=epEbUCC|=IPg#2Bza1~ePT@)A+Z3OH-*JAsqXs3$qlf5N;`?8D6g z?*{>8#V)Xr+fQzD%@bPKp@k82V70)EO{pS8{osbEC%cr-GPd>g1AS?+X7=9}K6|C12-6HE!G_< zAkjrrr`_#CN>dlygiBcfK?`!OSY+%$3>HfpC4z1f2a!eoREJ#tky@cotDjR3OVV|u zO~LtTG)GM$S^4)`AUjQ0-Vv6`!}Vq-dvj9$jPoLuTpe~yx&3g*IK;OonpGL|aJD$t zQNAT0ksH3QHt)v3UquDfFGEztwGiaCxe$ zr+gR0F%+A$X-&aOFOke5?W8^&pX_Gj;8VK!Ej+^oHq9X*ZHrr8Dj8N-r~f3F`T5mS ze407k%$T74C>4fNDh?U;ZmjDg!?k4eQ#^C&N+wQ}Nj=@Q9R1&F4a{JIBFq z&Vfn&j5ipO(M3OpGc`*XG0i389DiyEtiEY=@e6cpNpC6dnW^~KL%d^d7$pONpf+_wR8Q2zRs^UKr*!=AyM OUzI0Q>0I_x%Krg@o+j`B literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..32a4008489d85d665641ba9d41b066ca358258f5 GIT binary patch literal 3472 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 LGL^w;JEim=n)4oX literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dbcf40f5378d71994c577b57de2d6067d96e9598 GIT binary patch literal 3472 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 Kr1CpVsQm|DNhFj2 literal 0 HcmV?d00001 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 b778dde64514a038921db0830e2756efd76faa10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 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 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 41689821365f495e29bff929383daa86773df813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbW1dpr|r7{}S7&KjE?*HkVY*Qnt$8gktjvzTqJoyv7sGA!CBv$G=PmZRLN>EyUX zjK&$$g^;C=XdID{lw6ky$)R)poj>%^```DE_jx|w-}Al?9O3BdheA=zDQ+GZUn~Xb zh^I%nI>bgqpCuvf2|msiNGDem4(l2iMmGs?^9luMuB+*NaY?cYtii$1B5HnT+~+4# zFc&i7rC;3fiOPpvK=^iRYD#T=q~Qn@0vB)Y9q1kJ8)`zpAditkyu2`xp-6%&0pp6q zh6m9|W~f*+-h>uz9zqKIUPS;L;pFKQ?M4rdLy#?y_z<5c8af;ii#ZqRn*%e%s<&08A30t-h^m>J%BI z&Y#9f8RDz$>Kxew&%?Ql`?C06A;&tC{v?rvir5#$B)1t>7?qnmU@*L z7<%2So9YJCvpI~u{92q!5SgGX`95q%p{haV!h)+VH}x|+5(gQOwU&7EmH|-C#K?3e zRDTVbjv_?`w8eU29u!wFw?e)GHPw7_vSb<8k4{jU;|U&X8?J zn*0yU5*S^*#x}G`BSiSgF$*~U)@Hd;(yb(M_3%6Jd#GKv)=%IdX8{NE+0bi^$EZ3T^w4{ZX+vPv^$!;+@;bx7+W9=!xCZR4n}>TF%g{ZMS|w|J)SeN zUd8(<3JzYfS@8Im!IGPlIe}}B4lQpmu(kAId1OgTYD&_ciS(F%P^p#=df(ngk_7$? zUw1m?mUdf?k$#D9@8tYZ>VZ}7Pg#-UE7bZ{)YG9tnT?eRKiB)crUy0RJ6I4wC2{RE zZZ$$%#)-?nTzB3yp@|!A1u`b43qw#}{ylutE)%`uxs@1{QqFuvfd!E*`4+lpR;$Y9xYHy@xDe zcAONfrRu26?7Bd`Sb9`+QIJX8Mf+rDrXYph(*FnklEw=5+uC9oPW|0Z{V3k^n#=hl zEdS<>042O-d3YQv{!R1z8J*!JVSh;60g!q1U=StFD6D(YncNcEbx4dM2VLN4Jra*T z-+M!4E|S<)A{rrrhKt$`FOAEuPo+&HDs}R)Hyz>dXD8AD_n?jNR;p(|D$mM$)j$l4Kkm8>TZuJC{_iihUMJ;29RDtTbzqKp~a zVDM_a^p4%$J?QRM&H#gKCR*N^U)Tsgom}!#_jVRSZAQ`Wg^^{_YqfK<)XXi?Tv9mJ(KMJmxV%; zdj=TRZb0Qf%p>sgGCGEkkrCDIV0C6U^a%QvRr;iw0^ICKev2R=*du$MF&S$jQD6|GoM|FVWu9|a{8pVi&kk&D|DQa<5TW4o0Md4RDIRml;I}qS7zkS$4?;n#kICd zFJ(Nc_IQ~;uW4$0V=~`S1U$bNgf0I3)a2hG$n@%=KvE!A+|2F-T#gtmhL7-!D5}l2 qUzm0Z+~l!^v?FP`gUTHn{x*6YhQsVJ=q+Qfd#AzxBJWKdCGkHKJ{;o! 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 c4741402ac623e61ceea3d8dce5dd191737d2ac2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbVMYdF&j7|&)&0}fo8fHu`i@#kA%Qj;pxnGw|lQs{9QA8Bwa&&5Pns{=* z6y+9SNbY5plv3kSibB!OXMND)yx)Ew-plXuyoN|S8vq~#kz7q#_;5PaAx~)-e-Sx!-g7|rZGwhhE!pAwRNVwoG=#<6Vzz-KKfNKqDi? zUE@dKsn+{$=Pk6_qC{}lhG$=fx6hd!FJlpm6*o;B1{#y?PM9V|DdBJEin#^Bgk^)V zTd_dXn@s<@><1 z6P!;UhKj^ssw>ln>MJK6OIw;+@>pOSP@LFJ?RD@K73=p!GD@BQ{GZ|Ij^&Fj78np#C9Ud0WyFC&1Qz zOHFL5%(-B~Yvx3gDKfR~_TF8?nDJ0!)#JW6`??TY4yNN36F7P@)aTLo66i?Go3}iA zAe_G3ouupr`}msSvg2UH$#|Q?Bfe{)QtLOaM~yj48S*rl=Rf97EMG?@+>kRK(e#5T zeT(!hB$bmtBm-V|Beo-)-|u&ra}&tJR8A+&%)e6Dwrhs_inzVr>%49C)RnPW90F`X zYVHp|bO7G2J{f- z?o*ia`*f!?p*FSbj8=0Yp}7>cHaLhlW3{{YY9~zcDKV`>tDr8C-(w(=d!T$ESo`%6 z9&$rT@6;6W0yU_Pl=eK4AhPXnVu|3>wa(JFnS&>?FLvh172(*dj8RrEG&ks~SlFRl zL(!{r6em~&8HC@H)}7GR>{IGh9mQb~tMdb+0b>&(jFY7cS?)D5NJZQ8f6Y^aiInvj z`UP$Nmw``XAR;lH{|;A!D%LZz!YEqZnAZN-?X^5bFJ(kmLXFNjp)AVIe8*yofw@#m zTH2HMy*p?Zp5`b567n+J-gA2%)HP18&#&K23-)rNc6Kf48bdDmz{^RM_8lZKWM_W5 z+2SFPB8_=_IdNx&7bi<4v8e@?B?>VsDgQglqfxbQLrW*z)%4mlT%@)-Q%zKD$dN5w zE6}W}AKQk=yy*OAUa)(n9TC~(N`((lfTya?(9Tm~*^iX`WZM=NC2t64yV+9kJiB7Q znZ?=KEzi1hyAotFOtA3$EH0Nj@N>A=ckcS#UB8I99(J{|l^3UqN`*}nkg6xObsv2R z`olZws~a@_U`{i&AcnNq8Rlg?ztUYnG+92! zs{Tw-pP*QChsugooYf;XSn67!g% zl{|xU*^TO_vZ(2!U4vitzr#tP8A5%!wcAswE3NjN_I|(2^>^&5VsGY$t#NPlyBwPg zZhli!TW`2;=PecqRQ(FqyN{pS@lSZ=(ZVd*`J8{<&)>h6w$%2^p=afBckR{{9VRC( z^Xzo=xjpp$AC+L&<5M4}vBZrn2j2Q=zB=h?-q);Tuex-7&`#ZegUb>Z{65of zKO1Dq*nZCe6W)c&sH&J;N#R9Xn*ZjX#!jpD! z=k7>Bj@Iv$sZO2@TWY)6>oU9^7TnusX4-vtk-XFu*V|b}A~8wsp4&2=DqWA=FK~9q z$x7UF`#Y0W&7&s^BGdFEf1GL+3|D;jFgt`-voNR8QSWNf-z-K{8&P?Cr+@F~iD19} z`)%BhsTNIhxQ(tVB(n;9D@j@$xIXAbhjaXjLN4Qg=c?Xs!%y-k3Kg}!`f#6*L+VxY zeLt?9@%2udA5XaX+TEDhI&lAmd~FT=BLdOQHr;zq+L}c#)Un?nvHcHT`$^AB%%LY| z{0xt1SS71v0!(n5BaHp-v+1y^|NQ4%n>PK~?byzO=?6DVec-vSv*KjMH=gX>KR$ch z_U8>%ymLCqYx&9f%Hm5@iv8|+Ha|G&^ODKideycs=e{@bE%v_5dO3qH{Lb8q&24EC zw^{AQ6BcyX{r`E~UFgdGlN`}kHWgPb)7j+~Y8Hz(O0WB0yUTWKnZZ1kuA@6{w103v ze8++>LF@JIGwzpq@>eEE+y6TKkujpvqWH|q*}doZ-X_0ay0&R+_qtN0{foW!uT}he z>5_4&t@N3R#})s-Z|OOvv~XL?veUZ%xpxR^WGFwhT40JdNSAI|z93B|)F=4J%sVGb z4(qk7I{ALe@wHDTU5cC5etahPa`%ej{%gf&glD8re0l4b;Fe&mjXp1%tkZS#*RZJH zQ&%`Aa7N^syQ}e^4;Qbc-n^HYowN4yTJ{}KoO~zKeAW6{Wzl;@&f$&Hdr{(Ahh;(-*#bgML>z-k4xIQY zJbBY^j>|y{s%D>$S6pLutZ>?TfyFy4oMT1QE{o>nPTM1p%z60pm;aBv%1>1=>d$mp znf&U@>#YpuPt7>d#NKb>rayafb93{>e^<^Vy`8#33M~*(xhD=*2!46IeC@4$!Av5t zp$AnWzYG6PDEk^N+^D%_)4{Ti7gZu@cVZd+ZkDqe$G^|swm{8y)nfzekDW4WH;D?I sVlCrm-P_Zt-*Z;2!`^|vXno_0&1H?Z3TD{%%?>TUAba@Xf4oTw0Des#6951J 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 fbd5288bd15c5c056037ad5e41a2544a6c972264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbVMYdq5n7-m{-xsH_7FD%b6G-@Hsyp-#6igAaB5P-FPGd- zl-pUEG0L@Ea>*r^qZEadozME9pYwiuKD^KSd*0`HUp*rL>uPOHGp6AjF%&zRDS&4N zW3A|cp_fRemP9fdVQPc5wztE2`7`xBan4kbDpMEIzJDqLdw=SXDM6l_*C`~$RnEw* zj?Ng+m$A;mnZ|s02`_UpW zu5bciiU+KS{`Qz(F@p4rYzQ`?IHpgyksrbo?@JC2Lj@SoF^nKG!Jg>^=+h`pA$};= z5H#KrLo^_we#I!VbJhCXZ8$JO`JbC0^&T@;D$q3B3{^jRBiABVjGCI~kX2-&zph5BWm>GPyEGAEVj+O6^ zUDF-B8woVNOY`Bxu`dHIJNBDfA-Z{r2SqqW$(Dd|iwtMSr zRzj0uam971%vt8bPN}F`JM)rlV-pP%Qi0KnzOVc!DOu4zId7fRv z|MBv|NgbFZ=D6U63gA%T47YF>ff#u0g4fkI1C^;W+I5dd|Jh}ZxSk-cWB&Op^zfbL zn^QPBUJxJ}9pt+_9|c^+d`=dJ?|W0-yy&1g&$C4&v(%7BACwd7bRAm?J=q)l6mz@H z0kz1q=E?OcTUpm4?uZCe{)#YDA^g#f3{2YNmmUIm@F z`fiF%p~5I@?QwE0&<_(dCn?R)^U;>a`@Mby3U8gb6*Xk7rtBe$zTQ|oyLQ_oCQs6! zU)38d`!&p~0AEg+jR!m)hDyL4-ygMGbm8rR%3q9|UmBAZQ=VsyIY`ubT(hd0OdVW6 z!$r*SO|OErHDJ$W-B{*{4Ll2Dw|pIB#lFA4Z##eIw2H8Oj_KRFUBh zW4m?0>g$_8BsFVh*gYX6tEJ-hxNTVigU9%~hxT-B88duZiUb|$@WK}H?jBaUe}wKZ zYReku;pRYgW+k;NU8^zD1$MpQUVmwqk-xfG*Qxu4!APZZ?D&&{buL=IuZbt-)>lc7 zDAcL9AsM@GYw7FL18;W^73$sDb@RoOH#e5hoDmc0b6N{e`RJac;No)!kl#8!36m2{ z=={0dKf0tBy+1xEjj2g2yQJ1sfNd&;{^;p}U$QvBzr}?LKE@`ss^! z_#d7)!8Y1OYgOMD*v>^t+1_yA-6JFR65j11SzawAsgqyzqml6HLELI!sBNn6sRNa!VZI%!Eld{v|9Et`;tjLvrh>srIm`Z?ci`3 na6?i8EUEKYJo1#x#q#Lcs_^KckZfdfF6r{CgPaM$wvqY|y!08- 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 d7d2ebd1f8853fda06a9c3f9473f9ce4acc79936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbW1Yd8}M7{{}@gyA`3(x~PVayjg5ZgZWDU65&GQz>U`R!b5!ZOtLNm?4)aB)Jz! z=^0&+W4UcLp__V?%ah|)Zbep{^Hm@8IPbUrhxh+~e$RWcwZoz@7-B>~kbMk3Dmc)G zNx^v?iVP3;j$^>>cN1w5Y%YE;Vh_eC0OLt!`QZVuCQ0aX-JHz?Vt*Q!oYh7y!i8*Z zs?K{rznRZWa8S1a+S*}=d$CxyJ=>Rr5B6a@VFFG?5j|rLN1q6ELX&AY2PbbL*3XAX zXPtuo=^Mm^ZCbw+Z0)diejIx$oyrMj!pV52-GPjtAbcd#iA*HpiB3L;!eRpv*ch(> zc4FR`AlGZ5XNHt3=-&T9ek(^aB?Jr2j&KNb+DNDp= z@f+qg1R`Ek@vA`tD0&`vw#xQQ0Z?U0Sn63gPf^cU{*qypzF}}fph)64_C!QNx4rdj zj>zJ5Lrw~KFQYT_Wd6y{qc$PX4O?cU-TtORQCZY2S%UD9tMq`-tm0jZuJh&TC0z~O z7SDYBQ~_2IXD*7{!UKu!#-BU%*6v4Y^INkJet#iqyzN$?`e9ZHOcJ2Ntkx@|)K*$< z`tv9gK5K#ayIN{Ccez=a)M%S$aZQ#Z zP@m%e&n#&Ot)&rfNKt@ii|td!PIMVFHY-x7pkgP?gf|DWBf=Y zS$lMUyeL>rLh7Q&cXI_sizCO#cWn=U%%fk?zD9ZTS!uyEs;+lA-FpGL<)%5%n)o9X zN!5~do2E$s77b!v{w_Tu{*j&(DPaHj%;m|yDG7@TYcbcVJjjZFSKbo`@D)p0ocw0@ z@i5YG0&d2zpfmf#=8DfY;LB1Ro~w2;<=+vgoI=Y!+M&uZ1p2`81~WCFv- zvCV^&$P~h8|M39bheMz~r7?{MkeAI>-11X@=p#XE1AkJ%@X{QY%MQXFLdyeRxi^wE zCpa!;ebAaaqKVh~jj|4R3zDF{WXk?zJ!%vgn`d^$jT(g)nD#Yl)Ntg#%~F*lTGF^{8ZIU%$v_Xz6|zS zIFsi0P9jp1q`lHa!iTsaHTk9kIGp}_){P;A;^0L0)X?f=bD}eONprR*_%v9r{NU9a z#Nn?jgL@e()bX#5ggSxV=T`Jq>;7JYdH&-jj2cYBa5IaCzu;Hkbd&pu!Ufrnq>M6! zl%?dCxC6@F~&eIrgK#nt&20y$Op_dXtVz1o5x-zfG!uk8o_^KMjhjv{s$l$o? z9UI4t0{z+TOsPv+&enAHOeOk}yFas*=Pl{dY^@S0$-abMk)ht2Q% z{z5-DVOw3!eZ#*4P<_kRZhEcJR!ZgVGut(I00%_mAM%WPevRHefUM}w+$V3pn+ kZl~;2JZVoHyd`(Ev`Jjx?{YJHIza7bOiwsE+{pvR47Q|sQUMSoCxnqzJ>bKiiBR_XQZyX*D>*?6 zO3}xEwF-}pS`Ji%TA*+~Xf)lD?nc0RyV7k?9#Nqt~wMzlI-YI|N2>9O-fkNQ|J-jT#v7z3ct^p*BQ!vBd-#L;7vpkHW1k$6i zKJX(b8xItc7=*|E$Wd|q*!qBnLUDbl`Ot$Pk$lIaht|Z5qDgm7Dtg-BiD08xv@dqu z-1>^}&eE6%z#dy_4(L-U^m3l!rUfn!nLk6?oVNHSZGY;z!Sxk|VryBWkbr3TfUF73 zV7F*TaM)d8={ZvOgoDFo$HD8KQrw@Kl$Ts~YJQ6Vw4t!Bm@4FTV@5PhXMyThk>mKK z^!~(PH&MRY;~htvb5b2IJUtz%)e%g^3$ zGZJPCE7}rd>Qu|{3>jvP(Y5Pas2Z;X_3^y`>4 zeI!Pb%W%gz51r=&z;I3%DiEJ8PMRku+O=8_mkkjnt z6nw8(s47nBRyT(YL?18DHSED)bo+zK2H;A4V=WT{-^Ob$9V9NOe`@u<0NPe^;&vHs za4tx%Ds6>4I%n-7;B1?!M{GCkXw#dCse6f1g@|c~_Qk?FWA0&e5;}0)S?O|oTCqaX zLP8hDcVo-wnn-;!6xw^B^)z2^*=Zhp;}THhl9B~Ai@6q%_2<4sP+_{|($ z`d5<|hUr0$?=UG$)ffl>C!-Mc?0+RqT~BWdjA{utbe^S>nsXi@$6VT7SDG|}EaKVi z&(_TUiC<JGln=eC}dd@bG}*+k3pang(9Z&@=xgptu!5uIif$;1U6NKsIrR})1~ zWac;$5f-+O3-)7&?eB2!1p*e@9LIM!xcGhJ-NPA;6`2%6?L$>0gR^J69%w=M&aCKW iX;duXNF=N_dzkl9l;>`DKXWop4V*Xo?YqCQTK)&?9UUY9 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 04df936feb244e4812cc94a5cc7343e445a3d165..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbW1Yd8}M7{@c0qTy*njA~R8$=1+_m~A$bOdA`OI@4H%tJ)HyCc&A?GoZo zunRmB5aeosB2jVsE$j&xCr1K}5d#0?kefFE+U&ql0{v6tD-ZI#>OsB8(z&vCSZk@O z;Tr^@jG2LbkcF+)6S{8ol7X3F%$(Tf6dHx??hPm5Ee^ZV-Q4hg-WDVR2~V(aJnhAD zMPLH4E^yXqbGj?-SB`-H9Z_gB!N(yJ6Gh#Gf=ubEp{ z7V)|y>^88+l9mqoQUbe}CA)rspKq5vL*AJ9{cGZ$gf)Zf$_houk>^4ps^Ohotxqzy zO+A>$?FvXrmpDfs7&18yS@XQdgZfZ))sJ$;v`FRMT#RM%i#q?nB7#Q&W%gi{+59H%4<`w-cywQ@f$@7w$hSflS*{*2L`;rxTjaBit@dEU z>ED?p6`{H?Xs@2*V)qpGg1!^gLJdz7U;dk1M5`7WQV^5fiWLKnjZR%3e@2d&msyFqQl+aeJGZ7k-w(<#l5nJDZRfp+ z0};3>-K>_BpbbTzO$tX!4;Kh>(-1>QF7=p|LbG=E%yBjM@Rv1)tC zez6Wio#$ON9`yq|wciq8CzHgbJ1HvdFsQv$cO8VCy%_KG zUL;f!#doWi!uvyymuKqt;BeY~jM9FDT<=)hME{TR+Q1(h$a+%JGM3s2lAB@E0l zbRHxwQ%2{I4g#L$=X#Wuac8^EOjz9$v=UTAKe#I#{yMA(rxD#r;Ll2yq7n;aVi%&j zaGnZnqpKqI&QR%Zfyk+1oh9r%qPs~RKn)Ce&MnrEU2j50bI<~4J4@e?>d z0M}zTzQyQPf@;4vBo2+L=YA45cU9jI7(69j)2K-xx1`^-8*}JzTy9onm_>0rYF5qv zjbCOGwh+|o&|rBkHuapf+-ttdpK`EEA9)jy%@w(vqz7?@%fhE_+hc+s4pU;)9Lh)xQwZ1JHw`dEK5pwvRI@ zaQ28nb|Y4+H&Io7v{6CMfv+rKJ7h_{ee+X_g&jGTR1=ocDLrdUrd0ihyOLD{NPOj} z-`RtIg0%-)v-z)1ioX@C6R)Rdd0=%SiZ`vA@9US`em^wPfWsm%_QuP|Fsn$7l_RDE zG6^2Aix2VQ2JGo{eh&mJwA+qvv2pPFkzYhG7|Sy%fLZ%1iU(%Ty4_ZT7TdE!TO=Xj f#3LN|)3l-dCyiOoR%I!Z5z3IP*&jdsg_ZI@l93i` 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 1432a01522aa968d113857728a0b7cc44a1ff3af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 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 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 090e697fac72f6c3ee257fd44aba4d41d9f88059..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2192 zcmbW1YdjNb9LF=)PO@5r#5O5Kim}aD^>LXEA#F1f?QCW)4cU&_luIkJaiR-K6DdTw zRJ1Or9JxkRWbWHkiZh&wb)s|Lt{3&u^X~iN`TaiM&;R*9)^NA|Zb$ZVLgL_bB+8fO zAI-vq!KHytt%7b zYr_gh(1;YsiXR`9{}Y{DTs-K$eh4NmI)LaIO2A^m86hEV@iZHRw}(GHl8ZZFyWa)n z>*9mMsniw7z=aq)1-7#h2~v zyfJKqs4-=aHY>rTQ=3F`hm+&BJqK~lq>x42 zq9~^U%4@P2B(14SLK?Ny>p5ts{5}N3{qbz!s3C;8hgM%|YQf>tpUNHQ)MnN$$!)ok zDZq!jx<+-a&7jOhpQ326X4ZP;`;fxwC!bjZBdaRu9^Fo{e@)YVYx{y3_C)jP#NGJX}E#x?Azo$Tb2yLNqT27T~zU?fC= zaV0E38DG+aFmu9Q7XUSoae5E7O_nrTe6+)q*x`<4B}?a$HTn44dcbpGbGBz!l#=;m zH!WU@qhHZxMY{dM=u{k_x*yNfxV$%55VN(-whExEsZ2rMFTqg972h7ukm7Gc!sTAN z*_!KuXo=nSJOmpeDraqd3pwLZZE${;h))?cuXH@r#hfl9Wx$rwGSU+fb+atPV01Fm6a8n8=1yDLu zaUq59;OsF0M+PApSJMAl)pT(~VR%PPstgJP;d@L%yWJF%(sQ0>!J+0z>4j-we-34y zeE>!Mv-{=tVIwtXa^4LO?f_RcKueM}^A3wQttN+u(*`%+g z3Vm1Ci#4k!j^^bTPB5C^?}KsFi)z#@CvK)#m8Rz2+5qFHCfHWp`Bz^S&-|oUUC<}= z)Nc>rrg47nlqa4ytq=0*x>7w7gnoR}NWVCL4gQj$T`b22vx9{;H)$rKIiSo@wepUP zuN61jCxfz7lqHm@FztvOS1a2*oZU8aBDG_IdH=BJBLiT+eu6AHYZysAaoT<_%peuH zI?X4Bu|_lKVi*^^26zHoCIFA9U;GzPk<V# z=S>AjCqsYO|I8c_NCFNW&W0UpU)8>~wJK)pLJR*Xmg~}}9o{4R2cTDr7^glHNh8*Y zFO3wqznES#C442FIlfq6w8MP2L8qMZ%x)$5gaz;pR3$c}oqMD;D)ZPC5pczknXp7H$%`^C?8rZjRnaxMc6#LkeBv5kByiWsT ztc*pi|8kIyMg-0kmJ+MHVx>8oc~(`y2E8JUQwLk$EY;{rcB6l|rR7IugY3KQ^V{B= z$<($P+I5eC2S^pwcsS;9ZCX8blbXqbH?e%Bh}#NX)#zsW$Wj|8da~&da%!V{l3?MR z-|SoZ+_l47b*?|($>a4;ntp zGwb7r@vFOVtkcA4O3UKV5Pg3!RclsFR=L9_SNwElt@|7GrFU%|-%7RX#2Sk{BJqu` q@$usE8^uyhgL_=qaGcTX3pW~yNfI@JD~oEPOC0`2fB0^Q%J?6+KOc_( 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 0000000000000000000000000000000000000000..c2a187223630c9d9743b25d32388064b9970e1ef GIT binary patch literal 2192 zcmbW1SvVV57=|mc)H1{1u}y1jY8f;^qOp!Wf|5uOGgOg8>`N;`r-L>a)fQ!FV^6EG zuTlF>Et#5X8J^m=+H0vwQJuL>FZ40z?!7qQ|G(!y=YXN+CT32B&-pL6OK!7_U9T7;l4aw^|R@*$x0tLBF zg5j~A@@Gz7T>mAWuFG#F0>Ds9v}K4b#XB5I)PyUws_APFMpeZHqterNDU7}q+JrCn{8I&2y_jmTM=tzPvz))78GeN01Gy)eGIGPgF@I56f}hkHf4UL1s#GP=|wBIz8wx*%iD%7K97(;{{~Qc_-db z4zIp6qAPjK+OfHL-v^Oikt8_x%Y$cx3>yKd9&Ox_L+dVw=OKFg!OycwZimxDq}uoo zJpacm0h))fI~#7%%c=)Yg9@_raFl^$EkETRhK0Z2T7z|82v&H!zQ5a=Qg8CmW>Rhl z*6DF^;Ibc&)U_I}g+zQe5#?O8FvdvK@xL{?N|t;({2`@Uj%Heb5d~$+tB5?_){mW= zQ^;x6%_WHDYdeliC~n+j<``yIe0O>KhOS0j8@qZ1z^Tbe=bkt%aXq~mt~`MG152IR zatnr$mfAsI;n@YuOakvB_g+5aYB{OTy}z(!&!LrosEqGw6rpxw>-02x@;ZeqF>rO-~q(yUn{0pj~KdoNjd&^uQh-ZrVRIxW*3G znsB{vB&3$0=yRo;cme9ee9-@W(-j^Qt)35I3{9b+5iM)+whUN>`nKVSqL-L}+ z`j2G{_nqVGB}dbMNJSqo`O_x%rT%CZ6F5IDN+|~RF0o^$lfZd==6iR^n=Z7v&0m&5 z#CQp&M(vu;`^CpH@8XPv-XW%}kY8+|`h0_%capKK9Yr@-Pb#@M50R6E`F=K-@$Dxg zW5Lp1JDBA|(I-2CllND1QQ?YwGE+44g>kLeu#ihi)&UQLzW)*iy%^zFZxg>ss;}vK z9<6!t*Brw!Sj&{RWbIB0lYoYH4*i{9P9(viLWFH4ez<-3$TZ```dw`1AghOpoajml zm@|coVN?j8aqVB+8}weW+@p(nkFs5C)Ljo4QqI{LYTF5+U>$>92$<~~Iku5m{KznJ qTw24&HBq2W(I_grAYJJ=8qLGfqm9bs#RB}hP(<(44jiZOSM)F3OeBZ^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..69fc750debe4fefbf4a511e8e88634c2ecac620d GIT binary patch literal 2192 zcmXpobuslVHw#Y+FON(!4EC_}3r!6R^Tvkw?d!F zlrqzFbIZWgi2M@QOw(eIirk1G-^vhY!|ceAf^^rY0{1{ik6@!7y(PZG|qxUv4ed?LvnS7cgyH!v_@?ds$^O)UBTQ#7spljO2aYdT#{Sg~=v zirpHJD@jX3hu8MHv#jzxWUzn7npd}8&gOmcW|g7P@~alYBF-;FXa0$Kz<&O({r{Tc zs&bXvigs7a#8J}+duyQ8dWFDcb?wZVQ|fQp9$MgkrKwjfvwGzZzty*2-B-EnIqA>+ zIjW3(Pr8y8y*qeZ9#y}BhR58g!L?q?Ha`xUpucA8?$4@w=CAuE;T`^Ii%eGL!G&bR zC6nH7vzE894KFl*EzMsv$11Y&)C8k!^`k3XbGTpc^gK}*%71gm$s?YXI~*r_-<3aa zb|^vZltnhHMB-+~CtayW(gVX+KDn`?%Ot1n+N*N$+n2sf+$2BSX;Fxf>I?-9fmt7| zn;tzewxridE>jB(AOhPWUB-r5g)3?=th^ zXI%cnvMt4Ac6s(`IS;+fUo_V){Cn}K?xO|%>ONW7jkyvcp_8p9R-sym#%nc~TI9}j zqNXu+{%5uf4fF3-Eekex8$9`=Gkx-#Nm{ANzc}Jw-QJ+Fr6!_RGbN8z&w2J7>py!` zRnLgLGWxOd=36xjMV92iYd>4L4c7mb{^YL&Cq?bsM2snUKd{flb)BD zPJPpOEBn>c=jykfmpLC@DpjvYRTVKZl}wSTE;lHfyJ+i-G`6QKUl$t2ugg3UcXHX@ zuL1kgJT|l+-X+j_By^jV-q8g@MnAmQY@EsZPbaReaGz1wSDqV7*qB!x+ZbuoGyCVI z6QK&v7AW4oww1@k_kV%h{D&P*^+yYWE1%xkr5|c@-2Nn9FMWtx7g)jdd5v@1{EW1z z^F94jUoXD;I9>6;;_q|C@`M?lXifa~sQU5^eRY;}t__;dJ00h*<6&P;zj zui^4a&ijiyF10nYJ;^m*=3_tAy<4^4-?Gf<EMxx1 zpv9qM&?x&s_W99c=Mws+#($kxzlkHP{A1A8qJ{k19CubcHe66$eDa@^?f3BZ9pcxf zFncLw7?l5f$D)6-;mlU%$M=OQS1})Z_M_>3kHG?Vm#^LB?;>Vzdl&seHN9PItL~Q9 zChwoll6u}|$Mf9dp1aq7Z&Pdb)9+d)gzh`Ivh>W74UDLPh|1$r=f xRWog?Zc6HAM0Ig(Q?jd`yJ)KNzlI>zGgh;&NiAq%$~hdEo-*lRB&vtecmPbzCb<9r literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d4a1ea866e6f0d84261e64d598def496102538f5 GIT binary patch literal 2192 zcmbW1c{~$*9LL#ENze8m;-#3HE3{$L=E^;DZZ_9bO6~N@@9+Klet$n0!VYg2?!*X)Mo^4VIFeUr zlr0SriHQmJ!aFkv*6=`YLKwx?C(IsaiE%e@w*{!}g6PZS9?RMc4n}!0oE4nmqN4J6JWU>bGri2;lAr4(E3Fj{k6?M5yZx zZ4=ki2hQ|rTfIKTt*JU>UisfCw>d+GAO$iN?9374zE>3Ub(v+XkLfMD z97p&6k6r@we`MdafA+YIbmKgLcn0I&_5-K|>AQ?GB6v%j=FK zsxOdFPHFTU4V2vH#}l7SCACvcHaXpWi|_~xQX z;`lhEpxL;PEL&vg-ao89caC3RmS45!=;B#py`;y&+K&KHO;J)Q^_UhfI~T3lL-?D> z8e2FKhNMlmKFTg&pITW>IPU=x3lhI2L?s0n zvFU-m2Qd1)uiAHu)?ZEOjdNTzoz4U%1n-k4UPoszMGO8yEMw(jdqCH@gcF6!QdBMD z@90Y$-euV2nr`;>&zU+T|J9u*KfSHisypX(MLQrwQs0`LXR`JX3KhtIG7we_&1oEk zNciIhez11KtK8CBvo`9M7Fgz(`#m{&pNkoAZb<5u{@Z^`fSzw!bNg2uoch0|eGFQ( zVL;y~Gb6+SQ8!d#>9FYhVtx);YD!|I2!8S&t;@H&{LzYAGZ|Bz-1Y#>YA4pC^*cDS z213Wmnq0{ZUEieeW&@8EnZZ&i;O)*DZ-X@UkG?LV+B63;I>47Pa^4I(Q20K@>Ox1D zJ7a42!4abAQ;Y5`l@@&O;bNqhE3K0XspPCe2UpT;N&f@|(o*t0Um-+JY*HuV+j#8j z!6TXy6zOVh)m77s2+ZtQx#HnU&4;y366*qs@0l(Cpj7gB@1f;n!R-?c>qU(BU%^tz zPbcb`-~$Kiercj8D!OT({Vd3yEr@?zv1_y>r7!8C)MT(>xsyef{blG?S5&lB_lw?H zVYI=p&!IILofLJdOFQKdg35o|1zrsqlFVast+LrY?RRf3KM|6B zUo%ijvI1YPZr13_)J^3NN#-&iFz@WKn~n&R9li6Ht`VDCE6xf^tHnhxQItSCj+u^~ z>84{7VM_i>gqfeRrAxa<(mxgAqSbdOzhmPM4H+axh95cT5PUuKkIhJgMoeIXWAZ$$ zf!B6FUSH#Qf!QGP(YpY}x}^+08IO4K;$_i23Jn<>4sufX^Wx83tL!c7w+Z>Zf({mT zxGgPs+zKT}&?2vUw`}@0qF)Jo<10GX^1U2&eO9^qJoEK*7E>Z%MyAVfnD7j9a4zR| tt{HPkNssE2DqXK`9-CjBb#NmdFDXE?2b4L9fS@)UB_OlaTU7WL`WKhWACmw8 literal 0 HcmV?d00001 diff --git a/crates/threshold-signature-server/src/helpers/tests.rs b/crates/threshold-signature-server/src/helpers/tests.rs index b26f9a9ab..4f59f8e0c 100644 --- a/crates/threshold-signature-server/src/helpers/tests.rs +++ b/crates/threshold-signature-server/src/helpers/tests.rs @@ -226,12 +226,11 @@ pub async fn spawn_testing_validators( /// Add the pre-generated test keyshares to a kvdb pub async fn put_keyshares_in_db(validator_name: ValidatorName, kvdb: KvManager) { - let non_signer_name = ValidatorName::Dave; 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 )); std::fs::read(file_path).unwrap() }; From d9f2d9a1f1876e17dab2aa75ea9e0f3cd211bd5f Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 09:59:38 +0100 Subject: [PATCH 25/28] Update script to generate keyshares --- scripts/create-test-keyshares/src/main.rs | 48 +++++++++-------------- 1 file changed, 18 insertions(+), 30 deletions(-) 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); From 476e3274b7d8bc19b84b0a155a30d5b6cb6d2357 Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 10:00:46 +0100 Subject: [PATCH 26/28] Update script to generate keyshares readme --- scripts/create-test-keyshares/README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) 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. From 6b66e65010b40a5688ccb115f69f5d5ea353ec8f Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 10:12:04 +0100 Subject: [PATCH 27/28] Tidy --- crates/threshold-signature-server/src/unsafe/api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/threshold-signature-server/src/unsafe/api.rs b/crates/threshold-signature-server/src/unsafe/api.rs index fe30d19de..350e962cb 100644 --- a/crates/threshold-signature-server/src/unsafe/api.rs +++ b/crates/threshold-signature-server/src/unsafe/api.rs @@ -80,7 +80,6 @@ pub async fn unsafe_get( )] pub async fn put(State(app_state): State, Json(key): Json) -> StatusCode { tracing::trace!("Attempting to write value {:?} to database", &key.value); - println!("WRITING VALUE {:?}", key.value); match app_state.kv_store.kv().exists(&key.key.to_owned()).await { Ok(v) => { if v { From 6f91f3f9e9d83cb40d856a67bcbdb4f0a303584e Mon Sep 17 00:00:00 2001 From: peg Date: Tue, 26 Nov 2024 12:06:05 +0100 Subject: [PATCH 28/28] Undo testnet reshare fix --- pallets/propagation/src/lib.rs | 2 +- pallets/staking/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/staking/src/lib.rs b/pallets/staking/src/lib.rs index 7bc616b0b..4f7f854de 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -793,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, };