#![doc = include_str!("../README.md")]
use crate::{
application::{EdScheme, ThresholdScheme},
dkg::{ContinueOnUpdate, PostUpdate, Update, UpdateCallBack},
setup::ParticipantConfig,
};
use clap::{Args, Parser, Subcommand};
use commonware_codec::Encode;
use commonware_consensus::simplex::elector::{Random, RoundRobin};
use commonware_cryptography::{bls12381::primitives::variant::MinSig, ed25519::PublicKey};
use commonware_runtime::{
tokio::{self, telemetry::Logging},
Metrics, Runner,
};
use commonware_utils::{hex, NZU64};
use std::{future::Future, num::NonZeroU64, path::PathBuf, pin::Pin};
use tracing::Level;
mod application;
mod dkg;
mod engine;
mod namespace;
mod orchestrator;
mod setup;
mod validator;
struct SaveFileOnUpdate {
path: PathBuf,
}
impl SaveFileOnUpdate {
pub fn boxed(path: PathBuf) -> Box<Self> {
Box::new(Self { path })
}
}
impl UpdateCallBack<MinSig, PublicKey> for SaveFileOnUpdate {
fn on_update(
&mut self,
update: Update<MinSig, PublicKey>,
) -> Pin<Box<dyn Future<Output = PostUpdate> + Send>> {
let config_path = self.path.clone();
Box::pin(async move {
match update {
Update::Failure { .. } => PostUpdate::Continue,
Update::Success { output, share, .. } => {
let config_str =
std::fs::read_to_string(&config_path).expect("failed to read config file");
let config: ParticipantConfig = serde_json::from_str(&config_str)
.expect("Failed to deserialize participant configuration");
config.update_and_write(&config_path, |config| {
config.output = Some(hex(output.encode().as_ref()));
config.share = share;
});
PostUpdate::Stop
}
}
})
}
}
pub const BLOCKS_PER_EPOCH: NonZeroU64 = NZU64!(200);
#[derive(Parser)]
pub struct App {
#[arg(long, default_value_t = Level::INFO)]
log_level: Level,
#[arg(long, default_value_t = 3)]
worker_threads: usize,
#[command(subcommand)]
subcommand: Subcommands,
}
#[derive(Subcommand)]
pub enum Subcommands {
Setup(SetupArgs),
Dkg(ParticipantArgs),
Validator(ParticipantArgs),
}
#[derive(Args)]
pub struct SetupArgs {
#[arg(long, default_value_t = 6)]
num_peers: u32,
#[arg(long, default_value_t = 2)]
num_bootstrappers: usize,
#[arg(long, default_value_t = 4)]
num_participants_per_epoch: u32,
#[arg(long, default_value = "./data")]
datadir: PathBuf,
#[arg(long, default_value_t = 3000)]
base_port: u16,
#[arg(long, default_value_t = false)]
with_dkg: bool,
}
#[derive(Args)]
pub struct ParticipantArgs {
#[arg(long = "cfg")]
config_path: PathBuf,
#[arg(long = "peers")]
peers_path: PathBuf,
}
fn main() {
let app = App::parse();
let config = tokio::Config::new()
.with_worker_threads(app.worker_threads)
.with_catch_panics(false);
let runner = tokio::Runner::new(config);
runner.start(|context| async move {
tokio::telemetry::init(
context.with_label("telemetry"),
Logging {
level: app.log_level,
json: false,
},
None,
None,
);
match app.subcommand {
Subcommands::Setup(args) => setup::run(args),
Subcommands::Dkg(args) => {
let config_path = args.config_path.clone();
validator::run::<EdScheme, RoundRobin>(
context,
args,
SaveFileOnUpdate::boxed(config_path),
)
.await;
}
Subcommands::Validator(args) => {
validator::run::<ThresholdScheme<MinSig>, Random>(
context,
args,
ContinueOnUpdate::boxed(),
)
.await
}
}
});
}