mirror of
https://github.com/luxfi/safe-frost.git
synced 2026-10-30 15:19:29 +00:00
95 lines
2.6 KiB
Rust
95 lines
2.6 KiB
Rust
use rand::{Rng as _, seq::SliceRandom as _};
|
|
use std::{
|
|
fmt::Write as _,
|
|
path::Path,
|
|
process::{Command, Stdio},
|
|
};
|
|
|
|
/// Generate and verify a FROST signature.
|
|
///
|
|
/// For more details on the process, see `contracts.t.sol` end-to-end test.
|
|
#[test]
|
|
fn roundtrip() {
|
|
let safe_frost = SafeFrost::with_root_directory("roundtrip");
|
|
|
|
// Generate a secret and prepare for the signing rounds.
|
|
safe_frost.exec("split", &["--threshold", "3", "--signers", "5", "--force"]);
|
|
safe_frost.exec("info", &["public-key"]);
|
|
|
|
let message = random_message();
|
|
let participants = random_signers(3, 5);
|
|
|
|
// Round 1.
|
|
for participant in &participants {
|
|
safe_frost.exec("commit", &["--share-index", participant]);
|
|
}
|
|
safe_frost.exec("prepare", &["--message", &message]);
|
|
|
|
// Round 2.
|
|
for participant in &participants {
|
|
safe_frost.exec("sign", &["--share-index", participant]);
|
|
}
|
|
safe_frost.exec("aggregate", &[]);
|
|
|
|
// Verify the signature.
|
|
safe_frost.exec("verify", &[]);
|
|
safe_frost.exec("info", &["signature"]);
|
|
}
|
|
|
|
struct SafeFrost {
|
|
root: String,
|
|
}
|
|
|
|
impl SafeFrost {
|
|
fn with_root_directory(tag: &str) -> Self {
|
|
let root = Path::new(".frost")
|
|
.join(tag)
|
|
.into_os_string()
|
|
.into_string()
|
|
.unwrap();
|
|
Self { root }
|
|
}
|
|
|
|
fn exec(&self, subcommand: &str, options: &[&str]) {
|
|
print!("$ safe-frost {subcommand}");
|
|
for option in options {
|
|
print!(" {option}");
|
|
}
|
|
println!();
|
|
let output = Command::new("cargo")
|
|
.args(["run", "-q", "--"])
|
|
.args(["--root-directory", &self.root])
|
|
.arg(subcommand)
|
|
.args(options)
|
|
.stderr(Stdio::inherit())
|
|
.output()
|
|
.expect("Failed to execute `safe-frost`");
|
|
assert!(output.status.success(), "`safe-frost` command failed");
|
|
print!("{}", String::from_utf8_lossy(&output.stdout));
|
|
}
|
|
}
|
|
|
|
/// Generates a random message for signing.
|
|
fn random_message() -> String {
|
|
let mut rng = rand::thread_rng();
|
|
let mut message = [0_u8; 32];
|
|
rng.fill(&mut message);
|
|
let mut buffer = String::with_capacity(2 + message.len() * 2);
|
|
for byte in &message {
|
|
write!(&mut buffer, "{:02x}", byte).unwrap();
|
|
}
|
|
buffer
|
|
}
|
|
|
|
/// Pick a random set of signers from a larger group.
|
|
fn random_signers(threshold: usize, signers: usize) -> Vec<String> {
|
|
let mut rng = rand::thread_rng();
|
|
let mut signers = (0..signers).collect::<Vec<_>>();
|
|
signers.shuffle(&mut rng);
|
|
signers
|
|
.iter()
|
|
.take(threshold)
|
|
.map(ToString::to_string)
|
|
.collect()
|
|
}
|