From f54a54fe38dd7758818f9d639538e784e1cf78db Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 7 Aug 2024 07:49:55 +0200 Subject: [PATCH] Run the aux gen protocol when resharing to a new signing committee (#977) * Rename subsession enum * Fix updating keyshare on proactive refresh * Add aux gen to reshare process * Check that both aux info and keyshare changes following reshare in test * Testing-utils should load entropy-tss with the test_helpers feature present * Changelog * Tidy * Add TODO * Taplo * Rm entry from changelog * Comment following review --- crates/protocol/src/execute_protocol.rs | 8 ++-- crates/protocol/src/lib.rs | 6 +-- crates/testing-utils/Cargo.toml | 2 +- .../src/signing_client/api.rs | 16 ++++++- .../src/validator/api.rs | 43 ++++++++++++++----- .../src/validator/tests.rs | 24 ++++++++--- 6 files changed, 73 insertions(+), 26 deletions(-) diff --git a/crates/protocol/src/execute_protocol.rs b/crates/protocol/src/execute_protocol.rs index 9d95b6b20..842e252ea 100644 --- a/crates/protocol/src/execute_protocol.rs +++ b/crates/protocol/src/execute_protocol.rs @@ -36,7 +36,7 @@ use crate::{ errors::{GenericProtocolError, ProtocolExecutionErr}, protocol_message::{ProtocolMessage, ProtocolMessagePayload}, protocol_transport::Broadcaster, - DkgSubsession, KeyParams, KeyShareWithAuxInfo, PartyId, SessionId, + KeyParams, KeyShareWithAuxInfo, PartyId, SessionId, Subsession, }; use std::collections::BTreeSet; @@ -217,7 +217,7 @@ pub async fn execute_dkg( let my_party_id = PartyId::new(AccountId32(threshold_pair.public().0)); - let session_id_hash = session_id.blake2(Some(DkgSubsession::KeyInit))?; + let session_id_hash = session_id.blake2(Some(Subsession::KeyInit))?; let (key_init_parties, includes_me) = get_key_init_parties(&my_party_id, threshold, &party_ids, &session_id_hash)?; @@ -293,7 +293,7 @@ pub async fn execute_dkg( new_threshold: threshold, }; - let session_id_hash = session_id.blake2(Some(DkgSubsession::Reshare))?; + let session_id_hash = session_id.blake2(Some(Subsession::Reshare))?; let session = make_key_resharing_session( &mut OsRng, SynedrionSessionId::from_seed(session_id_hash.as_slice()), @@ -312,7 +312,7 @@ pub async fn execute_dkg( let chans = Channels(broadcaster.clone(), rx); // Now run the aux gen protocol to get AuxInfo - let session_id_hash = session_id.blake2(Some(DkgSubsession::AuxGen))?; + let session_id_hash = session_id.blake2(Some(Subsession::AuxGen))?; let session = make_aux_gen_session( &mut OsRng, SynedrionSessionId::from_seed(session_id_hash.as_slice()), diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 2b1b77ab7..4a4ef4234 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -203,7 +203,7 @@ impl SessionId { /// Optionally with some extra data used to identify a sub-session pub fn blake2( &self, - sub_session: Option, + sub_session: Option, ) -> Result<[u8; 32], ProtocolExecutionErr> { let mut hasher = Blake2s256::new(); hasher.update(bincode::serialize(self)?); @@ -214,9 +214,9 @@ impl SessionId { } } -/// A sub-protocol of the DKG protocol +/// A sub-protocol of the DKG or reshare protocols #[derive(Debug)] -pub enum DkgSubsession { +pub enum Subsession { /// The synedrion key init protocol KeyInit, /// The synedrion reshare protocol diff --git a/crates/testing-utils/Cargo.toml b/crates/testing-utils/Cargo.toml index e2b2d42e8..0c506b442 100644 --- a/crates/testing-utils/Cargo.toml +++ b/crates/testing-utils/Cargo.toml @@ -20,7 +20,7 @@ tokio ={ version="1.39", features=["macros", "fs", "rt-multi-thread" axum ={ version="0.7.5" } entropy-shared ={ version="0.2.0", path="../shared" } entropy-kvdb ={ version="0.2.0", path="../kvdb", default-features=false } -entropy-tss ={ version="0.2.0", path="../threshold-signature-server" } +entropy-tss ={ version="0.2.0", path="../threshold-signature-server", features=["test_helpers"] } entropy-protocol ={ version="0.2.0", path="../protocol" } synedrion ={ git="https://github.com/entropyxyz/synedrion", rev="3be1339c21384a8e60a1534f1d3bfdd022662e63" } hex ="0.4.3" diff --git a/crates/threshold-signature-server/src/signing_client/api.rs b/crates/threshold-signature-server/src/signing_client/api.rs index 8c3858ef6..bbea6738f 100644 --- a/crates/threshold-signature-server/src/signing_client/api.rs +++ b/crates/threshold-signature-server/src/signing_client/api.rs @@ -27,7 +27,7 @@ use axum::{ use blake2::{Blake2s256, Digest}; use entropy_protocol::{ execute_protocol::{execute_proactive_refresh, Channels}, - KeyParams, Listener, PartyId, SessionId, ValidatorInfo, + KeyParams, KeyShareWithAuxInfo, Listener, PartyId, SessionId, ValidatorInfo, }; use parity_scale_codec::Encode; @@ -112,7 +112,19 @@ pub async fn proactive_refresh( ocw_data.block_number, ) .await?; - let serialized_key_share = key_serialize(&new_key_share) + + // Get aux info from existing entry + let aux_info = { + let existing_entry = app_state.kv_store.kv().get(&key).await?; + let (_old_key_share, aux_info): KeyShareWithAuxInfo = deserialize(&existing_entry) + .ok_or_else(|| { + ProtocolErr::Deserialization("Failed to load KeyShare".into()) + })?; + aux_info + }; + + // Since this is a refresh with the parties not changing, store the old aux_info + let serialized_key_share = key_serialize(&(new_key_share, aux_info)) .map_err(|_| ProtocolErr::KvSerialize("Kv Serialize Error".to_string()))?; app_state.kv_store.kv().delete(&key).await?; diff --git a/crates/threshold-signature-server/src/validator/api.rs b/crates/threshold-signature-server/src/validator/api.rs index 338cc85e0..8332de2e0 100644 --- a/crates/threshold-signature-server/src/validator/api.rs +++ b/crates/threshold-signature-server/src/validator/api.rs @@ -29,6 +29,7 @@ use crate::{ }; use axum::{body::Bytes, extract::State, http::StatusCode}; use entropy_kvdb::kv_manager::{helpers::serialize as key_serialize, KvManager}; +use entropy_protocol::Subsession; pub use entropy_protocol::{ decode_verifying_key, errors::ProtocolExecutionErr, @@ -45,8 +46,8 @@ use subxt::{ OnlineClient, }; use synedrion::{ - make_key_resharing_session, sessions::SessionId as SynedrionSessionId, KeyResharingInputs, - NewHolder, OldHolder, + make_aux_gen_session, make_key_resharing_session, sessions::SessionId as SynedrionSessionId, + AuxInfo, KeyResharingInputs, NewHolder, OldHolder, }; use tokio::time::timeout; @@ -150,7 +151,7 @@ pub async fn new_reshare( let session_id = SessionId::Reshare { verifying_key, block_number: data.block_number }; let account_id = AccountId32(signer.signer().public().0); - let session_id_hash = session_id.blake2(None)?; + let session_id_hash = session_id.blake2(Some(Subsession::Reshare))?; let pair = PairWrapper(signer.signer().clone()); let mut converted_validator_info = vec![]; @@ -183,27 +184,47 @@ pub async fn new_reshare( ) .await?; - let channels = { + let (channels, broadcaster) = { let ready = timeout(Duration::from_secs(SETUP_TIMEOUT_SECONDS), rx_ready).await?; let broadcast_out = ready??; - Channels(broadcast_out, rx_from_others) + (Channels(broadcast_out.clone(), rx_from_others), broadcast_out) }; let session = make_key_resharing_session( &mut OsRng, SynedrionSessionId::from_seed(session_id_hash.as_slice()), - pair, + pair.clone(), &party_ids, inputs, ) .map_err(ProtocolExecutionErr::SessionCreation)?; - let new_key_share = execute_protocol_generic(channels, session, session_id_hash) + let (new_key_share_option, rx) = execute_protocol_generic(channels, session, session_id_hash) .await - .map_err(|_| ValidatorErr::ProtocolError("Error executing protocol".to_string()))? - .0 - .ok_or(ValidatorErr::NoOutputFromReshareProtocol)?; - let serialized_key_share = key_serialize(&new_key_share) + .map_err(|_| ValidatorErr::ProtocolError("Error executing protocol".to_string()))?; + + let new_key_share = new_key_share_option.ok_or(ValidatorErr::NoOutputFromReshareProtocol)?; + + // Setup channels for the next session + let channels = Channels(broadcaster, rx); + + // Now run an aux gen session + let session_id_hash = session_id.blake2(Some(Subsession::AuxGen))?; + let session = make_aux_gen_session( + &mut OsRng, + SynedrionSessionId::from_seed(session_id_hash.as_slice()), + pair, + &party_ids, + ) + .map_err(ProtocolExecutionErr::SessionCreation)?; + + let aux_info: AuxInfo = + execute_protocol_generic(channels, session, session_id_hash) + .await + .map_err(|_| ValidatorErr::ProtocolError("Error executing protocol".to_string()))? + .0; + + let serialized_key_share = key_serialize(&(new_key_share, aux_info)) .map_err(|_| ProtocolErr::KvSerialize("Kv Serialize Error".to_string()))?; let network_parent_key = hex::encode(NETWORK_PARENT_KEY); // TODO: should this be a two step process? see # https://github.com/entropyxyz/entropy-core/issues/968 diff --git a/crates/threshold-signature-server/src/validator/tests.rs b/crates/threshold-signature-server/src/validator/tests.rs index 0c9804c55..35885d769 100644 --- a/crates/threshold-signature-server/src/validator/tests.rs +++ b/crates/threshold-signature-server/src/validator/tests.rs @@ -31,7 +31,11 @@ use crate::{ }, validator::{api::validate_new_reshare, errors::ValidatorErr}, }; -use entropy_kvdb::clean_tests; +use entropy_kvdb::{ + clean_tests, + kv_manager::helpers::{deserialize, serialize}, +}; +use entropy_protocol::KeyShareWithAuxInfo; use entropy_shared::{ OcwMessageReshare, EVE_VERIFYING_KEY, MIN_BALANCE, NETWORK_PARENT_KEY, TEST_RESHARE_BLOCK_NUMBER, @@ -92,12 +96,22 @@ async fn test_reshare() { for response_result in response_results { assert_eq!(response_result.unwrap().text().await.unwrap(), ""); } + for i in 0..validator_ports.len() { - assert_ne!( - key_shares_before[i], - unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), validator_ports[i]).await - ); + let (key_share_before, aux_info_before): KeyShareWithAuxInfo = + deserialize(&key_shares_before[i]).unwrap(); + + let key_share_and_aux_data_after = + unsafe_get(&client, hex::encode(NETWORK_PARENT_KEY), validator_ports[i]).await; + let (key_share_after, aux_info_after): KeyShareWithAuxInfo = + deserialize(&key_share_and_aux_data_after).unwrap(); + + // Check key share has changed + assert_ne!(serialize(&key_share_before).unwrap(), serialize(&key_share_after).unwrap()); + // Check aux info has changed + assert_ne!(serialize(&aux_info_before).unwrap(), serialize(&aux_info_after).unwrap()); } + // TODO #981 - test signing a message with the new keyshare set clean_tests(); }