Implement phase 2 for Participant (#123) (#197)

* Add http option for Participant (#123)

* Fix redpallas tests for participant phase 2 (#123)

* Update participant/src/comms/http.rs

* Remove unnecessary session id for participant round 2 (#123)

* Fix clippy error (#123)

---------

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>
This commit is contained in:
natalie 2024-05-17 13:30:38 +01:00 committed by GitHub
parent fb6e8f5e9f
commit 06c0c2453d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 159 additions and 8 deletions

3
Cargo.lock generated
View File

@ -1378,7 +1378,9 @@ dependencies = [
"message-io",
"rand",
"reddsa",
"reqwest",
"serde_json",
"server",
"tokio",
]
@ -2074,6 +2076,7 @@ dependencies = [
"rand",
"reddsa",
"serde_json",
"server",
"tokio",
"trusted-dealer",
]

View File

@ -19,10 +19,6 @@ pub struct Config {
pub identifier: Identifier,
}
pub trait Logger {
fn log(&mut self, value: String);
}
fn validate_inputs(config: &Config) -> Result<(), Error> {
if config.min_signers < 2 {
return Err(Error::InvalidMinSigners);

View File

@ -17,6 +17,8 @@ serde_json = "1.0"
clap = { version = "4.5.4", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
message-io = "0.18"
reqwest = { version = "0.11.23", features = ["json"] }
server = { path = "../server" }
[features]
redpallas = []

View File

@ -9,6 +9,11 @@ pub struct Args {
#[arg(long, default_value_t = false)]
pub cli: bool,
/// HTTP mode. If enabled, it will use HTTP communication with a
/// FROST server.
#[arg(long, default_value_t = false)]
pub http: bool,
/// Public key package to use. Can be a file with a JSON-encoded
/// package, or "". If the file does not exist or if "" is specified,
/// then it will be read from standard input.
@ -22,4 +27,8 @@ pub struct Args {
/// Port to connect to, if using online comms
#[arg(short, long, default_value_t = 2744)]
pub port: u16,
/// Optional Session ID
#[arg(short, long, default_value = "")]
pub session_id: String,
}

View File

@ -1,6 +1,7 @@
use crate::args::Args;
use crate::comms::cli::CLIComms;
use crate::comms::http::HTTPComms;
use crate::comms::socket::SocketComms;
use crate::comms::Comms;
@ -17,6 +18,8 @@ pub async fn cli(
) -> Result<(), Box<dyn std::error::Error>> {
let mut comms: Box<dyn Comms> = if args.cli {
Box::new(CLIComms {})
} else if args.http {
Box::new(HTTPComms::new(args))
} else {
Box::new(SocketComms::new(args))
};
@ -45,7 +48,9 @@ pub async fn cli(
.await?;
let signature = generate_signature(round_2_config, &key_package, &nonces)?;
comms.send_signature_share(signature).await?;
comms
.send_signature_share(*key_package.identifier(), signature)
.await?;
print_values_round_2(signature, logger)?;

View File

@ -1,4 +1,5 @@
pub mod cli;
pub mod http;
pub mod socket;
use async_trait::async_trait;
@ -55,6 +56,7 @@ pub trait Comms {
async fn send_signature_share(
&mut self,
identifier: Identifier,
signature_share: SignatureShare,
) -> Result<(), Box<dyn Error>>;
}

View File

@ -64,6 +64,7 @@ impl Comms for CLIComms {
async fn send_signature_share(
&mut self,
_identifier: Identifier,
_signature_share: SignatureShare,
) -> Result<(), Box<dyn Error>> {
Ok(())

View File

@ -0,0 +1,129 @@
//! HTTP implementation of the Comms trait.
use async_trait::async_trait;
#[cfg(not(feature = "redpallas"))]
use frost_ed25519 as frost;
#[cfg(feature = "redpallas")]
use reddsa::frost::redpallas as frost;
use eyre::eyre;
use frost::{round1::SigningCommitments, round2::SignatureShare, Identifier};
use super::{Comms, GenericSigningPackage};
use std::io::{BufRead, Write};
use std::error::Error;
use std::time::Duration;
use crate::args::Args;
pub struct HTTPComms {
client: reqwest::Client,
host_port: String,
session_id: Uuid,
}
use server::Uuid;
// TODO: Improve error handling for invalid session id
impl HTTPComms {
pub fn new(args: &Args) -> Self {
let client = reqwest::Client::new();
Self {
client,
host_port: format!("http://{}:{}", args.ip, args.port),
session_id: Uuid::parse_str(&args.session_id).expect("invalid session id"),
}
}
}
#[async_trait(?Send)]
impl Comms for HTTPComms {
async fn get_signing_package(
&mut self,
_input: &mut dyn BufRead,
_output: &mut dyn Write,
commitments: SigningCommitments,
identifier: Identifier,
) -> Result<GenericSigningPackage, Box<dyn Error>> {
// Send Commitments to Server
self.client
.post(format!("{}/send_commitments", self.host_port))
.json(&server::SendCommitmentsArgs {
session_id: self.session_id,
identifier,
commitments: vec![commitments],
})
.send()
.await?;
eprint!("Waiting for coordinator to send signing package...");
// Receive SigningPackage from Coordinator
let r = loop {
let r = self
.client
.post(format!("{}/get_signing_package", self.host_port))
.json(&server::GetSigningPackageArgs {
session_id: self.session_id,
})
.send()
.await?;
if r.status() != 200 {
tokio::time::sleep(Duration::from_secs(2)).await;
eprint!(".");
} else {
eprintln!("\nSigning package received");
break r.json::<server::GetSigningPackageOutput>().await?;
}
};
#[cfg(feature = "redpallas")]
let signing_package = (
r.signing_package
.first()
.ok_or(eyre!("missing signing package"))
.cloned()?,
r.randomizer
.first()
.ok_or(eyre!("missing randomizer"))
.cloned()?,
);
#[cfg(not(feature = "redpallas"))]
let signing_package = r
.signing_package
.first()
.ok_or(eyre!("missing signing package"))
.cloned()?;
Ok(signing_package)
}
async fn send_signature_share(
&mut self,
identifier: Identifier,
signature_share: SignatureShare,
) -> Result<(), Box<dyn Error>> {
// Send signature share to Coordinator
eprintln!("Sending signature share to coordinator...");
let _r = self
.client
.post(format!("{}/send_signature_share", self.host_port))
.json(&server::SendSignatureShareArgs {
identifier,
session_id: self.session_id,
signature_share: vec![signature_share],
})
.send()
.await?;
Ok(())
}
}

View File

@ -117,6 +117,7 @@ impl Comms for SocketComms {
async fn send_signature_share(
&mut self,
_identifier: Identifier,
signature_share: SignatureShare,
) -> Result<(), Box<dyn Error>> {
// Send signature shares to Coordinator

View File

@ -42,8 +42,9 @@ async fn check_valid_round_1_inputs() {
key_package: "-".to_string(),
ip: "0.0.0.0".to_string(),
port: 80,
session_id: "session-id".to_string(),
http: false,
};
let input = SECRET_SHARE_JSON;
let mut valid_input = input.as_bytes();

View File

@ -1,5 +1,6 @@
use std::collections::BTreeMap;
use frost::Identifier;
use serde::{Deserialize, Serialize};
pub use uuid::Uuid;
@ -75,8 +76,8 @@ pub struct GetSigningPackageOutput {
#[derive(Debug, Serialize, Deserialize)]
pub struct SendSignatureShareArgs {
pub identifier: Identifier,
pub session_id: Uuid,
pub identifier: frost::Identifier,
pub signature_share: Vec<frost::round2::SignatureShare>,
}

View File

@ -176,8 +176,8 @@ async fn test_main_router() -> Result<(), Box<dyn std::error::Error>> {
let res = server
.post("/send_signature_share")
.json(&server::SendSignatureShareArgs {
session_id,
identifier: *identifier,
session_id,
signature_share,
})
.await;

View File

@ -18,6 +18,7 @@ dkg = { path = "../dkg"}
trusted-dealer = { path = "../trusted-dealer"}
participant = { path = "../participant"}
coordinator = { path = "../coordinator"}
server = { path = "../server"}
rand = "0.8"
[features]