From f2a9f23786d1abefe0fab73a1d9e1765b14ff6d1 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:12:34 -0400 Subject: [PATCH 01/18] Store `derivation_path` in `RegisteredInfo` --- pallets/registry/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pallets/registry/src/lib.rs b/pallets/registry/src/lib.rs index 31640dd95..bdc382700 100644 --- a/pallets/registry/src/lib.rs +++ b/pallets/registry/src/lib.rs @@ -119,6 +119,9 @@ pub mod pallet { pub struct RegisteredInfo { pub programs_data: BoundedVec, T::MaxProgramHashes>, pub program_modification_account: T::AccountId, + /// TODO (Nando): We're just going to store the `count` for now, but we should consider + /// storing the full derivation path here in the future (as a `Vec`). + pub derivation_path: Option, pub version_number: u8, } @@ -140,6 +143,7 @@ pub mod pallet { RegisteredInfo { programs_data: BoundedVec::default(), program_modification_account: account_id.clone(), + derivation_path: None, version_number: T::KeyVersionNumber::get(), }, ); @@ -656,6 +660,7 @@ pub mod pallet { RegisteredInfo { programs_data: registering_info.programs_data, program_modification_account: registering_info.program_modification_account, + derivation_path: None, version_number: registering_info.version_number, }, ); @@ -769,6 +774,7 @@ pub mod pallet { programs_data, program_modification_account: program_modification_account.clone(), version_number: T::KeyVersionNumber::get(), + derivation_path: Some(count), }, ); From b8a038bed51a602f179a72701c0d56a217c0f8c0 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 7 Aug 2024 16:17:44 -0400 Subject: [PATCH 02/18] Bump metadata --- crates/client/entropy_metadata.scale | Bin 207236 -> 207269 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index e4a096a429421bbd780f43e25175f76a859c1cc9..300d208d06da9ecb134b38508b0e674cae25cfa6 100644 GIT binary patch delta 69 zcmZp<%(L_|Ps0{QmRF1d(*s^IdQA6y#i%D|lagALS(aFmnV%P5kXVw@%FL+aUjX9T Ylo}gN-}{nLvVGz!#_bbdF@3fJ0I{VT&;S4c delta 38 wcmV+>0NMYg(+q^u41lx&1nL0{mr&^eNS96O0WO!o=>Zmpvg!f1vg!i!K0tmEF8}}l From e6a5ee1ef1e6c38282d3537f7bfa6ae1e96d7e34 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:14:00 -0400 Subject: [PATCH 03/18] Split registration flow based on existence of derivation path --- .../src/helpers/signing.rs | 6 ++- .../signing_client/protocol_execution/mod.rs | 29 ++++++++++--- .../src/user/api.rs | 43 +++++++++++++------ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/crates/threshold-signature-server/src/helpers/signing.rs b/crates/threshold-signature-server/src/helpers/signing.rs index 29daf9ac5..fc8799687 100644 --- a/crates/threshold-signature-server/src/helpers/signing.rs +++ b/crates/threshold-signature-server/src/helpers/signing.rs @@ -45,6 +45,7 @@ pub async fn do_signing( app_state: &AppState, signing_session_info: SigningSessionInfo, request_limit: u32, + derivation_path: Option, // TODO (Nando): Not a fan of this... ) -> Result { tracing::debug!("Preparing to perform signing"); @@ -60,8 +61,8 @@ pub async fn do_signing( let account_id = AccountId32(signer.public().0); - // set up context for signing protocol execution - let sign_context = signing_service.get_sign_context(info.clone()).await?; + // Set up context for signing protocol execution + let sign_context = signing_service.get_sign_context(info.clone(), derivation_path).await?; let tss_accounts: Vec = user_signature_request .validators_info @@ -89,6 +90,7 @@ pub async fn do_signing( &x25519_secret_key, ) .await?; + let channels = { let ready = timeout(Duration::from_secs(SETUP_TIMEOUT_SECONDS), rx_ready).await?; let broadcast_out = ready??; diff --git a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs index abc0bec69..7a3db6fed 100644 --- a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs +++ b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs @@ -63,18 +63,35 @@ impl<'a> ThresholdSigningService<'a> { fields(sign_init), level = tracing::Level::DEBUG )] - pub async fn get_sign_context(&self, sign_init: SignInit) -> Result { + pub async fn get_sign_context( + &self, + sign_init: SignInit, + derivation_path: Option, + ) -> Result { tracing::debug!("Getting signing context"); - let key_share_and_aux_info_vec = self - .kv_manager - .kv() - .get(&hex::encode(sign_init.signing_session_info.signature_verifying_key.clone())) - .await?; + + let verifying_key = if derivation_path.is_some() { + entropy_shared::NETWORK_PARENT_KEY.as_bytes().to_vec() + } else { + sign_init.signing_session_info.signature_verifying_key.clone() + }; + + let key_share_and_aux_info_vec = + self.kv_manager.kv().get(&hex::encode(verifying_key)).await?; + let (key_share, aux_info): ( ThresholdKeyShare, AuxInfo, ) = entropy_kvdb::kv_manager::helpers::deserialize(&key_share_and_aux_info_vec) .ok_or_else(|| ProtocolErr::Deserialization("Failed to load KeyShare".into()))?; + + let key_share = if let Some(path) = derivation_path { + let path = path.parse().expect("TODO"); + key_share.derive_bip32(&path).expect("TODO") + } else { + key_share + }; + Ok(SignContext::new(sign_init, key_share, aux_info)) } diff --git a/crates/threshold-signature-server/src/user/api.rs b/crates/threshold-signature-server/src/user/api.rs index 6e5b4bda7..6e914188e 100644 --- a/crates/threshold-signature-server/src/user/api.rs +++ b/crates/threshold-signature-server/src/user/api.rs @@ -145,6 +145,7 @@ pub async fn sign_tx( .number; check_stale(user_sig_req.block_number, block_number).await?; + // Probably impossible but block signing from parent key anyways if string_verifying_key == hex::encode(NETWORK_PARENT_KEY) { return Err(UserErr::NoSigningFromParentKey); @@ -159,7 +160,9 @@ pub async fn sign_tx( if user_details.programs_data.0.is_empty() { return Err(UserErr::NoProgramPointerDefined()); } - // handle aux data padding, if it is not explicit by client for ease send through None, error if incorrect length + + // handle aux data padding, if it is not explicit by client for ease send through None, error + // if incorrect length let auxilary_data_vec; if let Some(auxilary_data) = user_sig_req.clone().auxilary_data { if auxilary_data.len() < user_details.programs_data.0.len() { @@ -170,6 +173,7 @@ pub async fn sign_tx( } else { auxilary_data_vec = vec![None; user_details.programs_data.0.len()]; } + // gets fuel from chain let max_instructions_per_programs_query = entropy::storage().parameters().max_instructions_per_programs(); @@ -186,7 +190,9 @@ pub async fn sign_tx( runtime.evaluate(&program, &signature_request, Some(&program_info.program_config), None)?; } + // TODO (Nando): Internal algo needs to be changed here let signers = get_signers_from_chain(&api, &rpc).await?; + // Use the validator info from chain as we can be sure it is in the correct order and the // details are correct user_sig_req.validators_info = signers; @@ -207,22 +213,35 @@ pub async fn sign_tx( request_author, }; - let _has_key = check_for_key(&string_verifying_key, &app_state.kv_store).await?; + // In the new registration flow we don't store the verifying key in the KVDB, so we only do this + // check if we're using the old registration flow + if user_details.derivation_path.is_none() { + let _has_key = check_for_key(&string_verifying_key, &app_state.kv_store).await?; + } + + // TODO (Nando): We're hardcoding this for now since we know the path used on-chain + let derivation_path = user_details.derivation_path.map(|count| format!("m/0/{}", count)); let (mut response_tx, response_rx) = mpsc::channel(1); // Do the signing protocol in another task, so we can already respond tokio::spawn(async move { - let signing_protocol_output = - do_signing(&rpc, user_sig_req, &app_state, signing_session_id, request_limit) - .await - .map(|signature| { - ( - BASE64_STANDARD.encode(signature.to_rsv_bytes()), - signer.signer().sign(&signature.to_rsv_bytes()), - ) - }) - .map_err(|error| error.to_string()); + let signing_protocol_output = do_signing( + &rpc, + user_sig_req, + &app_state, + signing_session_id, + request_limit, + derivation_path, + ) + .await + .map(|signature| { + ( + BASE64_STANDARD.encode(signature.to_rsv_bytes()), + signer.signer().sign(&signature.to_rsv_bytes()), + ) + }) + .map_err(|error| error.to_string()); // This response chunk is sent later with the result of the signing protocol if response_tx.try_send(serde_json::to_string(&signing_protocol_output)).is_err() { From 117d420f77e96450e763edbd68035e43e8e3599e Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:14:23 -0400 Subject: [PATCH 04/18] Tiny up a few things related to jumpstart mocking --- .../src/validator/tests.rs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 83ca9edaa..ea12287c9 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -45,6 +45,29 @@ use parity_scale_codec::Encode; use serial_test::serial; use sp_keyring::AccountKeyring; +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(bounded_vec::BoundedVec(EVE_VERIFYING_KEY.to_vec())); + + submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None).await.unwrap(); + } +} + #[tokio::test] #[serial] async fn test_reshare() { @@ -54,7 +77,10 @@ async fn test_reshare() { let alice = AccountKeyring::AliceStash; let cxt = test_node_process_testing_state(true).await; - let (_validator_ips, _validator_ids) = spawn_testing_validators(true).await; + + let add_parent_key_to_kvdb = true; + let (_validator_ips, _validator_ids) = spawn_testing_validators(add_parent_key_to_kvdb).await; + let validator_ports = vec![3001, 3002, 3003]; let api = get_api(&cxt.ws_url).await.unwrap(); let rpc = get_rpc(&cxt.ws_url).await.unwrap(); @@ -65,7 +91,7 @@ async fn test_reshare() { key_shares_before.push(unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), *port).await); } - crate::user::tests::jump_start_network(&api, &rpc).await; + jump_start_network(&api, &rpc).await; let block_number = TEST_RESHARE_BLOCK_NUMBER; let onchain_reshare_request = From 2f718edb054996bb267a8c8a81a406be0f0cf47f Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:17:15 -0400 Subject: [PATCH 05/18] Small grammatical tidyings --- crates/client/src/client.rs | 2 +- crates/threshold-signature-server/src/user/tests.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 4fc87744c..6ce4957d3 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -295,7 +295,7 @@ pub async fn put_register_request_on_chain( rpc: &LegacyRpcMethods, signature_request_keypair: sr25519::Pair, deployer: SubxtAccountId32, - program_instance: BoundedVec, + program_instances: BoundedVec, ) -> Result<(), ClientError> { let registering_tx = entropy::tx().registry().register(deployer, program_instance); diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 71c2fa434..2a3ea0a3b 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -819,7 +819,7 @@ pub async fn put_register_request_on_chain( rpc: &LegacyRpcMethods, sig_req_keyring: &Sr25519Keyring, program_modification_account: subxtAccountId32, - program_instance: BoundedVec, + program_instances: BoundedVec, ) { let sig_req_account = PairSigner::::new(sig_req_keyring.pair()); @@ -835,7 +835,7 @@ pub async fn put_new_register_request_on_chain( rpc: &LegacyRpcMethods, signature_request_account: &Sr25519Keyring, program_modification_account: subxtAccountId32, - program_instance: BoundedVec, + program_instances: BoundedVec, ) -> Result { let signature_request_account = @@ -1337,8 +1337,8 @@ async fn test_new_registration_flow() { 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. + // 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; let entropy_api = get_api(&substrate_context.ws_url).await.unwrap(); From 31937ea2e725f26e8585845ce71f24f78708b5d6 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 12:39:03 -0400 Subject: [PATCH 06/18] Remove extra function introduced in merge --- .../src/validator/tests.rs | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index ea12287c9..c325b0019 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -45,29 +45,6 @@ use parity_scale_codec::Encode; use serial_test::serial; use sp_keyring::AccountKeyring; -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(bounded_vec::BoundedVec(EVE_VERIFYING_KEY.to_vec())); - - submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None).await.unwrap(); - } -} - #[tokio::test] #[serial] async fn test_reshare() { @@ -91,7 +68,7 @@ async fn test_reshare() { key_shares_before.push(unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), *port).await); } - jump_start_network(&api, &rpc).await; + crate::user::tests::jump_start_network(&api, &rpc).await; let block_number = TEST_RESHARE_BLOCK_NUMBER; let onchain_reshare_request = From 122bdfe656d49cba53fa93bd3720e106dd9f1cbd Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:18:54 -0400 Subject: [PATCH 07/18] Fix a few typos --- crates/client/src/client.rs | 2 +- crates/client/src/user.rs | 2 +- crates/threshold-signature-server/src/user/tests.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 6ce4957d3..9767e3704 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -297,7 +297,7 @@ pub async fn put_register_request_on_chain( deployer: SubxtAccountId32, program_instances: BoundedVec, ) -> Result<(), ClientError> { - let registering_tx = entropy::tx().registry().register(deployer, program_instance); + let registering_tx = entropy::tx().registry().register(deployer, program_instances); submit_transaction_with_pair(api, rpc, &signature_request_keypair, ®istering_tx, None) .await?; diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 1c8542d08..88f53dd5d 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -36,7 +36,7 @@ pub struct UserSignatureRequest { pub block_number: BlockNumber, /// Hashing algorithm to be used for signing pub hash: HashingAlgorithm, - /// The veryfying key for the signature requested + /// The verifying key for the signature requested pub signature_verifying_key: Vec, } diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 2a3ea0a3b..65bdcf033 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -825,7 +825,7 @@ pub async fn put_register_request_on_chain( PairSigner::::new(sig_req_keyring.pair()); let registering_tx = - entropy::tx().registry().register(program_modification_account, program_instance); + entropy::tx().registry().register(program_modification_account, program_instances); submit_transaction(api, rpc, &sig_req_account, ®istering_tx, None).await.unwrap(); } @@ -842,7 +842,7 @@ pub async fn put_new_register_request_on_chain( PairSigner::::new(signature_request_account.pair()); let registering_tx = - entropy::tx().registry().register_on_chain(program_modification_account, program_instance); + entropy::tx().registry().register_on_chain(program_modification_account, program_instances); let events = submit_transaction(api, rpc, &signature_request_account, ®istering_tx, None).await?; From 8b6095733b89827c854595ebae90d58721062b4e Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:37:38 -0400 Subject: [PATCH 08/18] Add a few logging events --- crates/threshold-signature-server/src/helpers/substrate.rs | 4 +++- .../src/signing_client/protocol_execution/mod.rs | 1 + .../src/signing_client/protocol_transport.rs | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/threshold-signature-server/src/helpers/substrate.rs b/crates/threshold-signature-server/src/helpers/substrate.rs index 6f5c05499..0b836844b 100644 --- a/crates/threshold-signature-server/src/helpers/substrate.rs +++ b/crates/threshold-signature-server/src/helpers/substrate.rs @@ -75,10 +75,12 @@ pub async fn get_registered_details( let registered_result = query_chain(api, rpc, registered_info_query, None).await?; let registration_info = if let Some(old_registration_info) = registered_result { + tracing::debug!("Found user in old `Registered` struct."); + 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"); + 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)); diff --git a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs index 7a3db6fed..3d348ec3c 100644 --- a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs +++ b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs @@ -109,6 +109,7 @@ impl<'a> ThresholdSigningService<'a> { threshold_signer: &sr25519::Pair, threshold_accounts: Vec, ) -> Result { + tracing::debug!("Executing signing session"); tracing::trace!("Signing info {session_id:?}"); let message_hash = if let SessionId::Sign(session_info) = &session_id { diff --git a/crates/threshold-signature-server/src/signing_client/protocol_transport.rs b/crates/threshold-signature-server/src/signing_client/protocol_transport.rs index a6a047db5..635d8814d 100644 --- a/crates/threshold-signature-server/src/signing_client/protocol_transport.rs +++ b/crates/threshold-signature-server/src/signing_client/protocol_transport.rs @@ -50,6 +50,8 @@ pub async fn open_protocol_connections( state: &ListenerState, x25519_secret_key: &x25519_dalek::StaticSecret, ) -> Result<(), ProtocolErr> { + tracing::debug!("Opening protocol connections"); + let connect_to_validators = validators_info .iter() .filter(|validators_info| { From 1cd8ca6bf219afb68444ae0436ee72e31192171c Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:40:03 -0400 Subject: [PATCH 09/18] Allow querying both new and old signers using helper --- crates/client/src/client.rs | 2 +- crates/client/src/user.rs | 28 ++++++++++++++----- .../src/user/api.rs | 4 +-- .../src/user/tests.rs | 20 +++++++++---- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 9767e3704..0688f096f 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -131,7 +131,7 @@ pub async fn sign( auxilary_data: Option>, ) -> Result { let message_hash = Hasher::keccak(&message); - let validators_info = get_signers_from_chain(api, rpc).await?; + let validators_info = get_signers_from_chain(api, rpc, false).await?; tracing::debug!("Validators info {:?}", validators_info); let block_number = rpc.chain_get_header(None).await?.ok_or(ClientError::BlockNumber)?.number; let signature_request = UserSignatureRequest { diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 88f53dd5d..679bfc2b3 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -43,20 +43,32 @@ pub struct UserSignatureRequest { pub async fn get_signers_from_chain( api: &OnlineClient, rpc: &LegacyRpcMethods, + with_parent_key: bool, ) -> Result, SubgroupGetError> { - let all_validators_query = entropy::storage().session().validators(); - let mut validators = query_chain(api, rpc, all_validators_query, None) - .await? - .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))?; - let block_hash = rpc.chain_get_block_hash(None).await?; - let mut handles = Vec::new(); + let mut validators = if with_parent_key { + let signer_query = entropy::storage().staking_extension().signers(); + query_chain(api, rpc, signer_query, None) + .await? + .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))? + } else { + let all_validators_query = entropy::storage().session().validators(); + let mut validators = query_chain(api, rpc, all_validators_query, None) + .await? + .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))?; + + validators.sort(); + validators + }; // TODO #898 For now we use a fix proportion of the number of validators as the threshold let threshold = (validators.len() as f32 * 0.75) as usize; + // TODO #899 For now we just take the first t validators as the ones to perform signing - validators.sort(); validators.truncate(threshold); + let block_hash = rpc.chain_get_block_hash(None).await?; + let mut handles = Vec::new(); + for validator in validators { let handle: tokio::task::JoinHandle> = tokio::task::spawn({ @@ -77,8 +89,10 @@ pub async fn get_signers_from_chain( }) } }); + handles.push(handle); } + let mut all_signers: Vec = vec![]; for handle in handles { all_signers.push(handle.await??); diff --git a/crates/threshold-signature-server/src/user/api.rs b/crates/threshold-signature-server/src/user/api.rs index 6e914188e..93bba52ae 100644 --- a/crates/threshold-signature-server/src/user/api.rs +++ b/crates/threshold-signature-server/src/user/api.rs @@ -190,8 +190,8 @@ pub async fn sign_tx( runtime.evaluate(&program, &signature_request, Some(&program_info.program_config), None)?; } - // TODO (Nando): Internal algo needs to be changed here - let signers = get_signers_from_chain(&api, &rpc).await?; + let with_parent_key = user_details.derivation_path.is_some(); + let signers = get_signers_from_chain(&api, &rpc, with_parent_key).await?; // Use the validator info from chain as we can be sure it is in the correct order and the // details are correct diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 65bdcf033..9ee3c5241 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -192,8 +192,10 @@ async fn test_sign_tx_no_chain() { request_author: signature_request_account.clone(), }); + let with_parent_key = false; let (validators_info, mut generic_msg, validator_ips_and_keys) = - get_sign_tx_data(&entropy_api, &rpc, hex::encode(PREIMAGE_SHOULD_SUCCEED)).await; + get_sign_tx_data(&entropy_api, &rpc, hex::encode(PREIMAGE_SHOULD_SUCCEED), with_parent_key) + .await; generic_msg.block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; // test points to no program @@ -376,8 +378,10 @@ async fn test_sign_tx_no_chain_fail() { let rpc = get_rpc(&substrate_context.node_proc.ws_url).await.unwrap(); let mock_client = reqwest::Client::new(); + let with_parent_key = false; let (validators_info, mut generic_msg, validator_ips_and_keys) = - get_sign_tx_data(&entropy_api, &rpc, hex::encode(PREIMAGE_SHOULD_SUCCEED)).await; + get_sign_tx_data(&entropy_api, &rpc, hex::encode(PREIMAGE_SHOULD_SUCCEED), with_parent_key) + .await; // fails verification tests // wrong key for wrong validator @@ -515,8 +519,10 @@ async fn test_program_with_config() { let message = "0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac990019243726561746564204f6e20456e74726f7079018080"; let message_hash = Hasher::keccak(message.as_bytes()); + + let with_parent_key = false; let (validators_info, mut generic_msg, validator_ips_and_keys) = - get_sign_tx_data(&entropy_api, &rpc, hex::encode(message)).await; + get_sign_tx_data(&entropy_api, &rpc, hex::encode(message), with_parent_key).await; let config = r#" { @@ -1097,7 +1103,9 @@ async fn test_device_key_proxy() { .await .unwrap(); - let validators_info = get_signers_from_chain(&entropy_api, &rpc).await.unwrap(); + let with_parent_key = false; + let validators_info = + get_signers_from_chain(&entropy_api, &rpc, with_parent_key).await.unwrap(); let context = signing_context(b""); let sr25519_signature: Sr25519Signature = keypair.sign(context.bytes(PREIMAGE_SHOULD_SUCCEED)); @@ -1580,8 +1588,10 @@ pub async fn get_sign_tx_data( api: &OnlineClient, rpc: &LegacyRpcMethods, message: String, + with_parent_key: bool, ) -> (Vec, UserSignatureRequest, Vec<(String, [u8; 32])>) { - let validators_info = get_signers_from_chain(api, rpc).await.unwrap(); + let validators_info = get_signers_from_chain(api, rpc, with_parent_key).await.unwrap(); + let generic_msg = UserSignatureRequest { message, auxilary_data: Some(vec![ From a2b77c3469ddf511e6e2cac9f36057ce91df4cf9 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:47:14 -0400 Subject: [PATCH 10/18] Add working test for signature request with derived account --- .../src/user/tests.rs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 9ee3c5241..9c7e1a836 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -364,6 +364,135 @@ async fn test_sign_tx_no_chain() { clean_tests(); } +#[tokio::test] +#[serial] +async fn signature_request_with_derived_account_works() { + 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` 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; + 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." + ); + + // Now that we've set up and registered a user, we can proceed with testing the signing flow + + let with_parent_key = true; + let (validators_info, mut signature_request, validator_ips_and_keys) = + get_sign_tx_data(&entropy_api, &rpc, hex::encode(PREIMAGE_SHOULD_SUCCEED), with_parent_key) + .await; + + // We'll use the actual verifying key we registered for the signature request + signature_request.signature_verifying_key = actual_verifying_key.to_vec(); + signature_request.block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; + + let signature_request_responses = submit_transaction_requests( + validator_ips_and_keys.clone(), + signature_request.clone(), + alice, + ) + .await; + + // We expect that the signature we get back is valid + let message_hash = Hasher::keccak(PREIMAGE_SHOULD_SUCCEED); + let verifying_key = + SynedrionVerifyingKey::try_from(signature_request.signature_verifying_key.as_slice()) + .unwrap(); + verify_signature(signature_request_responses, message_hash, &verifying_key, &validators_info) + .await; + + clean_tests(); +} + +#[ignore] #[tokio::test] #[serial] async fn test_sign_tx_no_chain_fail() { From 170f5e01a47db9e9637333c5edfc2d32c521d2b5 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:50:16 -0400 Subject: [PATCH 11/18] Fix Registry test compilation --- pallets/registry/src/tests.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/registry/src/tests.rs b/pallets/registry/src/tests.rs index 6313cfc3d..1483ca685 100644 --- a/pallets/registry/src/tests.rs +++ b/pallets/registry/src/tests.rs @@ -513,6 +513,7 @@ fn it_confirms_registers_a_user() { RegisteredInfo { programs_data: programs_info.clone(), program_modification_account: 2, + derivation_path: None, version_number: 1, } ); @@ -572,6 +573,7 @@ fn it_changes_a_program_pointer() { let mut registered_info = RegisteredInfo { programs_data: programs_info, program_modification_account: 2, + derivation_path: None, version_number: 1, }; @@ -652,6 +654,7 @@ fn it_changes_a_program_mod_account() { let mut registered_info = RegisteredInfo { programs_data: programs_info, program_modification_account: 2, + derivation_path: None, version_number: 1, }; From 4f3fb8c6ca09f3fbf52854719ce1d5ee721f3d4e Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 17:52:08 -0400 Subject: [PATCH 12/18] Don't ignore old signing test --- crates/threshold-signature-server/src/user/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/threshold-signature-server/src/user/tests.rs b/crates/threshold-signature-server/src/user/tests.rs index 9c7e1a836..da7f24377 100644 --- a/crates/threshold-signature-server/src/user/tests.rs +++ b/crates/threshold-signature-server/src/user/tests.rs @@ -492,7 +492,6 @@ async fn signature_request_with_derived_account_works() { clean_tests(); } -#[ignore] #[tokio::test] #[serial] async fn test_sign_tx_no_chain_fail() { From 2f00c950205418a7190c4e25d3489f4b762a0577 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 18:00:55 -0400 Subject: [PATCH 13/18] Fix Registry benchmark compilation --- pallets/registry/src/benchmarking.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/registry/src/benchmarking.rs b/pallets/registry/src/benchmarking.rs index f7312229f..9aa057a84 100644 --- a/pallets/registry/src/benchmarking.rs +++ b/pallets/registry/src/benchmarking.rs @@ -311,6 +311,7 @@ benchmarks! { RegisteredInfo { program_modification_account: sig_req_account.clone(), programs_data: programs_info, + derivation_path: None, version_number: T::KeyVersionNumber::get() }, ); @@ -345,6 +346,7 @@ benchmarks! { RegisteredInfo { program_modification_account: sig_req_account.clone(), programs_data: programs_info, + derivation_path: None, version_number: T::KeyVersionNumber::get() }, ); From 95416f875107706c911606b7a14cd76b52f6a787 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 18:13:55 -0400 Subject: [PATCH 14/18] Handle BIP-32 errors instead of panicking --- Cargo.lock | 3 +++ crates/threshold-signature-server/Cargo.toml | 3 ++- crates/threshold-signature-server/src/helpers/signing.rs | 2 +- .../threshold-signature-server/src/signing_client/errors.rs | 2 ++ .../src/signing_client/protocol_execution/mod.rs | 4 ++-- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cffe24e2e..2a125d758 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -831,6 +831,8 @@ dependencies = [ "bs58 0.5.1", "hmac 0.12.1", "k256", + "once_cell", + "pbkdf2 0.12.2", "rand_core 0.6.4", "ripemd", "sha2 0.10.8", @@ -2753,6 +2755,7 @@ dependencies = [ "backoff", "base64 0.22.1", "bincode 1.3.3", + "bip32", "bip39", "blake2 0.10.6", "blake3", diff --git a/crates/threshold-signature-server/Cargo.toml b/crates/threshold-signature-server/Cargo.toml index 73772b4a8..2673b8d8e 100644 --- a/crates/threshold-signature-server/Cargo.toml +++ b/crates/threshold-signature-server/Cargo.toml @@ -57,8 +57,9 @@ uuid ={ version="1.10.0", features=["v4"] } # Misc tokio-tungstenite="0.23.1" -bip39 ={ version="2.0.0", features=["zeroize"] } bincode ="1.3.3" +bip32 ={ version="0.5.2" } +bip39 ={ version="2.0.0", features=["zeroize"] } bytes ={ version="1.7", default-features=false, features=["serde"] } base64 ="0.22.1" clap ={ version="4.5.15", features=["derive"] } diff --git a/crates/threshold-signature-server/src/helpers/signing.rs b/crates/threshold-signature-server/src/helpers/signing.rs index fc8799687..ddab8a9d0 100644 --- a/crates/threshold-signature-server/src/helpers/signing.rs +++ b/crates/threshold-signature-server/src/helpers/signing.rs @@ -45,7 +45,7 @@ pub async fn do_signing( app_state: &AppState, signing_session_info: SigningSessionInfo, request_limit: u32, - derivation_path: Option, // TODO (Nando): Not a fan of this... + derivation_path: Option, ) -> Result { tracing::debug!("Preparing to perform signing"); diff --git a/crates/threshold-signature-server/src/signing_client/errors.rs b/crates/threshold-signature-server/src/signing_client/errors.rs index 66155004d..abe5c9065 100644 --- a/crates/threshold-signature-server/src/signing_client/errors.rs +++ b/crates/threshold-signature-server/src/signing_client/errors.rs @@ -108,6 +108,8 @@ pub enum ProtocolErr { SubstrateClient(#[from] entropy_client::substrate::SubstrateError), #[error("Listener: {0}")] Listener(#[from] entropy_protocol::errors::ListenerErr), + #[error("Failed to derive BIP-32 account: {0}")] + Bip32DerivationError(#[from] bip32::Error), } impl IntoResponse for ProtocolErr { diff --git a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs index 3d348ec3c..4b161a6db 100644 --- a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs +++ b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs @@ -86,8 +86,8 @@ impl<'a> ThresholdSigningService<'a> { .ok_or_else(|| ProtocolErr::Deserialization("Failed to load KeyShare".into()))?; let key_share = if let Some(path) = derivation_path { - let path = path.parse().expect("TODO"); - key_share.derive_bip32(&path).expect("TODO") + let path = path.parse()?; + key_share.derive_bip32(&path)? } else { key_share }; From 80e315f01e085cad7945d9027ff63b5001690d80 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Mon, 12 Aug 2024 19:09:52 -0400 Subject: [PATCH 15/18] Store full BIP-32 derivation path on-chain --- crates/client/entropy_metadata.scale | Bin 207269 -> 207273 bytes .../src/helpers/signing.rs | 2 +- .../signing_client/protocol_execution/mod.rs | 3 +-- .../src/user/api.rs | 12 +++++++++--- .../src/user/errors.rs | 2 ++ pallets/registry/src/lib.rs | 13 ++++++------- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index 300d208d06da9ecb134b38508b0e674cae25cfa6..7efb92da82182b3dafca8a27f2262a392da09eb6 100644 GIT binary patch delta 1384 zcmZXUT})g>6vyYBnT0#-E^JE|map(76p&4p1`=o}5dkYU3+1EKnjl@4Yj^qBh26Uf zF|}YIO`|9ck|U-`HEAF4MIUUgFD5=TrpCm^)Wq%yQ z{LVRZ{&VKueqdXBV4M94-uvU|x(3bNlSVKcY97%2&9TN7`sNYj;krgf7Cwg?8ui=; zPthF^eT)hAPy<&S>~CRe&KTYSwrRp+XhnELZCkL8TS*t{+JB_J(QO1pH*JXs`5B?GeWK|bBwh3Cn(2L-gb3&%mm zt<7LhE{^O$WyTQ+wvLF8u_N@*f=+X74{kWXnItX>tc5&U6nGMgB+ZRLA#Dmgfu)lE zJPj_YvtVgah18n$kP!(_&J0CjAtN}U7xpGc#^_fIy6CC}%fL;atHD_(p)syO$Fva% zhN3~g(!w22K5|qQBUn9StWGLz+>$lW`82E^Xp+K}lv#*=Xpo>ey22q{fsVfLgf3Wv z;(@VKitE**v2jCF+^pz;R>f-FV}i{pn5jx5g0&{$R{Bt48U2-pmC!-O=~!0KDdFXF zp|Mc-tx$i&7mE7)@uQ7L?nuJ#q~q7nnVetHup7LzreOv2(0vVS>UyLod9gpD>t5fa zCeDBt#Q@Z+n$D?|i5FgwOg&6!UaV(>R3ouB+bhA-84BnTrDD6!P>11*r+C$)r_>Xa z|1T&Q51Cp~FuAd&4E&N$NNu^N&q-j#Dwv`gD^^s-Qe>PsEaOC689HmjTzcP%-@#?- zv*C}`b18lchy51fevB>9D|T##Yjo9)wPI1)0?Y7;lYSRyp`Yv+71vWt!7Vf9PCVh~D9^6q?TaVGtIan_CHTYC*z(v`)SON!T zeJ*xy!v@V;_-n9f{$}AGP`A#J&cA>y`Zk?6!4CbM&Od{F^MCs?RN>7sTh9ib=JZQ|=rKc)E zs0Q}yPB~wj_&To#F9nP|Ie)P&;EP<+6YWQ5GPs==$@%y_%NM0`zJg{lco$UDy$pUA z8fekV+eMRf+@d8b&oLic`A5Lr@)W&m=l8i+)=_^ZAI+$ijuF*YUO7njGWpgqpVW+L zx2zf-5BtZ40={U#qnqDn@i&F)ncHl6|3Y2`Q{RsT*Cs`xGV4|JY%zyJUM delta 1284 zcmZWoUrbwN6#vfoZt3^6r4+}%3K&*oqYjdSY(fId=76P|4jC1lhF~q%wWGAq-kS12 zMiVew3`Qh)f|+qM@~{VEmT0co!=CnHGP5YgI1LZP_%IewhCJ|q`nBDIgyiPl^PTT^ ze)*l>J?G|%V|m3f{WZM5?wsY&)HiNK6YuIfT}?~+!;K{ z*s5R*zX7%;=+CoIWp+P;Mub0TYy%$PLs6*8Y{DVf$mQJT@+RB?+!71_F4fbOzaRrU zCTzjS;NDeKTHT%iX^~Qv%5v$JwUX@#n$XZq-#vz705q=Q(^RqzN9pg!IaS->p%dFG z`d}N5*oq}MdXgc=ip@LQaLWayxq@CwXjn}@Xjq4CN!byop%M$WVy(=prZ+8k7!K2v z1)qTix@N%_z)L?_&{N(lp*`1YPsT__X6T;~#TT>)JKi7{PcEea2ebtv36jfGVT^)yZ17I(vnzVgu4uu5G<|Nz zU+IP&%W2+$x4l#Q3bO|bvxUMe8O7KMvvjK%o8T%XoXRe6;uy^3;+J5aZacBnJHL-_ z@gUz~fp4+E&|ZQgxFqS@b$HgiR)RZ4utpzMpbs}xllC(Dr2^}g|=-w+RpEiiN)cYv%73?gO, + derivation_path: Option, ) -> Result { tracing::debug!("Preparing to perform signing"); diff --git a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs index 4b161a6db..780a21134 100644 --- a/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs +++ b/crates/threshold-signature-server/src/signing_client/protocol_execution/mod.rs @@ -66,7 +66,7 @@ impl<'a> ThresholdSigningService<'a> { pub async fn get_sign_context( &self, sign_init: SignInit, - derivation_path: Option, + derivation_path: Option, ) -> Result { tracing::debug!("Getting signing context"); @@ -86,7 +86,6 @@ impl<'a> ThresholdSigningService<'a> { .ok_or_else(|| ProtocolErr::Deserialization("Failed to load KeyShare".into()))?; let key_share = if let Some(path) = derivation_path { - let path = path.parse()?; key_share.derive_bip32(&path)? } else { key_share diff --git a/crates/threshold-signature-server/src/user/api.rs b/crates/threshold-signature-server/src/user/api.rs index 93bba52ae..9fb374ce8 100644 --- a/crates/threshold-signature-server/src/user/api.rs +++ b/crates/threshold-signature-server/src/user/api.rs @@ -161,7 +161,7 @@ pub async fn sign_tx( return Err(UserErr::NoProgramPointerDefined()); } - // handle aux data padding, if it is not explicit by client for ease send through None, error + // Handle aux data padding, if it is not explicit by client for ease send through None, error // if incorrect length let auxilary_data_vec; if let Some(auxilary_data) = user_sig_req.clone().auxilary_data { @@ -219,8 +219,14 @@ pub async fn sign_tx( let _has_key = check_for_key(&string_verifying_key, &app_state.kv_store).await?; } - // TODO (Nando): We're hardcoding this for now since we know the path used on-chain - let derivation_path = user_details.derivation_path.map(|count| format!("m/0/{}", count)); + let derivation_path = if let Some(path) = user_details.derivation_path { + let decoded_path = String::decode(&mut path.as_ref())?; + let path = bip32::DerivationPath::from_str(&decoded_path)?; + + Some(path) + } else { + None + }; let (mut response_tx, response_rx) = mpsc::channel(1); diff --git a/crates/threshold-signature-server/src/user/errors.rs b/crates/threshold-signature-server/src/user/errors.rs index e6eeb8203..61c43fe24 100644 --- a/crates/threshold-signature-server/src/user/errors.rs +++ b/crates/threshold-signature-server/src/user/errors.rs @@ -167,6 +167,8 @@ pub enum UserErr { SubgroupGet(#[from] entropy_client::user::SubgroupGetError), #[error("Unknown hashing algorthim - user is using a newer version than us")] UnknownHashingAlgorithm, + #[error("Failed to derive BIP-32 account: {0}")] + Bip32DerivationError(#[from] bip32::Error), } impl From for UserErr { diff --git a/pallets/registry/src/lib.rs b/pallets/registry/src/lib.rs index bdc382700..35c7b5075 100644 --- a/pallets/registry/src/lib.rs +++ b/pallets/registry/src/lib.rs @@ -119,9 +119,8 @@ pub mod pallet { pub struct RegisteredInfo { pub programs_data: BoundedVec, T::MaxProgramHashes>, pub program_modification_account: T::AccountId, - /// TODO (Nando): We're just going to store the `count` for now, but we should consider - /// storing the full derivation path here in the future (as a `Vec`). - pub derivation_path: Option, + /// The SCALE encoded BIP-32 `DerivationPath` used to register this account. + pub derivation_path: Option>, pub version_number: u8, } @@ -756,9 +755,9 @@ pub mod pallet { // For a V1 of this flow it's fine, but we'll need to think about a better solution // down the line. let count = RegisteredOnChain::::count(); - let path = - bip32::DerivationPath::from_str(&scale_info::prelude::format!("m/0/{}", count)) - .map_err(|_| Error::::InvalidBip32DerivationPath)?; + let inner_path = scale_info::prelude::format!("m/0/{}", count); + let path = bip32::DerivationPath::from_str(&inner_path) + .map_err(|_| Error::::InvalidBip32DerivationPath)?; let child_verifying_key = network_verifying_key .derive_verifying_key_bip32(&path) .map_err(|_| Error::::Bip32AccountDerivationFailed)?; @@ -773,8 +772,8 @@ pub mod pallet { RegisteredInfo { programs_data, program_modification_account: program_modification_account.clone(), + derivation_path: Some(inner_path.encode()), version_number: T::KeyVersionNumber::get(), - derivation_path: Some(count), }, ); From 2ea29682136bb3f61539510cb171d6b50445bd98 Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Tue, 13 Aug 2024 12:42:02 -0400 Subject: [PATCH 16/18] Add `CHANGELOG` entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a74c71764..1c63097f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ At the moment this project **does not** adhere to - Reshare confirmation ([#965](https://github.com/entropyxyz/entropy-core/pull/965)) - Set inital signers ([#971](https://github.com/entropyxyz/entropy-core/pull/971)) - Add parent key threshold dynamically ([#974](https://github.com/entropyxyz/entropy-core/pull/974)) +- Signing flow with derived accounts ([#990](https://github.com/entropyxyz/entropy-core/pull/990)) ### Changed - Fix TSS `AccountId` keys in chainspec ([#993](https://github.com/entropyxyz/entropy-core/pull/993)) From bac4ea843e25ad25980efdf6ecaf8e82e956063c Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 14 Aug 2024 15:01:32 -0400 Subject: [PATCH 17/18] Sort signers from Staking Extension pallet when signing --- crates/client/src/user.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 679bfc2b3..457fd7899 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -52,17 +52,18 @@ pub async fn get_signers_from_chain( .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))? } else { let all_validators_query = entropy::storage().session().validators(); - let mut validators = query_chain(api, rpc, all_validators_query, None) + query_chain(api, rpc, all_validators_query, None) .await? - .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))?; - - validators.sort(); - validators + .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))? }; // TODO #898 For now we use a fix proportion of the number of validators as the threshold let threshold = (validators.len() as f32 * 0.75) as usize; + // We sort the validators here to ensure that we have a consistent ordering that external + // clients (e.g the Entropy JS SDK) can rely on. + validators.sort(); + // TODO #899 For now we just take the first t validators as the ones to perform signing validators.truncate(threshold); From a5e1dc35c8440e90a6e03c1f833dd6505c22c09c Mon Sep 17 00:00:00 2001 From: Hernando Castano Date: Wed, 14 Aug 2024 16:13:41 -0400 Subject: [PATCH 18/18] Revert "Sort signers from Staking Extension pallet when signing" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out this breaks the signing flow for some reason 🤷 --- crates/client/src/user.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 457fd7899..679bfc2b3 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -52,18 +52,17 @@ pub async fn get_signers_from_chain( .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))? } else { let all_validators_query = entropy::storage().session().validators(); - query_chain(api, rpc, all_validators_query, None) + let mut validators = query_chain(api, rpc, all_validators_query, None) .await? - .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))? + .ok_or_else(|| SubgroupGetError::ChainFetch("Get all validators error"))?; + + validators.sort(); + validators }; // TODO #898 For now we use a fix proportion of the number of validators as the threshold let threshold = (validators.len() as f32 * 0.75) as usize; - // We sort the validators here to ensure that we have a consistent ordering that external - // clients (e.g the Entropy JS SDK) can rely on. - validators.sort(); - // TODO #899 For now we just take the first t validators as the ones to perform signing validators.truncate(threshold);