diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index 6c0060643..e4a096a42 100644 Binary files a/crates/client/entropy_metadata.scale and b/crates/client/entropy_metadata.scale differ diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index 338cc85e0..5c0f3c0d1 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -65,15 +65,12 @@ pub async fn new_reshare( let api = get_api(&app_state.configuration.endpoint).await?; let rpc = get_rpc(&app_state.configuration.endpoint).await?; validate_new_reshare(&api, &rpc, &data, &app_state.kv_store).await?; - let signers_query = entropy::storage().staking_extension().signers(); - let signers = query_chain(&api, &rpc, signers_query, None) - .await? - .ok_or_else(|| ValidatorErr::ChainFetch("Error getting signers"))?; - let next_signers_query = entropy::storage().staking_extension().signers(); + 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"))?; + .ok_or_else(|| ValidatorErr::ChainFetch("Error getting next signers"))? + .next_signers; let validators_info = get_validators_info(&api, &rpc, next_signers) .await @@ -84,9 +81,11 @@ pub async fn new_reshare( .map_err(|e| ValidatorErr::UserError(e.to_string()))?; let verifying_key_query = entropy::storage().staking_extension().jump_start_progress(); - let verifying_key = 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"))? + .ok_or_else(|| ValidatorErr::ChainFetch("Parent verifying key error"))?; + + let verifying_key = parent_key_details .verifying_key .ok_or_else(|| ValidatorErr::OptionUnwrapError("Failed to get verifying key".to_string()))? .0; @@ -106,10 +105,11 @@ pub async fn new_reshare( if !is_proper_signer { return Ok(StatusCode::MISDIRECTED_REQUEST); } - // get old key if have it + let my_stash_address = get_stash_address(&api, &rpc, signer.account_id()) .await .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let old_holder: Option> = if data.new_signer == my_stash_address.encode() { None @@ -120,19 +120,19 @@ pub async fn new_reshare( .ok_or_else(|| ValidatorErr::KvDeserialize("Failed to load KeyShare".into()))?; Some(OldHolder { key_share: key_share.0 }) }; + let party_ids: BTreeSet = validators_info.iter().cloned().map(|x| PartyId::new(x.tss_account)).collect(); - let old_holders_info = get_validators_info(&api, &rpc, signers) - .await - .map_err(|e| ValidatorErr::UserError(e.to_string()))?; + let pruned_old_holders = + prune_old_holders(&api, &rpc, data.new_signer, validators_info.clone()).await?; + let old_holders: BTreeSet = - old_holders_info.iter().cloned().map(|x| PartyId::new(x.tss_account)).collect(); + pruned_old_holders.into_iter().map(|x| PartyId::new(x.tss_account)).collect(); let new_holder = NewHolder { verifying_key: decoded_verifying_key, - // TODO: get from chain see #941 - old_threshold: party_ids.len(), + old_threshold: parent_key_details.parent_key_threshold as usize, old_holders, }; let key_info_query = entropy::storage().parameters().signers_info(); @@ -316,3 +316,26 @@ pub fn check_forbidden_key(key: &str) -> Result<(), ValidatorErr> { } Ok(()) } + +/// Filters out new signer from next signers to get old holders +pub async fn prune_old_holders( + api: &OnlineClient, + rpc: &LegacyRpcMethods, + new_signer: Vec, + validators_info: Vec, +) -> Result, ValidatorErr> { + Ok(if !new_signer.is_empty() { + let address_slice: &[u8; 32] = &new_signer.clone().try_into().unwrap(); + let new_signer_address = AccountId32(*address_slice); + let new_signer_info = &get_validators_info(api, rpc, vec![new_signer_address]) + .await + .map_err(|e| ValidatorErr::UserError(e.to_string()))?[0]; + validators_info + .iter() + .filter(|x| x.tss_account != new_signer_info.tss_account) + .cloned() + .collect() + } else { + validators_info.clone() + }) +} diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 0c9804c55..976e739ce 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -29,7 +29,10 @@ use crate::{ }, validator::get_signer_and_x25519_secret_from_mnemonic, }, - validator::{api::validate_new_reshare, errors::ValidatorErr}, + validator::{ + api::{prune_old_holders, validate_new_reshare}, + errors::ValidatorErr, + }, }; use entropy_kvdb::clean_tests; use entropy_shared::{ @@ -55,7 +58,7 @@ async fn test_reshare() { initialize_test_logger().await; clean_tests(); - let alice = AccountKeyring::Alice; + let alice = AccountKeyring::AliceStash; let cxt = test_node_process_testing_state(true).await; let (_validator_ips, _validator_ids) = spawn_testing_validators(true).await; @@ -163,6 +166,21 @@ async fn test_reshare_validation_fail_not_in_reshare() { clean_tests(); } +#[tokio::test] +#[serial] +async fn test_empty_next_signer() { + initialize_test_logger().await; + clean_tests(); + + let cxt = test_context_stationary().await; + let api = get_api(&cxt.node_proc.ws_url).await.unwrap(); + let rpc = get_rpc(&cxt.node_proc.ws_url).await.unwrap(); + + assert!(prune_old_holders(&api, &rpc, vec![], vec![]).await.is_ok()); + + clean_tests(); +} + async fn setup_for_reshare( api: &OnlineClient, rpc: &LegacyRpcMethods, @@ -173,7 +191,7 @@ async fn setup_for_reshare( 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]; + 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) = diff --git a/node/cli/src/chain_spec/integration_tests.rs b/node/cli/src/chain_spec/integration_tests.rs index cad55b3d9..bd901dc72 100644 --- a/node/cli/src/chain_spec/integration_tests.rs +++ b/node/cli/src/chain_spec/integration_tests.rs @@ -53,9 +53,15 @@ pub fn integration_tests_config() -> ChainSpec { 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::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + ], )) .build() } @@ -72,6 +78,7 @@ pub fn integration_tests_genesis_config( )>, initial_nominators: Vec, root_key: AccountId, + mock_signer_rotate_data: Vec, ) -> 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 @@ -208,9 +215,7 @@ pub fn integration_tests_genesis_config( ], vec![EVE_VERIFYING_KEY.to_vec(), DAVE_VERIFYING_KEY.to_vec()], ), - mock_signer_rotate: (true, initial_authorities.iter().map(|auth| { - auth.0.clone() - }).collect::>(), vec![get_account_id_from_seed::("Alice")],), + mock_signer_rotate: (true, mock_signer_rotate_data, vec![get_account_id_from_seed::("Alice//stash")],), }, "elections": ElectionsConfig { members: endowed_accounts diff --git a/node/cli/src/chain_spec/mod.rs b/node/cli/src/chain_spec/mod.rs index 077068836..79485d720 100644 --- a/node/cli/src/chain_spec/mod.rs +++ b/node/cli/src/chain_spec/mod.rs @@ -75,15 +75,15 @@ pub mod tss_account_id { pub static ref CHARLIE: sp_runtime::AccountId32 = super::hex!["946140d3d5ddb980c74ffa1bb64353b5523d2d77cdf3dc617fd63de9d3b66338"].into(); - /// Not sure what mnemonic is used to derive the following `AccountId`. + /// The `DEFAULT_DAVE_MNEMONIC` is used to derive the following `AccountId`. /// Mnemonic: "beef dutch panic monkey black glad audit twice humor gossip wealth drive" pub static ref DAVE: sp_runtime::AccountId32 = - super::hex!["12bf1c8e365c20cc2af606e3814b98b192857d85d182dac5e33fb90d0380ca75"].into(); + super::hex!["0a9054ef6b6b8ad0dd2c89895b2515583f2fbf1edced68e7328ae456d86b9402"].into(); - /// The `DEFAULT_CHARLIE_MNEMONIC` is used to derive the following `AccountId`. + /// The `DEFAULT_EVE_MNEMONIC` is used to derive the following `AccountId`. /// Mnemonic: "impact federal dish number fun crisp various wedding radio immense whisper glue" pub static ref EVE: sp_runtime::AccountId32 = - super::hex!["f2b4113735e988f662fe45e97b39770e804ebcd893ad0ab7cd8b7c5b5dcfff22"].into(); + super::hex!["ac0d9030598f1722ff7c6a2a3043fa65903448dcc7a23011ec06c1c31cdad120"].into(); } } @@ -97,7 +97,6 @@ pub mod tss_x25519_public_key { 8, 22, 19, 230, 107, 217, 249, 190, 14, 142, 155, 252, 156, 229, 120, 11, 180, 35, 83, 245, 222, 11, 153, 201, 162, 29, 153, 13, 123, 126, 128, 32, ]; - /// The `DEFAULT_BOB_MNEMONIC` is used to derive the public key. /// Mnemonic: "where sight patient orphan general short empower hope party hurt month voice" pub const BOB: [u8; 32] = [ diff --git a/node/cli/src/endowed_accounts.rs b/node/cli/src/endowed_accounts.rs index ebd58528f..34726dbcc 100644 --- a/node/cli/src/endowed_accounts.rs +++ b/node/cli/src/endowed_accounts.rs @@ -75,5 +75,6 @@ pub fn endowed_accounts_dev() -> Vec { crate::chain_spec::tss_account_id::ALICE.clone(), crate::chain_spec::tss_account_id::BOB.clone(), crate::chain_spec::tss_account_id::CHARLIE.clone(), + crate::chain_spec::tss_account_id::DAVE.clone(), ] }