Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ffe9533
Create DKG initial commit.
mskrzypkows Apr 1, 2026
3790758
Merge branch 'main' of github.com:NethermindEth/pluto into createdkg
mskrzypkows Apr 1, 2026
2aa66f5
Small improvements, better logging.
mskrzypkows Apr 1, 2026
b3d0126
improvements
mskrzypkows Apr 2, 2026
1ccaa57
Improved tests, joined some using test-case crate.
mskrzypkows Apr 2, 2026
fb6cee4
Merge branch 'main' of github.com:NethermindEth/pluto into createdkg
mskrzypkows Apr 8, 2026
a48d280
CreateDkgError enum for creating DKG config
mskrzypkows Apr 8, 2026
f67be4c
Merge branch 'main' into createdkg
mskrzypkows Apr 13, 2026
95c05e3
Merge branch 'main' of github.com:NethermindEth/pluto into createdkg
mskrzypkows Apr 15, 2026
2f38181
Merge branch 'createdkg' of github.com:NethermindEth/pluto into creat…
mskrzypkows Apr 15, 2026
5a7441c
cargo deny fixed
mskrzypkows Apr 15, 2026
31b738d
Merge branch 'main' of github.com:NethermindEth/pluto into createdkg
mskrzypkows Apr 16, 2026
457555a
review corrections part 1
mskrzypkows Apr 16, 2026
1580d10
loginfo version
mskrzypkows Apr 16, 2026
84476db
Added EthClient::Noop to match Charon's implementation
mskrzypkows Apr 16, 2026
50c1514
Merge branch 'main' of github.com:NethermindEth/pluto into createdkg
mskrzypkows Apr 16, 2026
8ee19df
Validation address module
mskrzypkows Apr 16, 2026
ebd9b79
constants file
mskrzypkows Apr 16, 2026
ce8dfdf
threshold from helpers
mskrzypkows Apr 16, 2026
874eb53
removed holesky from create_dkg.rs
mskrzypkows Apr 16, 2026
075af28
cont.
mskrzypkows Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ hex.workspace = true
humantime.workspace = true
tokio.workspace = true
pluto-app.workspace = true
pluto-eth1wrap.workspace = true
pluto-cluster.workspace = true
pluto-crypto.workspace = true
pluto-relay-server.workspace = true
pluto-tracing.workspace = true
pluto-core.workspace = true
pluto-p2p.workspace = true
pluto-eth1wrap.workspace = true
pluto-eth2api.workspace = true
pluto-eth2util.workspace = true
pluto-k1util.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use clap::{Parser, Subcommand};

use crate::commands::{
create_cluster::CreateClusterArgs,
create_dkg::CreateDkgArgs,
create_enr::CreateEnrArgs,
enr::EnrArgs,
relay::RelayArgs,
Expand Down Expand Up @@ -133,6 +134,10 @@ pub struct CreateArgs {
/// Create subcommands
#[derive(Subcommand)]
pub enum CreateCommands {
/// Create a cluster definition file for a new Distributed Key Generation
/// ceremony
Dkg(Box<CreateDkgArgs>),

/// Create an Ethereum Node Record (ENR) private key to identify this charon
/// client
Enr(CreateEnrArgs),
Expand Down
78 changes: 78 additions & 0 deletions crates/cli/src/commands/address_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#[derive(thiserror::Error, Debug)]
pub enum AddressValidationError {
/// Value exceeds usize::MAX.
#[error("Value {value} exceeds usize::MAX")]
ValueExceedsUsize {
/// The value that exceeds usize::MAX.
value: u64,
},

/// Mismatching number of fee recipient addresses.
#[error(
"mismatching --num-validators and --fee-recipient-addresses: num_validators={num_validators}, addresses={addresses}"
)]
MismatchingFeeRecipientAddresses {
/// Number of validators.
num_validators: u64,
/// Number of addresses.
addresses: usize,
},

/// Mismatching number of withdrawal addresses.
#[error(
"mismatching --num-validators and --withdrawal-addresses: num_validators={num_validators}, addresses={addresses}"
)]
MismatchingWithdrawalAddresses {
/// Number of validators.
num_validators: u64,
/// Number of addresses.
addresses: usize,
},
}

type Result<T> = std::result::Result<T, AddressValidationError>;

