Skip to content
Merged
Show file tree
Hide file tree
Changes from 95 commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
02dd798
make the two threshold DTO conversions fallible & validating
SimonRastikian Jun 15, 2026
44e9e76
Adding validation (upper cap and wrt the ReconstructionThreshold)
SimonRastikian Jun 15, 2026
7d86856
Updating tests now GovThreshold is capped at 80% no more allowed the …
SimonRastikian Jun 15, 2026
b33f455
Panic in migration misconfig
SimonRastikian Jun 15, 2026
02c5313
Remove migration function after migration
SimonRastikian Jun 15, 2026
a378c56
Raising the number of participants in the test to meet the 80% upper cap
SimonRastikian Jun 16, 2026
3da1652
Raising extra tests number of particpants to meet the 80% cap
SimonRastikian Jun 16, 2026
aeed6e7
Adding note documentation about cases where the threshold is a single…
SimonRastikian Jun 16, 2026
7257a16
Using already existent helpers in vote_add_domains
SimonRastikian Jun 16, 2026
a20a7a8
Bump the number of participants for parallel_sign_calls to meet 80% u…
SimonRastikian Jun 16, 2026
1afd958
make process_new_parameters_proposal self-contained
SimonRastikian Jun 16, 2026
db47b85
Removing redundant function
SimonRastikian Jun 16, 2026
0cf1b53
Obsolete test as the check happens in the underlying function
SimonRastikian Jun 16, 2026
27e40bf
make function private
SimonRastikian Jun 16, 2026
7878ac8
Fixing tests with govthreshold = number of participants
SimonRastikian Jun 16, 2026
1f28966
More participants during resharing
SimonRastikian Jun 16, 2026
7bca4d8
Raising the number of participants to 7
SimonRastikian Jun 16, 2026
263bc8d
Using governance_threshold_lower_bound and governance_threshold_upper…
SimonRastikian Jun 16, 2026
f45ebf5
cargo fmt
SimonRastikian Jun 16, 2026
c8fec49
reducing code redundancy
SimonRastikian Jun 16, 2026
6e1ce47
Some more redundancy reduction
SimonRastikian Jun 16, 2026
5056109
Merge branch 'main' into 3499-correlate-governancethreshold-with-reco…
SimonRastikian Jun 16, 2026
5f5af82
Disable 80% governance upper cap (set MAX_THRESHOLD_NUMERATOR = denom…
SimonRastikian Jun 16, 2026
fbf22be
Merge branch '3499-correlate-governancethreshold-with-reconstructiont…
SimonRastikian Jun 16, 2026
b310e84
delimiter fix
SimonRastikian Jun 16, 2026
ca8b199
Merge branch '3499-correlate-governancethreshold-with-reconstructiont…
SimonRastikian Jun 16, 2026
da2d190
Update crates/contract/src/v3_11_2_state.rs
SimonRastikian Jun 16, 2026
7458432
using ReconstructionThreshold as u64 and using checked arithmetic
SimonRastikian Jun 16, 2026
4f52bdd
Using 1 as values
SimonRastikian Jun 16, 2026
29613b8
editting doc to make fast ci check pass
SimonRastikian Jun 16, 2026
3b329e2
collapsing if statement
SimonRastikian Jun 16, 2026
f168468
Reducing verbosity of the upper/lower bounds functions
SimonRastikian Jun 17, 2026
cd557e1
nit
SimonRastikian Jun 17, 2026
e769a53
fixing conflict
SimonRastikian Jun 19, 2026
df26736
modifying the design doc
SimonRastikian Jun 19, 2026
876bcfe
Improving upper cap paragraph
SimonRastikian Jun 19, 2026
fc57a33
cargo fmt
SimonRastikian Jun 19, 2026
484d40e
calling function in crate
SimonRastikian Jun 19, 2026
ec871da
No need for variable
SimonRastikian Jun 19, 2026
ee76a11
no panic
SimonRastikian Jun 19, 2026
6a4fa9e
deterministic test
SimonRastikian Jun 19, 2026
0ef09e5
explaining relative to the participant count
SimonRastikian Jun 19, 2026
bc928d4
cargo fmt
SimonRastikian Jun 19, 2026
e3d6c89
A good pass over the node integration to the robust ecdsa
SimonRastikian Jun 22, 2026
2c690bd
Merge branch 'main' into 3164-adding-ReconstructionThreshold-in-node
SimonRastikian Jun 22, 2026
86bbbd2
Merging with main
SimonRastikian Jun 23, 2026
158c838
Fixing problems post conflict
SimonRastikian Jun 23, 2026
6b08b91
fixing running.rs calls
SimonRastikian Jun 23, 2026
fa67289
updating design document
SimonRastikian Jun 23, 2026
5c9a3fe
Compacting a comment
SimonRastikian Jun 23, 2026
be0b517
reconstruction threshold
SimonRastikian Jun 23, 2026
db5692e
more reconstruction threshold
SimonRastikian Jun 23, 2026
41184ee
Add tests
SimonRastikian Jun 23, 2026
cb5725f
cargo commit
SimonRastikian Jun 23, 2026
02e0d13
Removing a todo
SimonRastikian Jun 23, 2026
eed4c8f
checked arithmetics
SimonRastikian Jun 23, 2026
8423f2e
adjusting comment
SimonRastikian Jun 23, 2026
79f9666
blocking claude bug
SimonRastikian Jun 23, 2026
05bdcfa
populating the hashmap with a domain instead of nothing
SimonRastikian Jun 23, 2026
fb858bd
Add metric for peer presign requests with mismatched participant count
SimonRastikian Jun 23, 2026
50cf59e
Thread old reconstruction threshold through key resharing and bubble …
SimonRastikian Jun 23, 2026
49b39a7
Revert reconstruction_threshold rename (moved to follow-up branch)
SimonRastikian Jun 23, 2026
3c3912f
Reducing code redundancy
SimonRastikian Jun 25, 2026
9041366
Merge branch 'main' of github.com:near/mpc
SimonRastikian Jun 25, 2026
aa1b927
Reducing code redundancy
SimonRastikian Jun 25, 2026
1597647
reducing more code redundancy
SimonRastikian Jun 25, 2026
5055b0e
Reducing more code redundancy. Making must_get_domain more generic
SimonRastikian Jun 25, 2026
5f71e41
Improving comment
SimonRastikian Jun 25, 2026
4874fb3
no more robust ECDSA comments
SimonRastikian Jun 25, 2026
6c535c5
reducing even more code redundancy by introducing damgard_etal_domain…
SimonRastikian Jun 25, 2026
446f162
Reducing drastically redundancy discovered similar to the ones in e2e…
SimonRastikian Jun 25, 2026
1395243
Using helper
SimonRastikian Jun 30, 2026
37cf136
compacting the comments
SimonRastikian Jun 30, 2026
392549d
introducing ! and reducing code redundancy
SimonRastikian Jun 30, 2026
3d94416
cargo fmt
SimonRastikian Jun 30, 2026
71392e0
mentioning cait sith in error
SimonRastikian Jun 30, 2026
a0c3637
Switching aliases
SimonRastikian Jun 30, 2026
7c6164b
Some more aliases sync fix
SimonRastikian Jun 30, 2026
d80827e
comment reverted
SimonRastikian Jun 30, 2026
fad6726
deleting as usage
SimonRastikian Jun 30, 2026
5792061
Reducing immensly code redundancy by unifying underlying providers fo…
SimonRastikian Jun 30, 2026
0b8b201
Fixing todo #1680
SimonRastikian Jul 1, 2026
4b2f853
Deleting non-necessary comment
SimonRastikian Jul 1, 2026
f89e30d
Removing non-necessary comment
SimonRastikian Jul 1, 2026
766fef6
Moving the compute_thresholds function to robust_ecdsa.rs
SimonRastikian Jul 1, 2026
d759fc8
per domain data assigning each domain with reconstruction threshold test
SimonRastikian Jul 1, 2026
7733fd0
Making the comment more accurate
SimonRastikian Jul 1, 2026
9cc8741
Adding the reconstruction threshold in the domain registry
SimonRastikian Jul 1, 2026
954a69a
Improving output to prevent misleading understanding
SimonRastikian Jul 1, 2026
18e1ed1
Adding extra migration tests
SimonRastikian Jul 1, 2026
a2cc899
Reshare keys to the updated reconstruction threshold
SimonRastikian Jul 1, 2026
aac0973
Add node tests for changing reconstruction threshold via resharing
SimonRastikian Jul 1, 2026
ec959e4
cargo fmt
SimonRastikian Jul 1, 2026
955e669
receiving the new threshold from the contract state at the indexer level
SimonRastikian Jul 3, 2026
e89f261
Unit tests
SimonRastikian Jul 3, 2026
19f1ca5
fix conflict
SimonRastikian Jul 3, 2026
133ba43
reverting comment on the SC to prevent having to upgrade the SC
SimonRastikian Jul 6, 2026
6b4107e
Merge branch 'main' of github.com:near/mpc
SimonRastikian Jul 6, 2026
80de0a0
Merge branch 'main' of github.com:near/mpc
SimonRastikian Jul 10, 2026
2481a55
Updating design doc and getting rid of translate_threshold
SimonRastikian Jul 13, 2026
feaa80a
No silent failing using join_all
SimonRastikian Jul 13, 2026
46342fa
Removing unecessary function and instead using helper
SimonRastikian Jul 13, 2026
1905cd5
Fixing conflict
SimonRastikian Jul 13, 2026
857ac6f
Updating comments
SimonRastikian Jul 13, 2026
2066927
Renaming PerDomainData into Keyshare
SimonRastikian Jul 13, 2026
d22f3c1
Generalizing the struct DomainKeyshare
SimonRastikian Jul 13, 2026
aeedac8
Reducing code redundancy
SimonRastikian Jul 13, 2026
1ec15b5
comment rework
SimonRastikian Jul 13, 2026
6b119e8
Barak's commit
SimonRastikian Jul 14, 2026
98b5c73
tracking with domain id
SimonRastikian Jul 14, 2026
6eae2be
Switching from generic trait to C:Ciphersuite
SimonRastikian Jul 14, 2026
86aa75e
Switching to set and unifying triple-threshold interface
SimonRastikian Jul 14, 2026
b2bc11a
Using default blocktime instead of 600ms
SimonRastikian Jul 14, 2026
cb69750
cargo fmt
SimonRastikian Jul 14, 2026
aec2f59
Merge branch 'main' into 3164-adding-ReconstructionThreshold-in-node
SimonRastikian Jul 14, 2026
19954d8
input EcdsaKeyshare in run_background_presignature_generation
SimonRastikian Jul 14, 2026
996d9a3
Merge branch '3164-adding-ReconstructionThreshold-in-node' of github.…
SimonRastikian Jul 14, 2026
06a9a47
defining constant for 500 ms
SimonRastikian Jul 14, 2026
a83a1f2
consistent bail
SimonRastikian Jul 14, 2026
87c2d07
comment returning first domain
SimonRastikian Jul 14, 2026
5c53876
folding the threshold extraction into convert_key_event_to_instance
SimonRastikian Jul 14, 2026
d416017
Adding an .unwrap_or instead of of keeping things seemingly missed
SimonRastikian Jul 14, 2026
0156843
Killing nodes for tests
SimonRastikian Jul 14, 2026
316d15b
Merge branch 'main' into 3164-adding-ReconstructionThreshold-in-node
SimonRastikian Jul 14, 2026
bc8bc72
Reverting the join_all silent change and making a new PR for it
SimonRastikian Jul 15, 2026
75b82c4
Unnecessary commiti
SimonRastikian Jul 16, 2026
6e5dfce
Adding tests for the other schemes
SimonRastikian Jul 16, 2026
c3269ea
Fixing conflicts
SimonRastikian Jul 16, 2026
17d649e
cargo fmt
SimonRastikian Jul 16, 2026
96535d6
Fixing deeper issues following merge with main
SimonRastikian Jul 16, 2026
9ca92e1
Removing a test by extending another
SimonRastikian Jul 16, 2026
e350511
test: remove redundant static multidomain reconstruction-threshold te…
SimonRastikian Jul 16, 2026
5d3fedc
Merge branch 'main' into 3164-adding-ReconstructionThreshold-in-node
SimonRastikian Jul 16, 2026
9231d99
signing for all domains instead of all schemes
SimonRastikian Jul 17, 2026
4ab6098
Using variables
SimonRastikian Jul 17, 2026
3e3af3b
Solving conflicts
SimonRastikian Jul 21, 2026
da56e05
Removing debug_assert_eq
SimonRastikian Jul 21, 2026
97a24ae
Deleting useless input in struct
SimonRastikian Jul 21, 2026
c09f549
Less aggressive timing
SimonRastikian Jul 21, 2026
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
46 changes: 15 additions & 31 deletions crates/e2e-tests/tests/ckd_verification.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::common;
use crate::common::{
CKD_PV_VERIFICATION_PORT_SEED, CKD_VERIFICATION_PORT_SEED, must_get_bls_public_key,
must_get_domain, must_setup_cluster,
};

use anyhow::Context;
use blstrs::{G1Projective, G2Projective, Scalar};
Expand All @@ -7,8 +10,7 @@ use group::ff::Field as _;
use near_account_id::AccountId;
use near_mpc_contract_interface::types::kdf::derive_app_id;
use near_mpc_contract_interface::types::{
Bls12381G1PublicKey, Bls12381G2PublicKey, CKDAppPublicKey, CKDAppPublicKeyPV, Curve,
DomainPurpose,
Bls12381G1PublicKey, Bls12381G2PublicKey, CKDAppPublicKey, CKDAppPublicKeyPV, Protocol,
};
use rand::SeedableRng;
use threshold_signatures::confidential_key_derivation::{
Expand Down Expand Up @@ -42,20 +44,11 @@ fn verify_ckd(
#[expect(non_snake_case)]
async fn ckd_response__passes_cryptographic_verification() {
// given
let (cluster, running) =
common::must_setup_cluster(common::CKD_VERIFICATION_PORT_SEED, |_| {}).await;

let bls_domain = running
.domains
.domains
.iter()
.find(|d| {
Curve::from(d.protocol) == Curve::Bls12381 && matches!(d.purpose, DomainPurpose::CKD)
})
.expect("no Bls12381 CKD domain found")
.clone();

let mpc_pk = common::must_get_bls_public_key(&running, bls_domain.id);
let (cluster, running) = must_setup_cluster(CKD_VERIFICATION_PORT_SEED, |_| {}).await;

let bls_domain = must_get_domain(&running, Protocol::ConfidentialKeyDerivation);

let mpc_pk = must_get_bls_public_key(&running, bls_domain.id);
Comment thread
SimonRastikian marked this conversation as resolved.
let user = cluster.default_user_account().clone();

let mut rng = rand::rngs::StdRng::seed_from_u64(1);
Expand Down Expand Up @@ -95,20 +88,11 @@ async fn ckd_response__passes_cryptographic_verification() {
#[expect(non_snake_case)]
async fn ckd_pv_response__passes_cryptographic_verification() {
// given
let (cluster, running) =
common::must_setup_cluster(common::CKD_PV_VERIFICATION_PORT_SEED, |_| {}).await;

let bls_domain = running
.domains
.domains
.iter()
.find(|d| {
Curve::from(d.protocol) == Curve::Bls12381 && matches!(d.purpose, DomainPurpose::CKD)
})
.expect("no Bls12381 CKD domain found")
.clone();

let mpc_pk = common::must_get_bls_public_key(&running, bls_domain.id);
let (cluster, running) = must_setup_cluster(CKD_PV_VERIFICATION_PORT_SEED, |_| {}).await;

let bls_domain = must_get_domain(&running, Protocol::ConfidentialKeyDerivation);
Comment thread
SimonRastikian marked this conversation as resolved.

let mpc_pk = must_get_bls_public_key(&running, bls_domain.id);
let user = cluster.default_user_account().clone();

let mut rng = rand::rngs::StdRng::seed_from_u64(2);
Expand Down
56 changes: 54 additions & 2 deletions crates/e2e-tests/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use blstrs::{G1Projective, Scalar};
use e2e_tests::{CLUSTER_WAIT_TIMEOUT, MpcCluster, MpcClusterConfig, metrics};
use group::Group;
use near_mpc_contract_interface::types::{
Bls12381G2PublicKey, CKDAppPublicKey, Curve, DomainId, DomainPurpose, ProtocolContractState,
PublicKey, PublicKeyExtended, RunningContractState,
Bls12381G2PublicKey, CKDAppPublicKey, Curve, DomainConfig, DomainId, DomainPurpose, Protocol,
ProtocolContractState, PublicKey, PublicKeyExtended, ReconstructionThreshold,
RunningContractState,
};
use near_mpc_crypto_types::Bls12381G1PublicKey;
use serde_json::json;
Expand Down Expand Up @@ -36,6 +37,7 @@ pub const CONTRACT_UPGRADE_COMPATIBILITY_TESTNET_PORT_SEED: u16 = 19;
pub const TIMEOUT_METRIC_PORT_SEED: u16 = 20;
pub const MIGRATION_BACK_PORT_SEED: u16 = 21;
pub const SIGTERM_HANDLER_PORT_SEED: u16 = 22;
pub const DISTINCT_RECONSTRUCTION_THRESHOLDS_PORT_SEED: u16 = 23;

/// Start a cluster, wait for Running state and presignatures to buffer.
///
Expand Down Expand Up @@ -376,6 +378,28 @@ pub fn must_get_bls_public_key(
}
}

/// Builds a `DamgardEtAl` signing domain with reconstruction threshold `t`, which needs `2t - 1` signers.
pub fn damgard_etal_domain(id: u64, t: u64) -> DomainConfig {
DomainConfig {
id: DomainId(id),
protocol: Protocol::DamgardEtAl,
reconstruction_threshold: ReconstructionThreshold::new(t),
purpose: DomainPurpose::Sign,
}
}

/// Returns the domain running `protocol_type`, panicking if absent. Each
/// protocol appears at most once per registry, so it identifies a unique domain.
Comment thread
SimonRastikian marked this conversation as resolved.
Outdated
pub fn must_get_domain(running: &RunningContractState, protocol_type: Protocol) -> DomainConfig {
running
.domains
.domains
.iter()
.find(|d| d.protocol == protocol_type)
.unwrap_or_else(|| panic!("no domain with protocol {protocol_type:?}"))
.clone()
}

/// Send a sign request and assert the network produced a successful response.
///
/// Panics if the request can't be submitted to the contract — the test cannot
Expand Down Expand Up @@ -406,6 +430,34 @@ pub async fn send_sign_request(
Ok(())
}

/// Sign with every scheme in `running`, asserting each request succeeds.
pub async fn sign_all_schemes(
cluster: &MpcCluster,
running: &RunningContractState,
rng: &mut impl rand::Rng,
) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: the doc comment here is useless, as it just paraphrases the function name.

non-nit: I think instead of signing for all schemes, we may want to sign for all domains (that do signatures) in the contract?

Such that, in case we have two cait-sith domains, they both get a request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@SimonRastikian I don't think this comment was addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the miss, there was a lot of comments

for (label, protocol) in [
("ECDSA", Protocol::CaitSith),
("Damgard et al", Protocol::DamgardEtAl),
("EdDSA", Protocol::Frost),
] {
let domain = must_get_domain(running, protocol);
let payload = match Curve::from(protocol) {
Curve::Edwards25519 => generate_eddsa_payload(rng),
_ => generate_ecdsa_payload(rng),
};
let outcome = cluster
.send_sign_request(domain.id, payload, cluster.default_user_account())
.await
.expect("sign request failed");
assert!(
outcome.is_success(),
"{label} sign request failed: {:?}",
outcome.failure_message()
);
}
}

/// Send a CKD request and assert the network produced a successful response.
///
/// Panics if the request can't be submitted to the contract — the test cannot
Expand Down
50 changes: 50 additions & 0 deletions crates/e2e-tests/tests/distinct_reconstruction_thresholds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::common::{
DISTINCT_RECONSTRUCTION_THRESHOLDS_PORT_SEED, damgard_etal_domain, generate_ckd_app_public_key,
must_get_domain, must_setup_cluster, sign_all_schemes,
};

use near_mpc_contract_interface::types::Protocol;
use rand::SeedableRng;

/// Each domain signs using its own reconstruction threshold rather than the
/// governance threshold. The governance threshold is 4 with a total of 6 nodes.
/// The signing procedure succeeds despite Damgard et al. requiring at least 7
/// participants under a governance-threshold signing model.
/// Therefore, signing is performed using the reconstruction threshold,
/// not the governance threshold.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

although this is correct, it is a very indirect way to check that the per domain thresholds are being used. Here we just need to check that the signatures are produced correctly, integration tests in the node should be able to figure that the correct threshold is used. Therefore this part of the comment can be removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would still vote to keep it as it is indeed part of the logic of the test. But I guess your comment anw was not a blocker

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

although this is correct, it is a very indirect way to check that the per domain thresholds are being used.

A more direct approach for testing this would be to shut down some of the nodes.

E.g. you have governance and cat-sith threshold of 6, but set damgard-et-al reconstruction threshold to 3 (e.g. signing threshold of 5), then you bring down all but 5 nodes, now, we expect cait-sith signatures to fail, but damgard to be successful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kevindeforth I killed nodes in the test 0156843

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does this sound good to you?

#[tokio::test]
#[expect(non_snake_case)]
async fn distinct_reconstruction_thresholds__should_sign_for_every_scheme() {
// Given
let (cluster, contract_state) =
must_setup_cluster(DISTINCT_RECONSTRUCTION_THRESHOLDS_PORT_SEED, |c| {
c.num_nodes = 6;
c.initial_participant_indices = (0..6).collect();
c.threshold = 4;
c.triples_to_buffer = 2;
c.presignatures_to_buffer = 2;
Comment on lines +26 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldn't triples be at least 4 in this case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it's fine here as is. You need 2 triples per presignature and then you have a background triple generation process that continuously refills the buffer to 2 triples.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we usually put enough triples in the buffer for all the signatures in the test to try to avoid flakiness as much as possible.

c.domains
.push(damgard_etal_domain(c.domains.len() as u64, 3));
})
.await;

let ckd_domain = must_get_domain(&contract_state, Protocol::ConfidentialKeyDerivation);

// When / Then
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
Comment thread
SimonRastikian marked this conversation as resolved.
Outdated
sign_all_schemes(&cluster, &contract_state, &mut rng).await;

let outcome = cluster
.send_ckd_request(
ckd_domain.id,
generate_ckd_app_public_key(&mut rng),
cluster.default_user_account(),
)
.await
.expect("ckd request failed");
assert!(
outcome.is_success(),
"ckd request failed: {:?}",
outcome.failure_message()
);
}
1 change: 1 addition & 0 deletions crates/e2e-tests/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod ckd_verification;
mod cleanup_lagging_node;
mod common;
mod contract_upgrade_compatibility;
mod distinct_reconstruction_thresholds;
mod foreign_chain_configuration;
mod foreign_chain_tx_validation;
mod key_resharing;
Expand Down
9 changes: 2 additions & 7 deletions crates/e2e-tests/tests/parallel_sign_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use near_mpc_contract_interface::types::{
};
use serde_json::json;

/// 9 parallel calls (3 robust ECDSA + 2 ECDSA + 2 EdDSA + 2 CKD) via the test parallel
/// 9 parallel calls (3 DamgardEtAl + 2 ECDSA + 2 EdDSA + 2 CKD) via the test parallel
/// contract, against a 6-node / threshold-5 cluster that carries all four signing-scheme
/// domains. Verifies all calls succeed and both the signature and CKD queues drain.
#[tokio::test]
Expand All @@ -25,12 +25,7 @@ async fn mpc_cluster_should_successfully_process_parallel_requests() {
c.initial_participant_indices = (0..6).collect();
c.threshold = 5;
c.domains = vec![
DomainConfig {
id: DomainId(0),
protocol: Protocol::DamgardEtAl,
reconstruction_threshold: ReconstructionThreshold::new(3),
purpose: DomainPurpose::Sign,
},
common::damgard_etal_domain(0, 3),
Comment thread
SimonRastikian marked this conversation as resolved.
DomainConfig {
id: DomainId(1),
protocol: Protocol::CaitSith,
Expand Down
88 changes: 18 additions & 70 deletions crates/e2e-tests/tests/request_during_resharing.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,33 @@
use crate::common;

use mpc_primitives::domain::{Curve, DomainId};
use near_mpc_contract_interface::types::{
DomainConfig, DomainPurpose, Protocol, ProtocolContractState, ReconstructionThreshold,
use crate::common::{
REQUEST_DURING_RESHARING_PORT_SEED, damgard_etal_domain, generate_ckd_app_public_key,
must_get_domain, must_setup_cluster, sign_all_schemes,
};

use near_mpc_contract_interface::types::{Protocol, ProtocolContractState};
use rand::SeedableRng;

/// Tests that signature and CKD requests are processed using the previous
/// running state's threshold while resharing is in progress.
///
/// Setup: 6 nodes, 5 initial participants (threshold 5). Domains cover
/// classic ECDSA (CaitSith), robust ECDSA (DamgardEtAl), EdDSA (Frost) and
/// CKD (ConfidentialKeyDerivation). Threshold is 5 because robust ECDSA
/// requires ≥ 5 signers (see `robust_ecdsa::translate_threshold`). Begin
/// resharing to all 6 with threshold 6, then kill node 5 so resharing can't
/// complete. Requests should still succeed using the old threshold of 5
/// across all signing schemes.
/// classic ECDSA (CaitSith), DamgardEtAl, EdDSA (Frost) and
/// CKD (ConfidentialKeyDerivation). The DamgardEtAl domain uses a
/// reconstruction threshold of `t = 3`, which requires `2t - 1 = 5` signers,
/// so we need at least 5 participants. Begin resharing to all 6 with threshold
/// 6, then kill node 5 so resharing can't complete. Requests should still
/// succeed using the previous running state across all signing schemes.
#[tokio::test]
async fn test_request_during_resharing() {
// given
let (mut cluster, contract_state) =
common::must_setup_cluster(common::REQUEST_DURING_RESHARING_PORT_SEED, |c| {
must_setup_cluster(REQUEST_DURING_RESHARING_PORT_SEED, |c| {
c.num_nodes = 6;
c.initial_participant_indices = (0..5).collect();
c.threshold = 5;
c.triples_to_buffer = 2;
c.presignatures_to_buffer = 2;
c.domains.push(DomainConfig {
id: DomainId(c.domains.len() as u64),
protocol: Protocol::DamgardEtAl,
reconstruction_threshold: ReconstructionThreshold::new(3),
purpose: DomainPurpose::Sign,
});
c.domains
.push(damgard_etal_domain(c.domains.len() as u64, 3));
Comment thread
SimonRastikian marked this conversation as resolved.
})
.await;

Expand All @@ -46,66 +42,18 @@ async fn test_request_during_resharing() {
cluster.kill_nodes(&[5]).expect("failed to kill node 5");

// then
let ecdsa_domain = contract_state
.domains
.domains
.iter()
.find(|d| {
Curve::from(d.protocol) == Curve::Secp256k1
&& d.protocol == Protocol::CaitSith
&& d.purpose == DomainPurpose::Sign
})
.expect("no CaitSith Sign domain");
let robust_ecdsa_domain = contract_state
.domains
.domains
.iter()
.find(|d| d.protocol == Protocol::DamgardEtAl && d.purpose == DomainPurpose::Sign)
.expect("no DamgardEtAl Sign domain");
let eddsa_domain = contract_state
.domains
.domains
.iter()
.find(|d| {
Curve::from(d.protocol) == Curve::Edwards25519 && d.purpose == DomainPurpose::Sign
})
.expect("no Edwards25519 Sign domain");
let ckd_domain = contract_state
.domains
.domains
.iter()
.find(|d| d.purpose == DomainPurpose::CKD)
.expect("no CKD domain");
let ckd_domain = must_get_domain(&contract_state, Protocol::ConfidentialKeyDerivation);

let mut rng = rand::rngs::StdRng::seed_from_u64(0);
for i in 0..3 {
for (label, domain_id, is_eddsa) in [
("ECDSA", ecdsa_domain.id, false),
("robust ECDSA", robust_ecdsa_domain.id, false),
("EdDSA", eddsa_domain.id, true),
] {
let payload = if is_eddsa {
common::generate_eddsa_payload(&mut rng)
} else {
common::generate_ecdsa_payload(&mut rng)
};
tracing::info!(i, label, "sending sign request during resharing");
let outcome = cluster
.send_sign_request(domain_id, payload, cluster.default_user_account())
.await
.expect("sign request failed");
assert!(
outcome.is_success(),
"{label} sign request {i} failed: {:?}",
outcome.failure_message()
);
}
tracing::info!(i, "sending sign requests during resharing");
sign_all_schemes(&cluster, &contract_state, &mut rng).await;

tracing::info!(i, "sending CKD request during resharing");
let outcome = cluster
.send_ckd_request(
ckd_domain.id,
common::generate_ckd_app_public_key(&mut rng),
generate_ckd_app_public_key(&mut rng),
cluster.default_user_account(),
)
.await
Expand Down
Loading
Loading