2018-07-16 06:31:21 -07:00
|
|
|
#![deny(unused_must_use)]
|
2018-06-20 01:11:33 -07:00
|
|
|
//! Tests for synchronous distributed key generation.
|
|
|
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
2019-08-25 05:41:50 -07:00
|
|
|
use hbbft::crypto::SecretKey;
|
|
|
|
use hbbft::sync_key_gen::{to_pub_keys, PartOutcome, SyncKeyGen};
|
2018-11-22 04:00:02 -08:00
|
|
|
use hbbft::util;
|
2018-06-20 01:11:33 -07:00
|
|
|
|
|
|
|
fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
|
|
|
|
// Generate individual key pairs for encryption. These are not suitable for threshold schemes.
|
2018-08-06 07:53:37 -07:00
|
|
|
let sec_keys: Vec<SecretKey> = (0..node_num).map(|_| SecretKey::random()).collect();
|
2019-08-25 05:41:50 -07:00
|
|
|
let pub_keys = to_pub_keys(sec_keys.iter().enumerate());
|
2018-06-20 01:11:33 -07:00
|
|
|
|
|
|
|
// Create the `SyncKeyGen` instances and initial proposals.
|
|
|
|
let mut nodes = Vec::new();
|
|
|
|
let proposals: Vec<_> = sec_keys
|
|
|
|
.into_iter()
|
|
|
|
.enumerate()
|
2018-06-25 04:07:31 -07:00
|
|
|
.map(|(id, sk)| {
|
2018-10-02 07:24:51 -07:00
|
|
|
let (sync_key_gen, proposal) =
|
OsRng / external RNG Refactoring (#357)
* Use `OsRng` in place of `thread_rng`.
This changes the defaults of any builder by instantiating an `OsRng` instead of
a `thread_rng`, the former being much more secure than the latter.
Additionally, all the unit tests that still instantiate RNGs manually used `OsRng`s
as well; while there is no actual need for this level of security in tests, the performance overhead is very small and random number generation complexity has such a small impact on these tests that the convenience of being able to ban `thread_rng` from the codebase altogether, setting a good example and avoid issues when refactoring later greatly outweigh the negatives.
* Instead of storing random number generators in the various consensus algorithm instances, pass them in from the outside whenever they are needed.
This changes a large amount of interfaces (and in this commit is only partially done, since `DistAlgorithm` needs to be fundamentally altered as well.
It also obsoletes parts of the `util` module.
* Added an `R: Rng` type parameter to both methods of `DistAlgorithm`, forcing callers to pass in their own Rngs.
* Fixed documentation grammar and spelling in some of the altered interfaces due to RNG refactoring.
* Move `rng` argument to the end of the argument for most functions.
Also includes a reformatting due to Rust 1.30.
* Updated tests, accomodate `rng`-API changes.
* Fixed remaining compilation issues with new RNG code.
* Fix illegal `self` import outside curly braces.
* Cleaned up comments and fixed broken definition of `broadcast_input`.
* Updated existing test cases to properly work with static dispatch randomness.
* Do not use boxed `Rng`s for key generation in test networks.
* Use the passed-in `Rng` in `ReorderingAdversary`, instead of storing a boxed one.
* Fixed clippy lints after refactoring.
* Removed some no-longer necessary manual `fmt::Debug` implementations in test framework.
* Use `OsRng` even in tests in `binary_agreement_mitm`.
* Use a proper deterministic RNG in tests `binary_agreement_mitm`.
* Refactor `examples/simulation.rs` by not using `ThreadRng`, passing generic `Rng` parameters throughout and using a type alias instead of a newtype as the `Transaction`.
* Remove `thread_rng` use from `examples/node.rs`.
* Explicitly construct `InternalContrib` in `DynamicHoneyBadger::propose`.
* Fixed typo in description of `DistAlgorithm` trait.
2018-12-14 04:51:09 -08:00
|
|
|
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, &mut rand::thread_rng())
|
2018-10-02 07:24:51 -07:00
|
|
|
.unwrap_or_else(|_err| {
|
|
|
|
panic!("Failed to create `SyncKeyGen` instance #{}", id)
|
|
|
|
});
|
2018-06-20 01:11:33 -07:00
|
|
|
nodes.push(sync_key_gen);
|
|
|
|
proposal
|
2018-12-11 05:44:36 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-06-20 01:11:33 -07:00
|
|
|
|
|
|
|
// Handle the first `threshold + 1` proposals. Those should suffice for key generation.
|
2018-07-19 03:28:15 -07:00
|
|
|
let mut acks = Vec::new();
|
2018-06-25 04:07:31 -07:00
|
|
|
for (sender_id, proposal) in proposals[..=threshold].iter().enumerate() {
|
|
|
|
for (node_id, node) in nodes.iter_mut().enumerate() {
|
2018-07-08 09:41:50 -07:00
|
|
|
let proposal = proposal.clone().expect("proposal");
|
2018-10-24 12:23:54 -07:00
|
|
|
let ack = match node
|
OsRng / external RNG Refactoring (#357)
* Use `OsRng` in place of `thread_rng`.
This changes the defaults of any builder by instantiating an `OsRng` instead of
a `thread_rng`, the former being much more secure than the latter.
Additionally, all the unit tests that still instantiate RNGs manually used `OsRng`s
as well; while there is no actual need for this level of security in tests, the performance overhead is very small and random number generation complexity has such a small impact on these tests that the convenience of being able to ban `thread_rng` from the codebase altogether, setting a good example and avoid issues when refactoring later greatly outweigh the negatives.
* Instead of storing random number generators in the various consensus algorithm instances, pass them in from the outside whenever they are needed.
This changes a large amount of interfaces (and in this commit is only partially done, since `DistAlgorithm` needs to be fundamentally altered as well.
It also obsoletes parts of the `util` module.
* Added an `R: Rng` type parameter to both methods of `DistAlgorithm`, forcing callers to pass in their own Rngs.
* Fixed documentation grammar and spelling in some of the altered interfaces due to RNG refactoring.
* Move `rng` argument to the end of the argument for most functions.
Also includes a reformatting due to Rust 1.30.
* Updated tests, accomodate `rng`-API changes.
* Fixed remaining compilation issues with new RNG code.
* Fix illegal `self` import outside curly braces.
* Cleaned up comments and fixed broken definition of `broadcast_input`.
* Updated existing test cases to properly work with static dispatch randomness.
* Do not use boxed `Rng`s for key generation in test networks.
* Use the passed-in `Rng` in `ReorderingAdversary`, instead of storing a boxed one.
* Fixed clippy lints after refactoring.
* Removed some no-longer necessary manual `fmt::Debug` implementations in test framework.
* Use `OsRng` even in tests in `binary_agreement_mitm`.
* Use a proper deterministic RNG in tests `binary_agreement_mitm`.
* Refactor `examples/simulation.rs` by not using `ThreadRng`, passing generic `Rng` parameters throughout and using a type alias instead of a newtype as the `Transaction`.
* Remove `thread_rng` use from `examples/node.rs`.
* Explicitly construct `InternalContrib` in `DynamicHoneyBadger::propose`.
* Fixed typo in description of `DistAlgorithm` trait.
2018-12-14 04:51:09 -08:00
|
|
|
.handle_part(&sender_id, proposal, &mut rand::thread_rng())
|
2018-10-24 12:23:54 -07:00
|
|
|
.expect("failed to handle part")
|
|
|
|
{
|
|
|
|
PartOutcome::Valid(Some(ack)) => ack,
|
|
|
|
PartOutcome::Valid(None) => panic!("missing ack message"),
|
|
|
|
PartOutcome::Invalid(fault) => panic!("invalid proposal: {:?}", fault),
|
2018-07-08 09:41:50 -07:00
|
|
|
};
|
2018-07-19 03:28:15 -07:00
|
|
|
// Only the first `threshold + 1` manage to commit their `Ack`s.
|
2018-06-25 04:07:31 -07:00
|
|
|
if node_id <= 2 * threshold {
|
2018-07-19 03:28:15 -07:00
|
|
|
acks.push((node_id, ack));
|
2018-06-20 01:11:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-19 03:28:15 -07:00
|
|
|
// Handle the `Ack`s from `2 * threshold + 1` nodes.
|
|
|
|
for (sender_id, ack) in acks {
|
2018-06-20 01:11:33 -07:00
|
|
|
for node in &mut nodes {
|
2018-07-19 03:28:15 -07:00
|
|
|
assert!(!node.is_ready()); // Not enough `Ack`s yet.
|
2018-10-24 12:23:54 -07:00
|
|
|
node.handle_ack(&sender_id, ack.clone())
|
|
|
|
.expect("error handling ack");
|
2018-06-20 01:11:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the keys and test a threshold signature.
|
|
|
|
let msg = "Help I'm trapped in a unit test factory";
|
2018-08-06 07:53:37 -07:00
|
|
|
let pub_key_set = nodes[0]
|
|
|
|
.generate()
|
|
|
|
.expect("Failed to generate `PublicKeySet` for node #0")
|
|
|
|
.0;
|
2018-06-20 01:11:33 -07:00
|
|
|
let sig_shares: BTreeMap<_, _> = nodes
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(idx, node)| {
|
|
|
|
assert!(node.is_ready());
|
2018-08-06 07:53:37 -07:00
|
|
|
let (pks, opt_sk) = node.generate().unwrap_or_else(|_| {
|
|
|
|
panic!(
|
|
|
|
"Failed to generate `PublicKeySet` and `SecretKeyShare` for node #{}",
|
|
|
|
idx
|
|
|
|
)
|
|
|
|
});
|
2018-06-27 02:37:05 -07:00
|
|
|
let sk = opt_sk.expect("new secret key");
|
2018-06-20 01:11:33 -07:00
|
|
|
assert_eq!(pks, pub_key_set);
|
|
|
|
let sig = sk.sign(msg);
|
2018-07-23 02:50:44 -07:00
|
|
|
assert!(pks.public_key_share(idx).verify(&sig, msg));
|
|
|
|
(idx, sig)
|
2018-12-11 05:44:36 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-06-20 01:11:33 -07:00
|
|
|
let sig = pub_key_set
|
|
|
|
.combine_signatures(sig_shares.iter().take(threshold + 1))
|
|
|
|
.expect("signature shares match");
|
|
|
|
assert!(pub_key_set.public_key().verify(&sig, msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sync_key_gen() {
|
|
|
|
// This returns an error in all but the first test.
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
for &node_num in &[1, 2, 3, 4, 8, 15] {
|
2018-11-22 04:00:02 -08:00
|
|
|
let threshold = util::max_faulty(node_num);
|
2018-06-20 01:11:33 -07:00
|
|
|
test_sync_key_gen_with(threshold, node_num);
|
|
|
|
}
|
|
|
|
}
|