/// Validates that addresses match the number of validators.
/// If only one address is provided, it fills the slice to match num_validators.
///
/// Returns an error if the number of addresses doesn't match and isn't exactly
/// 1.
pub(super) fn validate_addresses(
num_validators: u64,
fee_recipient_addrs: &[String],
withdrawal_addrs: &[String],
) -> Result<(Vec<String>, Vec<String>)> {
let num_validators_usize =
usize::try_from(num_validators).map_err(|_| AddressValidationError::ValueExceedsUsize {
value: num_validators,
})?;

if fee_recipient_addrs.len() != num_validators_usize && fee_recipient_addrs.len() != 1 {
return Err(AddressValidationError::MismatchingFeeRecipientAddresses {
num_validators,
addresses: fee_recipient_addrs.len(),
});
}

if withdrawal_addrs.len() != num_validators_usize && withdrawal_addrs.len() != 1 {
return Err(AddressValidationError::MismatchingWithdrawalAddresses {
num_validators,
addresses: withdrawal_addrs.len(),
});
}

let mut fee_addrs = fee_recipient_addrs.to_vec();
let mut withdraw_addrs = withdrawal_addrs.to_vec();

if fee_addrs.len() == 1 {
let addr = fee_addrs[0].clone();
fee_addrs = vec![addr; num_validators_usize];
}

if withdraw_addrs.len() == 1 {
let addr = withdraw_addrs[0].clone();
withdraw_addrs = vec![addr; num_validators_usize];
}

Ok((fee_addrs, withdraw_addrs))
}
4 changes: 4 additions & 0 deletions crates/cli/src/commands/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub(super) const DEFAULT_NETWORK: &str = "mainnet";
pub(super) const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";
pub(crate) const MIN_NODES: u64 = 3;
pub(crate) const MIN_THRESHOLD: u64 = 2;
58 changes: 5 additions & 53 deletions crates/cli/src/commands/create_cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,17 @@ use rand::rngs::OsRng;
use tracing::{debug, info, warn};

use crate::{
commands::create_dkg,
commands::{
address_validation::validate_addresses,
constants::{MIN_NODES, MIN_THRESHOLD},
create_dkg,
},
error::{
CliError, CreateClusterError, InvalidNetworkConfigError, Result as CliResult,
ThresholdError,
},
};

/// Minimum number of nodes required in a cluster.
pub const MIN_NODES: u64 = 3;
/// Minimum threshold value.
pub const MIN_THRESHOLD: u64 = 2;
/// Zero ethereum address (not allowed on mainnet/gnosis).
pub const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";
/// HTTP scheme.
const HTTP_SCHEME: &str = "http";
/// HTTPS scheme.
Expand Down Expand Up @@ -1210,52 +1208,6 @@ async fn load_definition(
Ok(def)
}

/// Validates that addresses match the number of validators.
/// If only one address is provided, it fills the slice to match num_validators.
///
/// Returns an error if the number of addresses doesn't match and isn't exactly
/// 1.
fn validate_addresses(
num_validators: u64,
fee_recipient_addrs: &[String],
withdrawal_addrs: &[String],
) -> Result<(Vec<String>, Vec<String>)> {
let num_validators_usize =
usize::try_from(num_validators).map_err(|_| CreateClusterError::ValueExceedsUsize {
value: num_validators,
})?;

if fee_recipient_addrs.len() != num_validators_usize && fee_recipient_addrs.len() != 1 {
return Err(CreateClusterError::MismatchingFeeRecipientAddresses {
num_validators,
addresses: fee_recipient_addrs.len(),
});
}

if withdrawal_addrs.len() != num_validators_usize && withdrawal_addrs.len() != 1 {
return Err(CreateClusterError::MismatchingWithdrawalAddresses {
num_validators,
addresses: withdrawal_addrs.len(),
});
}

let mut fee_addrs = fee_recipient_addrs.to_vec();
let mut withdraw_addrs = withdrawal_addrs.to_vec();

// Expand single address to match num_validators
if fee_addrs.len() == 1 {
let addr = fee_addrs[0].clone();
fee_addrs = vec![addr; num_validators_usize];
}

if withdraw_addrs.len() == 1 {
let addr = withdraw_addrs[0].clone();
withdraw_addrs = vec![addr; num_validators_usize];
}

Ok((fee_addrs, withdraw_addrs))
}

/// Returns the safe threshold, logging a warning if a non-standard threshold is
/// provided.
fn safe_threshold(num_nodes: u64, threshold: Option<u64>) -> u64 {
Expand Down
Loading
Loading