Skip to content

Commit

Permalink
Select validators for jumpstart DKG (#1053)
Browse files Browse the repository at this point in the history
* Select validators based on block number when doing initial DKG

* Error handling

* Tidy

* Comments

* Select jumpstart validators when handling jumpstart extrinsic

* OcwMessageDkg no longer includes signature request account

* Update propagation pallet

* Update metadata

* DKG session id no longer contains signature request account

* Handle validating selected validators on jumpstart on TSS

* Lockfile

* Tidy

* Tests for registry pallet

* Tidy

* Add alloc feature to rand

* Update entropy-protocol tests

* Update registry benchmarks

* Recalculate registry weights

* Update test helper for mocking jumpstart

* Fix jumpstart network test

* Fix registry pallet tests

* Update TSS bad siging participant test

* Update propagation pallet test

* Changelog

* Update registry weights

* Update registry benchmarks

* Add comment following review

* Fix getting newest value from jumpstart dkg storage map and add test

* Update pallets/registry/src/benchmarking.rs

* Comment following review

---------

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
  • Loading branch information
ameba23 and HCastano authored Sep 19, 2024
1 parent 5a6d897 commit 959ce98
Show file tree
Hide file tree
Showing 18 changed files with 425 additions and 339 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ At the moment this project **does not** adhere to
cleaned up. A lot of storage entries, events, and extrinsics were removed from the `Registry`
pallet. The genesis build config was also removed. Additionally, the `new/user/` HTTP endpoint in
the TSS was removed since it was no longer necessary.
- In [#1045](https://github.com/entropyxyz/entropy-core/pull/1045), `ProgramsInfo` now takes `version_number` to maintain backwards compatibility if programs runtime is updated
- In [#1045](https://github.com/entropyxyz/entropy-core/pull/1045), `ProgramsInfo` now takes `version_number` to maintain backwards compatibility if programs runtime is updated

### Added
- Jumpstart network ([#918](https://github.com/entropyxyz/entropy-core/pull/918))
Expand All @@ -34,6 +34,7 @@ At the moment this project **does not** adhere to
- Attestation pallet ([#1003](https://github.com/entropyxyz/entropy-core/pull/1003))
- Update test CLI for new registration and signing flows ([#1008](https://github.com/entropyxyz/entropy-core/pull/1008))
- Add remove program function to entropy-client ([#1023](https://github.com/entropyxyz/entropy-core/pull/1023))
- Select validators for jumpstart DKG [#1053](https://github.com/entropyxyz/entropy-core/pull/1053))
- Add a programs version ([#1045](https://github.com/entropyxyz/entropy-core/pull/1045))

### Changed
Expand Down
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified crates/client/entropy_metadata.scale
Binary file not shown.
7 changes: 3 additions & 4 deletions crates/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ impl RecoverableSignature {
/// An identifier to specify and particular protocol session
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub enum SessionId {
/// A distributed key generation protocol session for registering
Dkg { user: AccountId32, block_number: u32 },
/// A distributed key generation protocol session for initial network jumpstart
Dkg { block_number: u32 },
/// A proactive refresh session
Reshare { verifying_key: Vec<u8>, block_number: u32 },
/// A signing session
Expand All @@ -181,8 +181,7 @@ pub struct SigningSessionInfo {
impl Hash for SessionId {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
SessionId::Dkg { user, block_number } => {
user.0.hash(state);
SessionId::Dkg { block_number } => {
block_number.hash(state);
},
SessionId::Reshare { verifying_key, block_number } => {
Expand Down
4 changes: 2 additions & 2 deletions crates/protocol/tests/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async fn test_dkg_with_parties(num_parties: usize) {
let parties: Vec<_> =
pairs.iter().map(|pair| ValidatorSecretInfo::pair_only(pair.clone())).collect();
let threshold = parties.len();
let session_id = SessionId::Dkg { user: AccountId32([0; 32]), block_number: 0 };
let session_id = SessionId::Dkg { block_number: 0 };
let mut outputs = test_protocol_with_parties(parties, session_id, threshold).await;
if let ProtocolOutput::Dkg(_keyshare) = outputs.pop().unwrap() {
} else {
Expand All @@ -159,7 +159,7 @@ async fn test_dkg_and_sign_with_parties(num_parties: usize) {
let (pairs, ids) = get_keypairs_and_ids(num_parties);
let dkg_parties =
pairs.iter().map(|pair| ValidatorSecretInfo::pair_only(pair.clone())).collect();
let session_id = SessionId::Dkg { user: AccountId32([0; 32]), block_number: 0 };
let session_id = SessionId::Dkg { block_number: 0 };
let outputs = test_protocol_with_parties(dkg_parties, session_id, threshold).await;

let signing_committee = (0..threshold)
Expand Down
3 changes: 1 addition & 2 deletions crates/shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ pub struct ValidatorInfo {
pub tss_account: codec::alloc::vec::Vec<u8>,
}

/// Offchain worker message for initiating a dkg
/// Offchain worker message for initiating the initial jumpstart DKG
#[cfg(not(feature = "wasm"))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, TypeInfo)]
pub struct OcwMessageDkg {
pub block_number: BlockNumber,
pub sig_request_accounts: Vec<Vec<u8>>,
pub validators_info: Vec<ValidatorInfo>,
}

Expand Down
7 changes: 5 additions & 2 deletions crates/threshold-signature-server/src/helpers/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,18 @@ pub async fn jump_start_network_with_signer(
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];
let validators_names =
vec![ValidatorName::Alice, 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();
// Ignore the error as one confirmation will fail
let _result =
submit_transaction(api, rpc, &tss_signer, &jump_start_confirm_request, None).await;
}
}

Expand Down
3 changes: 1 addition & 2 deletions crates/threshold-signature-server/src/helpers/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ pub async fn do_dkg(
signer: &PairSigner<EntropyConfig, sr25519::Pair>,
x25519_secret_key: &StaticSecret,
state: &ListenerState,
sig_request_account: AccountId32,
block_number: u32,
) -> Result<KeyShareWithAuxInfo, UserErr> {
let session_id = SessionId::Dkg { user: sig_request_account.clone(), block_number };
let session_id = SessionId::Dkg { block_number };
let account_id = AccountId32(signer.signer().public().0);
let mut converted_validator_info = vec![];
let mut tss_accounts = vec![];
Expand Down
82 changes: 28 additions & 54 deletions crates/threshold-signature-server/src/user/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use futures::{
};
use num::{bigint::BigInt, FromPrimitive, Num, ToPrimitive};
use parity_scale_codec::{Decode, DecodeAll, Encode};
use rand_core::OsRng;
use serde::{Deserialize, Serialize};
use sp_core::{crypto::AccountId32, H256};
use subxt::{
Expand All @@ -55,6 +56,7 @@ use subxt::{
Config, OnlineClient,
};
use synedrion::ThresholdKeyShare;
use tokio::select;
use tracing::instrument;
use x25519_dalek::StaticSecret;
use zeroize::Zeroize;
Expand Down Expand Up @@ -265,7 +267,7 @@ pub async fn generate_network_key(
let data = OcwMessageDkg::decode(&mut encoded_data.as_ref())?;
tracing::Span::current().record("block_number", data.block_number);

if data.sig_request_accounts.is_empty() {
if data.validators_info.is_empty() {
return Ok(StatusCode::NO_CONTENT);
}

Expand Down Expand Up @@ -317,57 +319,40 @@ async fn setup_dkg(
app_state: AppState,
) -> Result<(), UserErr> {
tracing::debug!("Preparing to execute DKG");
for sig_request_account in data.sig_request_accounts.into_iter() {
let address_slice: &[u8; 32] = &sig_request_account
.clone()
.try_into()
.map_err(|_| UserErr::AddressConversionError("Invalid Length".to_string()))?;
let sig_request_address = SubxtAccountId32(*address_slice);

let (key_share, aux_info) = do_dkg(
&data.validators_info,
&signer,
x25519_secret_key,
&app_state.listener_state,
sig_request_address.clone(),
data.block_number,
)
.await?;
let (key_share, aux_info) = do_dkg(
&data.validators_info,
&signer,
x25519_secret_key,
&app_state.listener_state,
data.block_number,
)
.await?;

let verifying_key = key_share.verifying_key().to_encoded_point(true).as_bytes().to_vec();
let string_verifying_key = if sig_request_account == NETWORK_PARENT_KEY.encode() {
hex::encode(NETWORK_PARENT_KEY)
} else {
hex::encode(verifying_key.clone())
}
.to_string();
let verifying_key = key_share.verifying_key().to_encoded_point(true).as_bytes().to_vec();

let serialized_key_share = key_serialize(&(key_share, aux_info))
.map_err(|_| UserErr::KvSerialize("Kv Serialize Error".to_string()))?;
let serialized_key_share = key_serialize(&(key_share, aux_info))
.map_err(|_| UserErr::KvSerialize("Kv Serialize Error".to_string()))?;

let reservation = app_state.kv_store.kv().reserve_key(string_verifying_key.clone()).await?;
app_state.kv_store.kv().put(reservation, serialized_key_share.clone()).await?;
let reservation = app_state.kv_store.kv().reserve_key(hex::encode(NETWORK_PARENT_KEY)).await?;
app_state.kv_store.kv().put(reservation, serialized_key_share.clone()).await?;

let block_hash = rpc
.chain_get_block_hash(None)
.await?
.ok_or_else(|| UserErr::OptionUnwrapError("Error getting block hash".to_string()))?;
let block_hash = rpc
.chain_get_block_hash(None)
.await?
.ok_or_else(|| UserErr::OptionUnwrapError("Error getting block hash".to_string()))?;

let nonce_call =
entropy::apis().account_nonce_api().account_nonce(signer.account_id().clone());
let nonce = api.runtime_api().at(block_hash).call(nonce_call).await?;
let nonce_call = entropy::apis().account_nonce_api().account_nonce(signer.account_id().clone());
let nonce = api.runtime_api().at(block_hash).call(nonce_call).await?;

// TODO: Error handling really complex needs to be thought about.
confirm_jump_start(&api, rpc, sig_request_address, &signer, verifying_key, nonce).await?;
}
// TODO: Error handling really complex needs to be thought about.
confirm_jump_start(&api, rpc, &signer, verifying_key, nonce).await?;
Ok(())
}

/// Confirms that the network wide distributed key generation process has taken place.
pub async fn confirm_jump_start(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
who: SubxtAccountId32,
signer: &PairSigner<EntropyConfig, sr25519::Pair>,
verifying_key: Vec<u8>,
nonce: u32,
Expand All @@ -377,10 +362,6 @@ pub async fn confirm_jump_start(
// TODO: Understand this better, potentially use sign_and_submit_default
// or other method under sign_and_*

if who.encode() != NETWORK_PARENT_KEY.encode() {
return Err(UserErr::UnableToConfirmJumpStart);
}

let jump_start_request = entropy::tx().registry().confirm_jump_start(
entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec(verifying_key),
);
Expand Down Expand Up @@ -419,18 +400,11 @@ async fn validate_jump_start(
return Err(UserErr::StaleData);
}

let mut hasher_chain_data = Blake2s256::new();
hasher_chain_data.update(Some(chain_data.sig_request_accounts.clone()).encode());
let chain_data_hash = hasher_chain_data.finalize();
let mut hasher_verifying_data = Blake2s256::new();

// Check that the on-chain selected validators match those from the HTTP request
let verifying_data_query = entropy::storage().registry().jumpstart_dkg(chain_data.block_number);

let verifying_data = query_chain(api, rpc, verifying_data_query, None).await?;
hasher_verifying_data.update(verifying_data.encode());

let verifying_data_hash = hasher_verifying_data.finalize();
if verifying_data_hash != chain_data_hash {
let verifying_data = query_chain(api, rpc, verifying_data_query, None).await?.unwrap();
let verifying_data: Vec<_> = verifying_data.into_iter().map(|v| v.0).collect();
if verifying_data != chain_data.validators_info {
return Err(UserErr::InvalidData);
}

Expand Down
46 changes: 17 additions & 29 deletions crates/threshold-signature-server/src/user/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ use synedrion::{
k256::ecdsa::{RecoveryId, Signature as k256Signature, VerifyingKey},
AuxInfo, ThresholdKeyShare,
};
use tokio::select;
use tokio::{
io::{AsyncRead, AsyncReadExt},
task::JoinHandle,
Expand Down Expand Up @@ -612,7 +613,7 @@ async fn test_fails_to_sign_if_non_signing_group_participants_are_used() {
for res in test_user_bad_connection_res {
assert_eq!(
res.unwrap().text().await.unwrap(),
"{\"Err\":\"Oneshot timeout error: channel closed\"}"
"{\"Err\":\"Subscribe message rejected: NoListener(\\\"no listener\\\")\"}"
);
}

Expand Down Expand Up @@ -715,46 +716,33 @@ async fn test_jumpstart_network() {

let alice = AccountKeyring::Alice;

let cxt = test_context_stationary().await;
let add_parent_key = false;
let (_validator_ips, _validator_ids) =
spawn_testing_validators(false, ChainSpecType::Development).await;
let api = get_api(&cxt.node_proc.ws_url).await.unwrap();
let rpc = get_rpc(&cxt.node_proc.ws_url).await.unwrap();
spawn_testing_validators(add_parent_key, ChainSpecType::Integration).await;

let force_authoring = true;
let substrate_context = test_node_process_testing_state(force_authoring).await;

let api = get_api(&substrate_context.ws_url).await.unwrap();
let rpc = get_rpc(&substrate_context.ws_url).await.unwrap();

let client = reqwest::Client::new();

let block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number + 1;

let validators_info = vec![
entropy_shared::ValidatorInfo {
ip_address: b"127.0.0.1:3001".to_vec(),
x25519_public_key: X25519_PUBLIC_KEYS[0],
tss_account: TSS_ACCOUNTS[0].clone().encode(),
},
entropy_shared::ValidatorInfo {
ip_address: b"127.0.0.1:3002".to_vec(),
x25519_public_key: X25519_PUBLIC_KEYS[1],
tss_account: TSS_ACCOUNTS[1].clone().encode(),
},
entropy_shared::ValidatorInfo {
ip_address: b"127.0.0.1:3003".to_vec(),
x25519_public_key: X25519_PUBLIC_KEYS[2],
tss_account: TSS_ACCOUNTS[2].clone().encode(),
},
];
let onchain_user_request = OcwMessageDkg {
sig_request_accounts: vec![NETWORK_PARENT_KEY.encode()],
block_number,
validators_info,
};

put_jumpstart_request_on_chain(&api, &rpc, &alice).await;

run_to_block(&rpc, block_number + 1).await;

let selected_validators_query = entropy::storage().registry().jumpstart_dkg(block_number);
let validators_info =
query_chain(&api, &rpc, selected_validators_query, None).await.unwrap().unwrap();
let validators_info: Vec<_> = validators_info.into_iter().map(|v| v.0).collect();
let onchain_user_request = OcwMessageDkg { block_number, validators_info };

// succeeds
let response_results = join_all(
vec![3002, 3003]
vec![3002, 3003, 3004]
.iter()
.map(|port| {
client
Expand Down
16 changes: 2 additions & 14 deletions pallets/propagation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ pub mod pallet {
use codec::Encode;
use entropy_shared::{
OcwMessageAttestationRequest, OcwMessageDkg, OcwMessageProactiveRefresh, OcwMessageReshare,
ValidatorInfo,
};
use frame_support::{pallet_prelude::*, sp_runtime::traits::Saturating};
use frame_system::pallet_prelude::*;
Expand Down Expand Up @@ -110,7 +109,7 @@ pub mod pallet {
/// Submits a distributed key generation request to jumpstart the network to the threshold
/// servers.
pub fn post_dkg(block_number: BlockNumberFor<T>) -> Result<(), http::Error> {
let messages = pallet_registry::Pallet::<T>::jumpstart_dkg(
let validators_info = pallet_registry::Pallet::<T>::jumpstart_dkg(
block_number.saturating_sub(1u32.into()),
);

Expand All @@ -121,24 +120,13 @@ pub mod pallet {
let url =
str::from_utf8(&from_local).unwrap_or("http://localhost:3001/generate_network_key");

log::warn!("propagation::post::messages: {:?}", &messages);
log::warn!("propagation::post::validators_info: {:?}", &validators_info);
let converted_block_number: u32 =
BlockNumberFor::<T>::try_into(block_number).unwrap_or_default();
let servers_info =
pallet_registry::Pallet::<T>::get_validators_info().unwrap_or_default();
let validators_info = servers_info
.iter()
.map(|server_info| ValidatorInfo {
x25519_public_key: server_info.x25519_public_key,
ip_address: server_info.endpoint.clone(),
tss_account: server_info.tss_account.encode(),
})
.collect::<Vec<_>>();
// the data is serialized / encoded to Vec<u8> by parity-scale-codec::encode()
let req_body = OcwMessageDkg {
// subtract 1 from blocknumber since the request is from the last block
block_number: converted_block_number.saturating_sub(1),
sig_request_accounts: messages,
validators_info,
};

Expand Down
8 changes: 1 addition & 7 deletions pallets/propagation/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ fn knows_how_to_mock_several_http_calls() {
uri: "http://localhost:3001/generate_network_key".into(),
sent: true,
response: Some([].to_vec()),
body: [
0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 10, 32, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
11, 32, 4, 0, 0, 0, 0, 0, 0, 0,
]
.to_vec(),
body: [0, 0, 0, 0, 0].to_vec(),
..Default::default()
});

Expand Down
Loading

0 comments on commit 959ce98

Please sign in to comment.