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

Jumpstart network #918

Merged
merged 27 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ At the moment this project **does not** adhere to
- Add `blake2` as built in hash function and make `HashingAlgorithm` non-exhaustive ([#881](https://github.com/entropyxyz/entropy-core/pull/881))
- Add sort to subgroup signer selection ([#900](https://github.com/entropyxyz/entropy-core/pull/900))
- Create four node Docker Compose chainspec ([#902](https://github.com/entropyxyz/entropy-core/pull/902))
- Jumpstart network ([#922](https://github.com/entropyxyz/entropy-core/pull/922))
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved

### Changed
- Move TSS mnemonic out of keystore ([#853](https://github.com/entropyxyz/entropy-core/pull/853))
Expand Down
Binary file modified crates/client/entropy_metadata.scale
Binary file not shown.
49 changes: 37 additions & 12 deletions crates/threshold-signature-server/src/user/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use futures::{
use num::{bigint::BigInt, FromPrimitive, Num, ToPrimitive};
use parity_scale_codec::{Decode, DecodeAll, Encode};
use serde::{Deserialize, Serialize};
use sp_core::crypto::AccountId32;
use sp_core::{crypto::AccountId32, H256};
use subxt::{
backend::legacy::LegacyRpcMethods,
ext::sp_core::{crypto::Ss58Codec, sr25519, sr25519::Signature, Pair},
Expand Down Expand Up @@ -142,6 +142,11 @@ pub async fn sign_tx(
.number;

check_stale(user_sig_req.block_number, block_number).await?;
// Probably impossible but block signing from master key anyways
if user_sig_req.signature_verifying_key == H256::zero().0.to_vec() {
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved
return Err(UserErr::NoSigningFromMasterKey);
}

let user_details =
get_registered_details(&api, &rpc, user_sig_req.signature_verifying_key.clone()).await?;
check_hash_pointer_out_of_bounds(&user_sig_req.hash, user_details.programs_data.0.len())?;
Expand Down Expand Up @@ -307,7 +312,7 @@ async fn setup_dkg(
app_state: AppState,
) -> Result<(), UserErr> {
tracing::debug!("Preparing to execute DKG");

let master_key = H256::zero();
HCastano marked this conversation as resolved.
Show resolved Hide resolved
let subgroup = get_subgroup(&api, rpc, signer.account_id()).await?;
let stash_address = get_stash_address(&api, rpc, signer.account_id()).await?;
let mut addresses_in_subgroup = return_all_addresses_of_subgroup(&api, rpc, subgroup).await?;
Expand All @@ -325,21 +330,31 @@ async fn setup_dkg(
.try_into()
.map_err(|_| UserErr::AddressConversionError("Invalid Length".to_string()))?;
let sig_request_address = SubxtAccountId32(*address_slice);
let user_details =
get_registering_user_details(&api, &sig_request_address.clone(), rpc).await?;
let mut key_visibility = KeyVisibility::Public;
// If master key set visibility to public
if sig_request_account != master_key.encode() {
let user_details =
get_registering_user_details(&api, &sig_request_address.clone(), rpc).await?;
key_visibility = user_details.key_visibility.0;
};
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved

let key_share = do_dkg(
&data.validators_info,
&signer,
x25519_secret_key,
&app_state.listener_state,
sig_request_address.clone(),
*user_details.key_visibility,
key_visibility,
data.block_number,
)
.await?;
let verifying_key = key_share.verifying_key().to_encoded_point(true).as_bytes().to_vec();
let string_verifying_key = hex::encode(verifying_key.clone()).to_string();
let mut string_verifying_key = hex::encode(verifying_key.clone()).to_string();
HCastano marked this conversation as resolved.
Show resolved Hide resolved
// If jump start store key under the master key (zero address)
if sig_request_account == master_key.encode() {
string_verifying_key = hex::encode(master_key).to_string();
}
HCastano marked this conversation as resolved.
Show resolved Hide resolved

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

Expand Down Expand Up @@ -371,6 +386,7 @@ async fn setup_dkg(
&signer,
verifying_key,
nonce + i as u32,
master_key.encode(),
)
.await?;
}
Expand Down Expand Up @@ -492,6 +508,7 @@ pub async fn is_registering(
}

/// Confirms that a address has finished registering on chain.
#[allow(clippy::too_many_arguments)]
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved
pub async fn confirm_registered(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
Expand All @@ -500,17 +517,25 @@ pub async fn confirm_registered(
signer: &PairSigner<EntropyConfig, sr25519::Pair>,
verifying_key: Vec<u8>,
nonce: u32,
master_key: Vec<u8>,
) -> Result<(), UserErr> {
// TODO error handling + return error
// TODO fire and forget, or wait for in block maybe Ddos error
// TODO: Understand this better, potentially use sign_and_submit_default
// or other method under sign_and_*
let registration_tx = entropy::tx().registry().confirm_register(
who,
subgroup,
entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec(verifying_key),
);
submit_transaction(api, rpc, signer, &registration_tx, Some(nonce)).await?;

// If master key call jump_start_result
if master_key != who.encode() {
let tx_request = entropy::tx().registry().confirm_register(
who,
subgroup,
entropy::runtime_types::bounded_collections::bounded_vec::BoundedVec(verifying_key),
);
submit_transaction(api, rpc, signer, &tx_request, Some(nonce)).await?;
} else {
let tx_request = entropy::tx().registry().jump_start_results(subgroup);
submit_transaction(api, rpc, signer, &tx_request, Some(nonce)).await?;
}
Ok(())
}

Expand Down
2 changes: 2 additions & 0 deletions crates/threshold-signature-server/src/user/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ pub enum UserErr {
EncryptionOrAuthentication(#[from] EncryptedSignedMessageErr),
#[error("Custom hash choice out of bounds")]
CustomHashOutOfBounds,
#[error("No signing from master key")]
NoSigningFromMasterKey,
HCastano marked this conversation as resolved.
Show resolved Hide resolved
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved
#[error("Listener: {0}")]
Listener(#[from] entropy_protocol::errors::ListenerErr),
#[error("Error creating sr25519 keypair from seed: {0}")]
Expand Down
104 changes: 103 additions & 1 deletion crates/threshold-signature-server/src/user/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@ async fn test_sign_tx_no_chain() {
for res in test_user_custom_hash_out_of_bounds {
assert_eq!(res.unwrap().text().await.unwrap(), "Custom hash choice out of bounds");
}

generic_msg.block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number;
generic_msg.signature_verifying_key = H256::zero().0.to_vec();
let test_user_sign_with_master_key = submit_transaction_requests(
vec![validator_ips_and_keys[1].clone()],
generic_msg.clone(),
one,
)
.await;
for res in test_user_sign_with_master_key {
assert_eq!(res.unwrap().text().await.unwrap(), "No signing from master key");
}
clean_tests();
}

Expand Down Expand Up @@ -774,6 +786,82 @@ async fn test_store_share() {
clean_tests();
}

#[tokio::test]
#[serial]
async fn test_jumpstart_network() {
initialize_test_logger().await;
clean_tests();

let alice = AccountKeyring::Alice;

let cxt = test_context_stationary().await;
let (_validator_ips, _validator_ids, _) =
spawn_testing_validators(Some(DEFAULT_VERIFYING_KEY.to_vec()), false, false).await;
let api = get_api(&cxt.node_proc.ws_url).await.unwrap();
let rpc = get_rpc(&cxt.node_proc.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(),
},
];
let onchain_user_request = OcwMessageDkg {
sig_request_accounts: vec![H256::zero().encode()],
block_number,
validators_info,
};

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

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

// succeeds
let user_registration_response = client
.post("http://127.0.0.1:3002/user/new")
.body(onchain_user_request.clone().encode())
.send()
.await
.unwrap();

assert_eq!(user_registration_response.text().await.unwrap(), "");
// wait for jump start event check that key exists in kvdb
for _ in 0..45 {
std::thread::sleep(std::time::Duration::from_millis(1000));
let block_hash = rpc.chain_get_block_hash(None).await.unwrap();
let events = EventsClient::new(api.clone()).at(block_hash.unwrap()).await.unwrap();
let jump_start_event = events.find::<entropy::registry::events::JumpStartDone>();
for event in jump_start_event.flatten() {
dbg!(event);
JesseAbram marked this conversation as resolved.
Show resolved Hide resolved
break;
}
}

let get_query = UnsafeQuery::new(hex::encode(H256::zero()), [].to_vec()).to_json();
// check get key before registration to see if key gets replaced
let response_key = client
.post("http://127.0.0.1:3001/unsafe/get")
.header("Content-Type", "application/json")
.body(get_query.clone())
.send()
.await
.unwrap();
// check to make sure keyshare is correct
let key_share: Option<KeyShare<KeyParams>> =
entropy_kvdb::kv_manager::helpers::deserialize(&response_key.bytes().await.unwrap());
assert_eq!(key_share.is_some(), true);
clean_tests();
}

#[tokio::test]
#[serial]
async fn test_return_addresses_of_subgroup() {
Expand Down Expand Up @@ -1053,6 +1141,18 @@ pub async fn put_register_request_on_chain(
submit_transaction(api, rpc, &sig_req_account, &registering_tx, None).await.unwrap();
}

pub async fn put_jumpstart_request_on_chain(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
sig_req_keyring: &Sr25519Keyring,
) {
let sig_req_account =
PairSigner::<EntropyConfig, sp_core::sr25519::Pair>::new(sig_req_keyring.pair());

let registering_tx = entropy::tx().registry().jump_start_network();
submit_transaction(api, rpc, &sig_req_account, &registering_tx, None).await.unwrap();
}

#[tokio::test]
#[serial]
async fn test_sign_tx_user_participates() {
Expand Down Expand Up @@ -1927,7 +2027,7 @@ async fn test_mutiple_confirm_done() {
.await;

let (signer_alice, _) = get_signer_and_x25519_secret_from_mnemonic(DEFAULT_MNEMONIC).unwrap();

let master_key = H256::zero().encode();
confirm_registered(
&api,
&rpc,
Expand All @@ -1936,6 +2036,7 @@ async fn test_mutiple_confirm_done() {
&signer_alice,
DEFAULT_VERIFYING_KEY.to_vec(),
0u32,
master_key.clone(),
)
.await
.unwrap();
Expand All @@ -1947,6 +2048,7 @@ async fn test_mutiple_confirm_done() {
&signer_alice,
DEFAULT_VERIFYING_KEY.to_vec(),
1u32,
master_key,
)
.await
.unwrap();
Expand Down
61 changes: 61 additions & 0 deletions pallets/registry/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,67 @@ pub fn add_non_syncing_validators<T: Config>(
}

benchmarks! {
jump_start_network {

let sig_req_account: T::AccountId = whitelisted_caller();
let balance = <T as pallet_staking_extension::Config>::Currency::minimum_balance() * 100u32.into();
let _ = <T as pallet_staking_extension::Config>::Currency::make_free_balance_be(&sig_req_account, balance);

}: _(RawOrigin::Signed(sig_req_account.clone()))
verify {
assert_last_event::<T>(Event::NetworkJumpStarted().into());
}

jump_start_results_done {
let c in 0 .. SIG_PARTIES as u32;
let sig_req_account: T::AccountId = whitelisted_caller();
let validator_account: T::AccountId = whitelisted_caller();
let threshold_account: T::AccountId = whitelisted_caller();

let sig_party_size = MaxValidators::<T>::get() / SIG_PARTIES as u32;
// add validators and a registering user
for i in 0..SIG_PARTIES {
let validators = add_non_syncing_validators::<T>(sig_party_size, 0, i as u8);
<ThresholdToStash<T>>::insert(&threshold_account, &validators[i]);
}
<JumpStartProgress<T>>::put(JumpStartDetails {
jump_start_status: JumpStartStatus::InProgress(0),
confirmations: vec![1],
});


let balance = <T as pallet_staking_extension::Config>::Currency::minimum_balance() * 100u32.into();
let _ = <T as pallet_staking_extension::Config>::Currency::make_free_balance_be(&threshold_account, balance);
}: jump_start_results(RawOrigin::Signed(threshold_account), 0)
verify {
assert_last_event::<T>(Event::<T>::JumpStartDone().into());
}

jump_start_results_confirm {
let c in 0 .. SIG_PARTIES as u32;
let sig_req_account: T::AccountId = whitelisted_caller();
let validator_account: T::AccountId = whitelisted_caller();
let threshold_account: T::AccountId = whitelisted_caller();

let sig_party_size = MaxValidators::<T>::get() / SIG_PARTIES as u32;
// add validators and a registering user
for i in 0..SIG_PARTIES {
let validators = add_non_syncing_validators::<T>(sig_party_size, 0, i as u8);
<ThresholdToStash<T>>::insert(&threshold_account, &validators[i]);
}
<JumpStartProgress<T>>::put(JumpStartDetails {
jump_start_status: JumpStartStatus::InProgress(0),
confirmations: vec![],
});


let balance = <T as pallet_staking_extension::Config>::Currency::minimum_balance() * 100u32.into();
let _ = <T as pallet_staking_extension::Config>::Currency::make_free_balance_be(&threshold_account, balance);
}: jump_start_results(RawOrigin::Signed(threshold_account), 0)
verify {
assert_last_event::<T>(Event::<T>::JumpStartConfirmation(0).into());
}

register {
let p in 1 .. T::MaxProgramHashes::get();
let program = vec![0u8];
Expand Down
Loading