diff --git a/crates/bin/pcli/src/command/init.rs b/crates/bin/pcli/src/command/init.rs index d60b2eb76c..4af57486a4 100644 --- a/crates/bin/pcli/src/command/init.rs +++ b/crates/bin/pcli/src/command/init.rs @@ -2,11 +2,15 @@ use std::{io::Read, str::FromStr}; use anyhow::Result; use camino::Utf8PathBuf; +use penumbra_custody::threshold; use penumbra_keys::keys::{Bip44Path, SeedPhrase, SpendKey}; use rand_core::OsRng; use url::Url; -use crate::config::{CustodyConfig, PcliConfig}; +use crate::{ + config::{CustodyConfig, PcliConfig}, + terminal::ActualTerminal, +}; #[derive(Debug, clap::Parser)] pub struct InitCmd { @@ -128,44 +132,45 @@ pub enum ThresholdInitCmd { #[clap(long, value_delimiter = ' ', multiple_values = true)] home: Vec, }, + /// Generate a config file without using a trusted dealer. + Dkg { + /// The minimum number of signers required to make a signature (>= 2). + #[clap(short, long)] + threshold: u16, + /// The maximum number of signers that can make a signature + #[clap(short, long)] + num_participants: u16, + }, } -impl ThresholdInitCmd { - fn exec(&self, grpc_url: Url) -> Result<()> { - use penumbra_custody::threshold; - - match self { - ThresholdInitCmd::Deal { threshold: t, home } => { - if *t < 2 { - anyhow::bail!("threshold must be >= 2"); - } - let n = home.len() as u16; - println!("Generating {}-of-{} threshold config.", t, n); - let configs = threshold::Config::deal(&mut OsRng, *t, n)?; - println!("Writing dealt config files..."); - for (i, (config, path)) in configs.into_iter().zip(home.iter()).enumerate() { - let full_viewing_key = config.fvk().clone(); - let config = PcliConfig { - custody: CustodyConfig::Threshold(config), - full_viewing_key, - grpc_url: grpc_url.clone(), - view_url: None, - disable_warning: false, - }; - println!(" Writing signer {} config to {}", i, path); - std::fs::create_dir_all(path)?; - config.save(path.join(crate::CONFIG_FILE_NAME))?; - } - Ok(()) - } - } +fn exec_deal(threshold: u16, home: Vec, grpc_url: Url) -> Result<()> { + if threshold < 2 { + anyhow::bail!("threshold must be >= 2"); } + let n = home.len() as u16; + println!("Generating {}-of-{} threshold config.", threshold, n); + let configs = threshold::Config::deal(&mut OsRng, threshold, n)?; + println!("Writing dealt config files..."); + for (i, (config, path)) in configs.into_iter().zip(home.iter()).enumerate() { + let full_viewing_key = config.fvk().clone(); + let config = PcliConfig { + custody: CustodyConfig::Threshold(config), + full_viewing_key, + grpc_url: grpc_url.clone(), + view_url: None, + disable_warning: false, + }; + println!(" Writing signer {} config to {}", i, path); + std::fs::create_dir_all(path)?; + config.save(path.join(crate::CONFIG_FILE_NAME))?; + } + Ok(()) } impl InitCmd { - pub fn exec(&self, home_dir: impl AsRef) -> Result<()> { - if let InitSubCmd::Threshold(cmd) = &self.subcmd { - cmd.exec(self.grpc_url.clone())?; + pub async fn exec(&self, home_dir: impl AsRef) -> Result<()> { + if let InitSubCmd::Threshold(ThresholdInitCmd::Deal { threshold, home }) = &self.subcmd { + exec_deal(threshold.clone(), home.clone(), self.grpc_url.clone())?; return Ok(()); } let home_dir = home_dir.as_ref(); @@ -196,7 +201,16 @@ impl InitCmd { CustodyConfig::SoftKms(spend_key.into()), ) } - InitSubCmd::Threshold(_) => panic!("this should already have been handled above"), + InitSubCmd::Threshold(ThresholdInitCmd::Dkg { + threshold, + num_participants, + }) => { + let config = threshold::dkg(*threshold, *num_participants, &ActualTerminal).await?; + (config.fvk().clone(), CustodyConfig::Threshold(config)) + } + InitSubCmd::Threshold(ThresholdInitCmd::Deal { .. }) => { + panic!("this should already have been handled above") + } InitSubCmd::ViewOnly { full_viewing_key } => { let full_viewing_key = full_viewing_key.parse()?; (full_viewing_key, CustodyConfig::ViewOnly) diff --git a/crates/bin/pcli/src/main.rs b/crates/bin/pcli/src/main.rs index 083b50ad55..5433a4365c 100644 --- a/crates/bin/pcli/src/main.rs +++ b/crates/bin/pcli/src/main.rs @@ -105,7 +105,7 @@ async fn main() -> Result<()> { // create the client state, so handle it specially here so that we can have // common code for the other subcommands. if let Command::Init(init_cmd) = &opt.cmd { - init_cmd.exec(opt.home.as_path())?; + init_cmd.exec(opt.home.as_path()).await?; return Ok(()); }