mod handlers;
use clap::{value_parser, Arg, Command};
use commonware_cryptography::{
ed25519::{PrivateKey, PublicKey},
PrivateKeyExt as _, Signer as _,
};
use commonware_p2p::{authenticated::discovery, Manager};
use commonware_runtime::{tokio, Metrics, Runner};
use commonware_utils::{quorum, set::Ordered, NZU32};
use governor::Quota;
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,
time::Duration,
};
use tracing::info;
const APPLICATION_NAMESPACE: &[u8] = b"_COMMONWARE_VRF_";
fn main() {
let runtime_cfg = tokio::Config::default();
let executor = tokio::Runner::new(runtime_cfg.clone());
let matches = Command::new("commonware-vrf")
.about("generate bias-resistant randomness with friends")
.arg(
Arg::new("bootstrappers")
.long("bootstrappers")
.required(false)
.value_delimiter(',')
.value_parser(value_parser!(String)),
)
.arg(Arg::new("me").long("me").required(true))
.arg(
Arg::new("participants")
.long("participants")
.required(true)
.value_delimiter(',')
.value_parser(value_parser!(u64))
.help("All participants (arbiter and contributors)"),
)
.arg(
Arg::new("arbiter")
.long("arbiter")
.required(false)
.value_parser(value_parser!(u64))
.help("If set, run as a contributor otherwise run as the arbiter"),
)
.arg(
Arg::new("contributors")
.long("contributors")
.required(true)
.value_delimiter(',')
.value_parser(value_parser!(u64))
.help("contributors"),
)
.arg(
Arg::new("corrupt")
.long("corrupt")
.num_args(0)
.help("Send invalid dealings to contributors"),
)
.arg(
Arg::new("lazy")
.long("lazy")
.num_args(0)
.help("Only send 2f dealings to contributors (force reveal f)"),
)
.arg(
Arg::new("forger")
.long("forger")
.num_args(0)
.help("Forge acknowledgements from contributors"),
)
.get_matches();
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let me = matches
.get_one::<String>("me")
.expect("Please provide identity");
let parts = me.split('@').collect::<Vec<&str>>();
if parts.len() != 2 {
panic!("Identity not well-formed");
}
let key = parts[0].parse::<u64>().expect("Key not well-formed");
let signer = PrivateKey::from_seed(key);
tracing::info!(key = ?signer.public_key(), "loaded signer");
let port = parts[1].parse::<u16>().expect("Port not well-formed");
tracing::info!(port, "loaded port");
let participants = matches
.get_many::<u64>("participants")
.expect("Please provide allowed keys")
.copied();
if participants.len() == 0 {
panic!("Please provide at least one participant");
}
let recipients = participants
.into_iter()
.map(|peer| {
let verifier = PrivateKey::from_seed(peer).public_key();
tracing::info!(key = ?verifier, "registered authorized key",);
verifier
})
.collect::<Ordered<_>>();
let bootstrappers = matches.get_many::<String>("bootstrappers");
let mut bootstrapper_identities = Vec::new();
if let Some(bootstrappers) = bootstrappers {
for bootstrapper in bootstrappers {
let parts = bootstrapper.split('@').collect::<Vec<&str>>();
let bootstrapper_key = parts[0]
.parse::<u64>()
.expect("Bootstrapper key not well-formed");
let verifier = PrivateKey::from_seed(bootstrapper_key).public_key();
let bootstrapper_address =
SocketAddr::from_str(parts[1]).expect("Bootstrapper address not well-formed");
bootstrapper_identities.push((verifier, bootstrapper_address));
}
}
const MAX_MESSAGE_SIZE: usize = 1024 * 1024; let p2p_cfg = discovery::Config::local(
signer.clone(),
APPLICATION_NAMESPACE,
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
bootstrapper_identities.clone(),
MAX_MESSAGE_SIZE,
);
executor.start(|context| async move {
let (mut network, mut oracle) =
discovery::Network::new(context.with_label("network"), p2p_cfg);
oracle.update(0, recipients).await;
let mut contributors = Vec::new();
let participants = matches
.get_many::<u64>("contributors")
.expect("Please provide contributors")
.copied();
if participants.len() == 0 {
panic!("Please provide at least one contributor");
}
for peer in participants {
let verifier = PrivateKey::from_seed(peer).public_key();
tracing::info!(key = ?verifier, "registered contributor",);
contributors.push(verifier);
}
let threshold = quorum(contributors.len() as u32);
info!(threshold, "inferred parameters");
const DEFAULT_MESSAGE_BACKLOG: usize = 256;
const DKG_FREQUENCY: Duration = Duration::from_secs(10);
const DKG_PHASE_TIMEOUT: Duration = Duration::from_secs(1);
if let Some(arbiter) = matches.get_one::<u64>("arbiter") {
let corrupt = matches.get_flag("corrupt");
let lazy = matches.get_flag("lazy");
let forger = matches.get_flag("forger");
let (contributor_sender, contributor_receiver) = network.register(
handlers::DKG_CHANNEL,
Quota::per_second(NZU32!(10)),
DEFAULT_MESSAGE_BACKLOG,
);
let arbiter = PrivateKey::from_seed(*arbiter).public_key();
let (contributor, requests) = handlers::Contributor::new(
context.with_label("contributor"),
signer,
DKG_PHASE_TIMEOUT,
arbiter,
contributors.clone().into_iter().collect(),
corrupt,
lazy,
forger,
);
contributor.start(contributor_sender, contributor_receiver);
let (vrf_sender, vrf_receiver) = network.register(
handlers::VRF_CHANNEL,
Quota::per_second(NZU32!(10)),
DEFAULT_MESSAGE_BACKLOG,
);
let signer = handlers::Vrf::new(
context.with_label("signer"),
Duration::from_secs(5),
threshold,
contributors,
requests,
);
signer.start(vrf_sender, vrf_receiver);
} else {
let (arbiter_sender, arbiter_receiver) = network.register(
handlers::DKG_CHANNEL,
Quota::per_second(NZU32!(10)),
DEFAULT_MESSAGE_BACKLOG,
);
let arbiter: handlers::Arbiter<_, PublicKey> = handlers::Arbiter::new(
context.with_label("arbiter"),
DKG_FREQUENCY,
DKG_PHASE_TIMEOUT,
contributors.into_iter().collect(),
);
arbiter.start(arbiter_sender, arbiter_receiver);
}
network.start().await.expect("Network failed");
});
}