From e7f5b6eb0aaa531b1d961b962cb7264ae89afd9c Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:08:22 -0400 Subject: [PATCH] Add new registration flow test to TSS side (#997) * Move jumpstart helper to user test module I think this makes sense since we're going to have a few test flows in there that utilize it. * Update registration query to check old and new paths * Add new helper for registering using the new flow * Add test for the new registration flow * Remove `RegisteredOnChain` struct from genesis The verifying key that's being used in there is wrong (we need to derive it), and it's easier to go through the registration flow ourselves due to the prereq that the network has been jumpstarted. * Appease Clippy --- .../src/helpers/substrate.rs | 27 +++- .../src/user/mod.rs | 2 +- .../src/user/tests.rs | 150 ++++++++++++++++++ .../src/validator/tests.rs | 41 +---- pallets/registry/src/lib.rs | 9 -- 5 files changed, 177 insertions(+), 52 deletions(-) diff --git a/crates/threshold-signature-server/src/helpers/substrate.rs b/crates/threshold-signature-server/src/helpers/substrate.rs index 5b036275c..6f5c05499 100644 --- a/crates/threshold-signature-server/src/helpers/substrate.rs +++ b/crates/threshold-signature-server/src/helpers/substrate.rs @@ -62,16 +62,33 @@ pub async fn get_program( } /// Returns a registered user's key visibility +#[tracing::instrument(skip_all, fields(verifying_key))] pub async fn get_registered_details( api: &OnlineClient, rpc: &LegacyRpcMethods, verifying_key: Vec, ) -> Result { - let registered_info_query = entropy::storage().registry().registered(BoundedVec(verifying_key)); - let result = query_chain(api, rpc, registered_info_query, None) - .await? - .ok_or_else(|| UserErr::ChainFetch("Not Registering error: Register Onchain first"))?; - Ok(result) + tracing::info!("Querying chain for registration info."); + + let registered_info_query = + entropy::storage().registry().registered(BoundedVec(verifying_key.clone())); + let registered_result = query_chain(api, rpc, registered_info_query, None).await?; + + let registration_info = if let Some(old_registration_info) = registered_result { + old_registration_info + } else { + // We failed with the old registration path, let's try the new one + tracing::warn!("Didn't find user in old `Registered` struct, trying new one"); + + let registered_info_query = + entropy::storage().registry().registered_on_chain(BoundedVec(verifying_key)); + + query_chain(api, rpc, registered_info_query, None) + .await? + .ok_or_else(|| UserErr::ChainFetch("Not Registering error: Register Onchain first"))? + }; + + Ok(registration_info) } /// Takes Stash keys and returns validator info from chain diff --git a/crates/threshold-signature-server/src/user/mod.rs b/crates/threshold-signature-server/src/user/mod.rs index 597aaaf60..28497ced8 100644 --- a/crates/threshold-signature-server/src/user/mod.rs +++ b/crates/threshold-signature-server/src/user/mod.rs @@ -31,7 +31,7 @@ use subxt::ext::sp_runtime::AccountId32; pub use self::errors::*; #[cfg(test)] -mod tests; +pub(crate) mod tests; /// User input, contains key (substrate key) and value (entropy shard) #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 2f30a0cdc..71c2fa434 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -88,6 +88,7 @@ use subxt::{ Config, OnlineClient, }; use subxt_signer::ecdsa::PublicKey as EcdsaPublicKey; +use synedrion::{ecdsa::VerifyingKey as SynedrionVerifyingKey, DeriveChildKey}; use synedrion::{ k256::ecdsa::{RecoveryId, Signature as k256Signature, VerifyingKey}, AuxInfo, ThresholdKeyShare, @@ -828,6 +829,32 @@ pub async fn put_register_request_on_chain( submit_transaction(api, rpc, &sig_req_account, ®istering_tx, None).await.unwrap(); } +/// Registers an account on-chain using the new registration flow. +pub async fn put_new_register_request_on_chain( + api: &OnlineClient, + rpc: &LegacyRpcMethods, + signature_request_account: &Sr25519Keyring, + program_modification_account: subxtAccountId32, + program_instance: BoundedVec, +) -> Result +{ + let signature_request_account = + PairSigner::::new(signature_request_account.pair()); + + let registering_tx = + entropy::tx().registry().register_on_chain(program_modification_account, program_instance); + + let events = + submit_transaction(api, rpc, &signature_request_account, ®istering_tx, None).await?; + + // Since we're only submitting one request above, looking for the first event as opposed to + // say, all events, should be fine. + let registered_event = + events.find_first::()?.unwrap(); + + Ok(registered_event) +} + pub async fn put_jumpstart_request_on_chain( api: &OnlineClient, rpc: &LegacyRpcMethods, @@ -1297,6 +1324,107 @@ async fn test_faucet() { clean_tests(); } +#[tokio::test] +#[serial] +async fn test_new_registration_flow() { + initialize_test_logger().await; + clean_tests(); + + let alice = AccountKeyring::Alice; + let bob = AccountKeyring::Bob; + let charlie = AccountKeyring::Charlie; + + let add_parent_key_to_kvdb = true; + let (_validator_ips, _validator_ids) = spawn_testing_validators(add_parent_key_to_kvdb).await; + + // Here we need to use `--chain=integration-tests` 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; + let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); + let rpc = get_rpc(&substrate_context.ws_url).await.unwrap(); + + // We first need to jump start the network and grab the resulting network wide verifying key + // for later + jump_start_network(&entropy_api, &rpc).await; + + let 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(); + + let network_verifying_key = jump_start_progress.verifying_key.unwrap().0; + + // We need to store a program in order to be able to register succesfully + let program_hash = store_program( + &entropy_api, + &rpc, + &bob.pair(), // This is our program deployer + TEST_PROGRAM_WASM_BYTECODE.to_owned(), + vec![], + vec![], + vec![], + ) + .await + .unwrap(); + + let registration_request = put_new_register_request_on_chain( + &entropy_api, + &rpc, + &alice, // This is our signature request account + charlie.to_account_id().into(), // This is our program modification account + BoundedVec(vec![ProgramInstance { program_pointer: program_hash, program_config: vec![] }]), + ) + .await; + + assert!( + matches!(registration_request, Ok(_)), + "We expect our registration request to succeed." + ); + + let entropy::registry::events::AccountRegistered( + _actual_signature_request_account, + actual_verifying_key, + ) = registration_request.unwrap(); + + // This is slightly more convenient to work with later one + let actual_verifying_key = actual_verifying_key.0; + + // Next we want to check that the info that's on-chain is what we actually expect + let registered_info = crate::helpers::substrate::get_registered_details( + &entropy_api, + &rpc, + actual_verifying_key.to_vec(), + ) + .await; + + assert!( + matches!(registered_info, Ok(_)), + "We expect that the verifying key we got back matches registration entry in storage." + ); + + assert_eq!( + registered_info.unwrap().program_modification_account, + charlie.to_account_id().into() + ); + + // Next, let's check that the child verifying key matches + let network_verifying_key = + SynedrionVerifyingKey::try_from(network_verifying_key.as_slice()).unwrap(); + + // We hardcode the derivation path here since we know that there's only been one registration + // request (ours). + let derivation_path = "m/0/0".parse().unwrap(); + let expected_verifying_key = + network_verifying_key.derive_verifying_key_bip32(&derivation_path).unwrap(); + let expected_verifying_key = expected_verifying_key.to_encoded_point(true).as_bytes().to_vec(); + + assert_eq!( + expected_verifying_key, actual_verifying_key, + "The derived child key doesn't match our registered verifying key." + ); + + clean_tests(); +} #[tokio::test] #[serial] async fn test_mutiple_confirm_done() { @@ -1471,3 +1599,25 @@ pub async fn get_sign_tx_data( (validators_info, generic_msg, validator_ips_and_keys) } + +pub async fn jump_start_network( + api: &OnlineClient, + rpc: &LegacyRpcMethods, +) { + let alice = AccountKeyring::Alice; + let signer = PairSigner::::new(alice.clone().into()); + + 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::Bob, ValidatorName::Charlie, ValidatorName::Dave]; + for validator_name in validators_names { + 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())); + + submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None).await.unwrap(); + } +} diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 23b64fc25..83ca9edaa 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -14,20 +14,13 @@ // along with this program. If not, see . use super::api::{check_balance_for_fees, check_forbidden_key}; use crate::{ - chain_api::{ - entropy::{self, runtime_types::bounded_collections::bounded_vec}, - get_api, get_rpc, EntropyConfig, - }, + chain_api::{get_api, get_rpc}, helpers::{ - launch::{ - development_mnemonic, ValidatorName, FORBIDDEN_KEYS, LATEST_BLOCK_NUMBER_RESHARE, - }, - substrate::submit_transaction, + launch::{FORBIDDEN_KEYS, LATEST_BLOCK_NUMBER_RESHARE}, tests::{ initialize_test_logger, run_to_block, setup_client, spawn_testing_validators, unsafe_get, }, - validator::get_signer_and_x25519_secret_from_mnemonic, }, validator::{ api::{prune_old_holders, validate_new_reshare}, @@ -40,8 +33,7 @@ use entropy_kvdb::{ }; use entropy_protocol::KeyShareWithAuxInfo; use entropy_shared::{ - OcwMessageReshare, EVE_VERIFYING_KEY, MIN_BALANCE, NETWORK_PARENT_KEY, - TEST_RESHARE_BLOCK_NUMBER, + OcwMessageReshare, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, }; use entropy_testing_utils::{ constants::{ALICE_STASH_ADDRESS, RANDOM_ACCOUNT}, @@ -52,9 +44,6 @@ use futures::future::join_all; use parity_scale_codec::Encode; use serial_test::serial; use sp_keyring::AccountKeyring; -use subxt::{ - backend::legacy::LegacyRpcMethods, ext::sp_core::sr25519, tx::PairSigner, OnlineClient, -}; #[tokio::test] #[serial] @@ -76,7 +65,7 @@ async fn test_reshare() { key_shares_before.push(unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), *port).await); } - setup_for_reshare(&api, &rpc).await; + crate::user::tests::jump_start_network(&api, &rpc).await; let block_number = TEST_RESHARE_BLOCK_NUMBER; let onchain_reshare_request = @@ -195,28 +184,6 @@ async fn test_empty_next_signer() { clean_tests(); } -async fn setup_for_reshare( - api: &OnlineClient, - rpc: &LegacyRpcMethods, -) { - let alice = AccountKeyring::Alice; - let signer = PairSigner::::new(alice.clone().into()); - - 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::Bob, ValidatorName::Charlie, ValidatorName::Dave]; - for validator_name in validators_names { - 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(bounded_vec::BoundedVec(EVE_VERIFYING_KEY.to_vec())); - - submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None).await.unwrap(); - } -} #[tokio::test] #[should_panic = "Account does not exist, add balance"] async fn test_check_balance_for_fees() { diff --git a/pallets/registry/src/lib.rs b/pallets/registry/src/lib.rs index f38a29aac..31640dd95 100644 --- a/pallets/registry/src/lib.rs +++ b/pallets/registry/src/lib.rs @@ -143,15 +143,6 @@ pub mod pallet { version_number: T::KeyVersionNumber::get(), }, ); - - RegisteredOnChain::::insert( - verifying_key.clone(), - RegisteredInfo { - programs_data: BoundedVec::default(), - program_modification_account: account_id.clone(), - version_number: T::KeyVersionNumber::get(), - }, - ); } } }