commit c3d49d6115e3448e4a7371a08e06b4fc4e278f69
parent 8c4c4317687a74a12cf3af423fb252b44ac3de65
Author: nickfarrow <nick@nickfarrow.com>
Date: Tue, 4 Oct 2022 18:22:45 +1100
add proptest
Diffstat:
6 files changed, 295 insertions(+), 253 deletions(-)
diff --git a/README.md b/README.md
@@ -3,7 +3,6 @@
[roast paper](https://eprint.iacr.org/2022/550.pdf)
-
## todo
Later will be made agnostic to the threshold signature scheme that is used / frost implementation that is used.
@@ -12,6 +11,7 @@ Finish roastlib and test without specifying any io methods from client to server
Create communication endpoints for both ROAST server (coordinator) and signer clients using new layout, then use them to communicate a test session.
* Current plan is to communicate via http requests and rocket with some mutable state for client and server.
+## excerpt
```
let frost = secp_frost::Frost::<Sha256, Deterministic<Sha256>>::default();
let (secret_shares, frost_keys) = frost::frost_keygen(2, 3);
@@ -96,6 +96,16 @@ Unforgability: a threshold signature scheme is existentially unforgable under CM
can produce a valid signature on a message that was never used in a signing session and A never asked in any query round.
+
+#### Nonce aggregation
+A semi-interactive threshold signature scheme is aggregatable if |p| and |sigma| are constant in parameters n and t, for p <- PreAgg(PK, {t_i}_i_in_T), sigma <- SignAgg(PK, p, {sigma_i}_i_in_T) and all inputs PK and m.
+
+The aggregation of these elements is important for practical purposes as it reduces the size of the final signature as well as the amount of data that needs to be breadcast during signing.
+
+In each of the signing rounds, a coordinator node will be one of the ssigners and can collect the contributions from all signers, aggregate them, and broadcast only the aggregate output back to signers.
+
+> Doesn't seem super important
+
FROST3 -> PreAgg (nonce agg) -> Aggregate two presignature products D=prod(d_i), and E=prod(e_i) for i in T. Whereas FROST2 the aggregated presignature is not really aggregated, just the set {(D_i, E_i) for i in T}. The SignRound algorightm takes care of computing the products, as before. Other FROST versions include 2-BTZ and 2-CKM.
### FROSTLAND
@@ -124,7 +134,7 @@ Along with each signature share, each signer is also required to provide a fresh
### Eliminating the Trusted Coordinator
A simple method to eliminate the need for semi-strusted coordinator is to let the signers run enough instances of the coordinator process: the n signers choose among themselves any set n-t+1 coordinators and start s-t+1 concurrent runs of roast. Note that one of these sessions will have t honest signers.
-The concurrent runs of ROAST do not need to be started simultaneously - e.g. honest signers can resend their reply in the run with coorinator_2 only after d seconds and only if they have not obtained a valid signature from any other run (is that a concern?)
+The concurrent runs of ROAST do not need to be started simultaneously - e.g. honest signers can resend their reply in the run with coorinator_2 only after d seconds and only if they have not obtained a valid signature from any other run (is that a concern? Yes excessive computation and communication.)
![image](https://user-images.githubusercontent.com/24557779/192925900-3c15cddf-a467-47be-80a5-3b04b0acbd47.png)
\ No newline at end of file
diff --git a/roastlib/Cargo.lock b/roastlib/Cargo.lock
@@ -135,6 +135,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -156,6 +171,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
name = "bytes"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -989,6 +1010,38 @@ dependencies = [
]
[[package]]
+name = "proptest"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "byteorder",
+ "lazy_static",
+ "num-traits",
+ "quick-error 2.0.1",
+ "rand 0.8.5",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1066,6 +1119,15 @@ dependencies = [
]
[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core 0.6.4",
+]
+
+[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1251,6 +1313,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error 1.2.3",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1281,6 +1355,7 @@ name = "secp256kfun"
version = "0.7.1"
dependencies = [
"digest",
+ "proptest",
"rand_core 0.6.4",
"serde",
"subtle-ng",
@@ -1777,6 +1852,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/roastlib/Cargo.toml b/roastlib/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
schnorr_fun = { path = "../../secp256kfun/schnorr_fun", features = ["serde"]}
-secp256kfun = { path = "../../secp256kfun/secp256kfun"}
+secp256kfun = { path = "../../secp256kfun/secp256kfun", features = ["proptest"]}
rand = "0.8.5"
sha2 = "0.10"
rng = "0.1.0"
diff --git a/roastlib/src/coordinator.rs b/roastlib/src/coordinator.rs
@@ -1,3 +1,4 @@
+//! ROAST coordinator
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex},
@@ -26,10 +27,10 @@ pub struct RoastState<'a> {
message: Message<'a, Public>,
responsive_signers: HashSet<usize>,
malicious_signers: HashSet<usize>,
+ session_counter: usize,
latest_nonces: HashMap<usize, Nonce>,
sessions: HashMap<usize, Arc<Mutex<RoastSignSession>>>,
signer_session_map: HashMap<usize, usize>,
- session_counter: usize,
}
pub struct RoastSignSession {
@@ -67,7 +68,7 @@ impl<'a, H: Digest + Clone + Digest<OutputSize = U32>, NG> Coordinator<'a, H, NG
// }
// Main body of the ROAST coordinator algorithm
- pub fn process(
+ pub fn receive(
&self,
index: usize,
signature_share: Option<Scalar<Public, Zero>>,
@@ -98,6 +99,10 @@ impl<'a, H: Digest + Clone + Digest<OutputSize = U32>, NG> Coordinator<'a, H, NG
// If this is not the inital message from S_i
match roast_state.signer_session_map.get(&index) {
Some(session_id) => {
+ println!(
+ "Party {} sent a signature for sign session {}",
+ index, session_id
+ );
// Get session from roast_state
let session = {
let roast_session = roast_state
@@ -113,9 +118,6 @@ impl<'a, H: Digest + Clone + Digest<OutputSize = U32>, NG> Coordinator<'a, H, NG
roast_state.message,
)
};
- println!("Party {} is loading signing session {}", index, session_id);
-
- // dbg!(&self.frost_key.clone(), &session, index, signature_share);
if !self.frost.verify_signature_share(
&self.frost_key.clone(),
diff --git a/roastlib/src/frost.rs b/roastlib/src/frost.rs
@@ -1,7 +1,7 @@
-use secp256kfun::{rand_core::RngCore, Scalar};
+use secp256kfun::Scalar;
use schnorr_fun::{
- frost::{Frost, PointPoly, ScalarPoly, XOnlyFrostKey},
+ frost::{Frost, ScalarPoly, XOnlyFrostKey},
nonce::Deterministic,
};
@@ -12,68 +12,47 @@ pub fn frost_keygen(threshold: usize, n_parties: usize) -> (Vec<Scalar>, Vec<XOn
let frost = Frost::new(Schnorr::<Sha256, Deterministic<Sha256>>::new(
Deterministic::<Sha256>::default(),
));
- dbg!(threshold, n_parties);
assert!(threshold <= n_parties);
// create some scalar polynomial for each party
- let mut scalar_polys = vec![];
- for i in 1..=n_parties {
- println!("Creating scalar poly {}", i);
- let scalar_poly = (1..=threshold)
- .map(|_| {
- let mut rng: rand::rngs::StdRng = rand::SeedableRng::from_entropy();
- Scalar::from(rng.next_u32())
- .non_zero()
- .expect("computationally unreachable")
- })
- .collect();
- scalar_polys.push(ScalarPoly::new(scalar_poly));
- }
- let point_polys: Vec<PointPoly> = scalar_polys.iter().map(|sp| sp.to_point_poly()).collect();
-
+ let mut rng = rand::rngs::ThreadRng::default();
+ let scalar_polys = (0..n_parties)
+ .map(|_| ScalarPoly::random(threshold, &mut rng))
+ .collect::<Vec<_>>();
+ let point_polys = scalar_polys
+ .iter()
+ .map(ScalarPoly::to_point_poly)
+ .collect::<Vec<_>>();
let keygen = frost.new_keygen(point_polys).unwrap();
-
- let mut proofs_of_possession = vec![];
- let mut shares_vec = vec![];
- for (i, sp) in scalar_polys.into_iter().enumerate() {
- println!("calculating shares and pop {}", i);
- let (shares, pop) = frost.create_shares(&keygen, sp);
- proofs_of_possession.push(pop);
- shares_vec.push(shares);
- }
- println!("Calculated shares and pops");
-
- // collect the recieved shares for each party
- let mut recieved_shares: Vec<Vec<_>> = vec![];
- for party_index in 0..n_parties {
- println!("Collecting shares for {}", party_index);
- recieved_shares.push(vec![]);
- for share_index in 0..n_parties {
- recieved_shares[party_index].push(shares_vec[share_index][party_index].clone());
- }
- }
-
- // println!("{:?}", recieved_shares);
+ let (shares, proofs_of_possesion): (Vec<_>, Vec<_>) = scalar_polys
+ .into_iter()
+ .map(|sp| frost.create_shares(&keygen, sp))
+ .unzip();
+ // collect the received shares for each party
+ let received_shares = (0..n_parties)
+ .map(|party_index| {
+ (0..n_parties)
+ .map(|share_index| shares[share_index][party_index].clone())
+ .collect()
+ })
+ .collect::<Vec<Vec<_>>>();
// finish keygen for each party
- let (secret_shares, frost_keys): (Vec<Scalar>, Vec<XOnlyFrostKey>) = (0..n_parties)
- .map(|i| {
- println!("Finishing keygen for participant {}", i);
- std::thread::sleep(std::time::Duration::from_secs(1));
+ let (secret_shares, frost_keys): (Vec<_>, Vec<_>) = (0..n_parties)
+ .map(|party_index| {
let (secret_share, frost_key) = frost
.finish_keygen(
keygen.clone(),
- i,
- recieved_shares[i].clone(),
- proofs_of_possession.clone(),
+ party_index,
+ received_shares[party_index].clone(),
+ proofs_of_possesion.clone(),
)
- .expect("collected shares");
- println!("Calculated secret share.");
+ .unwrap();
+
let xonly_frost_key = frost_key.into_xonly_key();
(secret_share, xonly_frost_key)
})
.unzip();
- println!("Finished keygen!\n\n");
(secret_shares, frost_keys)
}
diff --git a/roastlib/src/main.rs b/roastlib/src/main.rs
@@ -1,205 +1,172 @@
-use roast::coordinator;
-use roast::frost;
-use roast::signer;
-use schnorr_fun::frost as secp_frost;
-use schnorr_fun::musig::Nonce;
-use schnorr_fun::nonce::Deterministic;
-use schnorr_fun::Message;
-use sha2::Sha256;
-
-fn main() {
- test_t_of_n_sequential(9, 15);
- // test_2_of_3()
-}
+fn main() {}
+
+#[cfg(test)]
+mod test {
+ use roast::coordinator;
+ use roast::frost;
+ use roast::signer;
+ use schnorr_fun::frost as secp_frost;
+ use schnorr_fun::musig::Nonce;
+ use schnorr_fun::nonce::Deterministic;
+ use schnorr_fun::Message;
+ use sha2::Sha256;
+
+ use secp256kfun::proptest::{
+ proptest,
+ strategy::{Just, Strategy},
+ };
+
+ #[test]
+ fn test_2_of_3_sequential() {
+ let frost = secp_frost::Frost::<Sha256, Deterministic<Sha256>>::default();
+ let (secret_shares, frost_keys) = frost::frost_keygen(2, 3);
+
+ let message = Message::plain("test", b"test");
+ let roast = coordinator::Coordinator::new(frost.clone(), frost_keys[0].clone(), message);
+
+ // Create each signer session and create an initial nonce
+ let (mut signer1, nonce1) = signer::RoastSigner::new(
+ frost.clone(),
+ frost_keys[0].clone(),
+ 0,
+ secret_shares[0].clone(),
+ [].as_slice(),
+ message,
+ );
+ let (mut signer2, nonce2) = signer::RoastSigner::new(
+ frost,
+ frost_keys[1].clone(),
+ 1,
+ secret_shares[1].clone(),
+ [1].as_slice(),
+ message,
+ );
+
+ // Begin with each signer sending a nonce to ROAST, marking these signers as responsive.
+ let (combined_signature, nonce_set) = roast.receive(0, None, nonce1);
+ assert!(nonce_set.is_none());
+ assert!(combined_signature.is_none());
+
+ let (_combined_signature, nonce_set) = roast.receive(1, None, nonce2);
+ assert!(nonce_set.is_some());
+
+ // Once ROAST receives the threshold number of nonces, it responds with a nonce set
+ let sign_session_nonces = nonce_set.expect("roast responded with nonces");
+
+ // The signer signs using this the nonces for this sign session,
+ // and responds to ROAST with a signature share.
+ let (sig_share2, nonce2) = signer2.sign(sign_session_nonces.clone());
+ let (combined_signature, nonce_set) = roast.receive(1, Some(sig_share2), nonce2);
+ dbg!(&combined_signature.is_some(), &nonce_set.is_some());
+ assert!(combined_signature.is_none());
+
+ // ROAST also sends the nonce set to the other signer, who also signs
+ let (sig_share1, nonce1) = signer1.sign(sign_session_nonces);
+
+ let (combined_signature, nonce_set) = roast.receive(0, Some(sig_share1), nonce1);
+ dbg!(&combined_signature.is_some(), &nonce_set.is_some());
+ assert!(combined_signature.is_some());
+
+ // Once the threshold number of signature shares have been received,
+ // ROAST combines the signature shares into the aggregate signature
+ dbg!(combined_signature);
+ }
-fn test_t_of_n_sequential(threshold: usize, n_parties: usize) {
- let frost = secp_frost::Frost::<Sha256, Deterministic<Sha256>>::default();
- let (secret_shares, frost_keys) = frost::frost_keygen(threshold, n_parties);
-
- let message = Message::plain("test", b"test");
- let roast = coordinator::Coordinator::new(frost.clone(), frost_keys[0].clone(), message);
-
- // Create each signer session and create an initial nonce
- let (mut signers, mut nonces): (Vec<_>, Vec<_>) = frost_keys
- .into_iter()
- .zip(secret_shares)
- .enumerate()
- .map(|(i, (frost_key, secret_share))| {
- signer::RoastSigner::new(
- frost.clone(),
- frost_key,
- i,
- secret_share,
- [i as u8].as_slice(),
- message,
- )
- })
- .unzip();
-
- let mut sig_shares = vec![];
- let mut nonce_set: Vec<Option<Vec<(usize, Nonce)>>> = vec![None; n_parties + 1];
- let mut finished_signature = None;
- let mut n_rounds = 0;
-
- while finished_signature.is_none() {
- n_rounds += 1;
- for signer_index in 0..n_parties {
- // Check to see if this signer has recieved any nonces
- let (sig, new_nonce) = match nonce_set[signer_index].clone() {
- // Sign if we have recieved nonces, and create new nonce
- Some(signing_nonces) => {
- // dbg!(&signing_nonces);
- let (sig, nonce) = signers[signer_index].sign(signing_nonces);
- (Some(sig), nonce)
+ // This test works, but slowly since it goes through a few sets of responsive signers
+ // before producing a complete signature. This is because we aren't accurately replicating
+ // any asynchronous messages.
+ fn t_of_n_sequential(threshold: usize, n_parties: usize) {
+ let frost = secp_frost::Frost::<Sha256, Deterministic<Sha256>>::default();
+ let (secret_shares, frost_keys) = frost::frost_keygen(threshold, n_parties);
+
+ let message = Message::plain("test", b"test");
+ let roast = coordinator::Coordinator::new(frost.clone(), frost_keys[0].clone(), message);
+
+ // Create each signer session and create an initial nonce
+ let (mut signers, mut nonces): (Vec<_>, Vec<_>) = frost_keys
+ .into_iter()
+ .zip(secret_shares)
+ .enumerate()
+ .map(|(i, (frost_key, secret_share))| {
+ signer::RoastSigner::new(
+ frost.clone(),
+ frost_key,
+ i,
+ secret_share,
+ [i as u8].as_slice(),
+ message,
+ )
+ })
+ .unzip();
+
+ let mut sig_shares = vec![];
+ let mut nonce_set: Vec<Option<Vec<(usize, Nonce)>>> = vec![None; n_parties + 1];
+ let mut finished_signature = None;
+ let mut n_rounds = 0;
+
+ while finished_signature.is_none() {
+ n_rounds += 1;
+ for signer_index in 0..n_parties {
+ // Check to see if this signer has recieved any nonces
+ let (sig, new_nonce) = match nonce_set[signer_index].clone() {
+ // Sign if we have recieved nonces, and create new nonce
+ Some(signing_nonces) => {
+ // dbg!(&signing_nonces);
+ let (sig, nonce) = signers[signer_index].sign(signing_nonces);
+ (Some(sig), nonce)
+ }
+ // Otherwise, just create a new nonce
+ None => (
+ None,
+ signers[signer_index]
+ .new_nonce([signer_index as u8].as_slice())
+ .public(),
+ ),
+ };
+ // Send signature and our next nonce to ROAST
+ let (combined_sig, updated_nonce_set) = roast.receive(signer_index, sig, new_nonce);
+
+ if combined_sig.is_some() {
+ finished_signature = combined_sig;
+ break;
}
- // Otherwise, just create a new nonce
- None => (
- None,
- signers[signer_index]
- .new_nonce([signer_index as u8].as_slice())
- .public(),
- ),
- };
- // Send signature and our next nonce to ROAST
- let (combined_sig, updated_nonce_set) = roast.process(signer_index, sig, new_nonce);
-
- if combined_sig.is_some() {
- finished_signature = combined_sig;
- break;
- }
- // hacky mimic broadcast
- // Store the new nonce_set for this caller,
- // and for peers who are have not recieved any nonces yet.
- // this will probably break when introducing malicious signers
- if updated_nonce_set.is_some() {
- nonce_set[signer_index] = updated_nonce_set.clone();
- nonce_set = nonce_set
- .into_iter()
- .map(|nonce| {
- if nonce.is_some() {
- nonce
- } else {
- updated_nonce_set.clone()
- }
- })
- .collect()
- }
+ // hacky mimic broadcast
+ // Store the new nonce_set for this caller,
+ // and for peers who are have not recieved any nonces yet.
+ // this will probably break when introducing malicious signers
+ if updated_nonce_set.is_some() {
+ nonce_set[signer_index] = updated_nonce_set.clone();
+ nonce_set = nonce_set
+ .into_iter()
+ .map(|nonce| {
+ if nonce.is_some() {
+ nonce
+ } else {
+ updated_nonce_set.clone()
+ }
+ })
+ .collect()
+ }
- nonces[signer_index] = new_nonce;
+ nonces[signer_index] = new_nonce;
- if sig.is_some() {
- sig_shares.push(sig);
+ if sig.is_some() {
+ sig_shares.push(sig);
+ }
+ // dbg!(&sig_shares);
}
- // dbg!(&sig_shares);
}
+ dbg!(&finished_signature, &n_rounds);
+ assert!(finished_signature.is_some())
}
- dbg!(finished_signature, n_rounds);
-
- // let sig_shares: Vec<_> = signers
- // .into_iter()
- // .zip(nonces.clone())
- // .enumerate()
- // .map(|(i, (mut signer, nonce))| {
- // let (combined_sig, nonce_set) = roast.process(i, None, nonce);
- // let sig = match nonce_set {
- // Some(nonce_set) => Some(signer.sign(nonce_set)),
- // None => None,
- // };
- // sig
- // })
- // .collect();
-
- // dbg!(&sig_shares);
-
- // let combined_sigs: Vec<_> = sig_shares
- // .into_iter()
- // .filter(|sig_share| sig_share.is_some())
- // .enumerate()
- // .map(|(i, sig_share)| {
- // let (sig, new_nonce) = sig_share.expect("filtered");
- // let (combined_sig, nonce_set) = roast.process(i, Some(sig), new_nonce);
- // combined_sig
- // })
- // .collect();
- // dbg!(combined_sigs);
-}
-// send_sigs
-
-// // Once ROAST receives the threshold number of nonces, it responds with a nonce set
-// let sign_session_nonces = nonce_set.expect("roast responded with nonces");
-
-// // The signer signs using this nonce set and response with a signature share
-// let (sig_share2, nonce2) = signer2.sign(sign_session_nonces.clone());
-// let (combined_signature, nonce_set) = roast.process(1, Some(sig_share2), nonce2);
-// dbg!(&combined_signature.is_some(), &nonce_set.is_some());
-// assert!(combined_signature.is_none());
-
-// // ROAST also sends the nonce set to the other signer, who also signs
-// let (sig_share1, nonce1) = signer1.sign(sign_session_nonces);
-
-// let (combined_signature, nonce_set) = roast.process(0, Some(sig_share1), nonce1);
-// dbg!(&combined_signature.is_some(), &nonce_set.is_some());
-// assert!(combined_signature.is_some());
-
-// // Once the threshold number of signature shares have been received,
-// // ROAST combines the signature shares into the aggregate signature
-// dbg!(combined_signature);
-
-fn test_2_of_3_sequential() {
- let frost = secp_frost::Frost::<Sha256, Deterministic<Sha256>>::default();
- let (secret_shares, frost_keys) = frost::frost_keygen(2, 3);
-
- let message = Message::plain("test", b"test");
- let roast = coordinator::Coordinator::new(frost.clone(), frost_keys[0].clone(), message);
-
- // Create each signer session and create an initial nonce
- let (mut signer1, nonce1) = signer::RoastSigner::new(
- frost.clone(),
- frost_keys[0].clone(),
- 0,
- secret_shares[0].clone(),
- [].as_slice(),
- message,
- );
- let (mut signer2, nonce2) = signer::RoastSigner::new(
- frost,
- frost_keys[1].clone(),
- 1,
- secret_shares[1].clone(),
- [1].as_slice(),
- message,
- );
-
- // Begin with each signer sending a nonce to ROAST
- let (combined_signature, nonce_set) = roast.process(0, None, nonce1);
- assert!(nonce_set.is_none());
- assert!(combined_signature.is_none());
-
- let (_combined_signature, nonce_set) = roast.process(1, None, nonce2);
- assert!(nonce_set.is_some());
-
- // Once ROAST receives the threshold number of nonces, it responds with a nonce set
- let sign_session_nonces = nonce_set.expect("roast responded with nonces");
-
- // The signer signs using this the nonces for this sign session,
- // and responds to ROAST with a signature share.
- let (sig_share2, nonce2) = signer2.sign(sign_session_nonces.clone());
- let (combined_signature, nonce_set) = roast.process(1, Some(sig_share2), nonce2);
- dbg!(&combined_signature.is_some(), &nonce_set.is_some());
- assert!(combined_signature.is_none());
-
- // ROAST also sends the nonce set to the other signer, who also signs
- let (sig_share1, nonce1) = signer1.sign(sign_session_nonces);
-
- let (combined_signature, nonce_set) = roast.process(0, Some(sig_share1), nonce1);
- dbg!(&combined_signature.is_some(), &nonce_set.is_some());
- assert!(combined_signature.is_some());
-
- // Once the threshold number of signature shares have been received,
- // ROAST combines the signature shares into the aggregate signature
- dbg!(combined_signature);
+ proptest! {
+ #[test]
+ fn roast_proptest_t_of_n(
+ (n_parties, threshold) in (2usize..5).prop_flat_map(|n| (Just(n), 2usize..=n))
+ ) {
+ t_of_n_sequential(threshold, n_parties);
+ }
+ }
}
-// #[cfg(test)]
-// mod test {}