Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select validators for jumpstart DKG #1053

Merged
merged 32 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c515a98
Select validators based on block number when doing initial DKG
ameba23 Sep 13, 2024
8c198a5
Error handling
ameba23 Sep 13, 2024
7f64bbd
Tidy
ameba23 Sep 13, 2024
6c493ed
Comments
ameba23 Sep 13, 2024
3da3e69
Select jumpstart validators when handling jumpstart extrinsic
ameba23 Sep 16, 2024
c2534db
OcwMessageDkg no longer includes signature request account
ameba23 Sep 16, 2024
960ad7d
Update propagation pallet
ameba23 Sep 16, 2024
bb69aec
Update metadata
ameba23 Sep 16, 2024
44e7f51
DKG session id no longer contains signature request account
ameba23 Sep 16, 2024
02075b3
Handle validating selected validators on jumpstart on TSS
ameba23 Sep 16, 2024
42ceb07
Lockfile
ameba23 Sep 16, 2024
9c52270
Tidy
ameba23 Sep 16, 2024
35b7068
Tests for registry pallet
ameba23 Sep 16, 2024
5235481
Tidy
ameba23 Sep 16, 2024
bf0a608
Add alloc feature to rand
ameba23 Sep 16, 2024
4fa98a4
Update entropy-protocol tests
ameba23 Sep 16, 2024
ad8702b
Update registry benchmarks
ameba23 Sep 16, 2024
a8c96e3
Recalculate registry weights
ameba23 Sep 16, 2024
7e026e3
Update test helper for mocking jumpstart
ameba23 Sep 16, 2024
b78f6b7
Fix jumpstart network test
ameba23 Sep 16, 2024
dde7d20
Fix registry pallet tests
ameba23 Sep 16, 2024
87e9a82
Update TSS bad siging participant test
ameba23 Sep 16, 2024
a9f06e2
Update propagation pallet test
ameba23 Sep 17, 2024
a12525f
Changelog
ameba23 Sep 17, 2024
cad4347
Update registry weights
ameba23 Sep 17, 2024
5ecf047
Merge master
ameba23 Sep 17, 2024
cd5df2d
Update registry benchmarks
ameba23 Sep 18, 2024
851e329
Add comment following review
ameba23 Sep 18, 2024
e22f902
Fix getting newest value from jumpstart dkg storage map and add test
ameba23 Sep 18, 2024
19457cf
Update pallets/registry/src/benchmarking.rs
HCastano Sep 18, 2024
243ddd9
Comment following review
ameba23 Sep 19, 2024
bebd86b
Merge branch 'peg/select-initial-validators' of github.com:entropyxyz…
ameba23 Sep 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we don't know which validators are going to be picked, but we want all those who are picked to send confirmations. Since nothing bad happens if the wrong one confirms, we just have them all send confirmations here.

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
Loading