From 0d641967e77ab50b57a1298e3cb5c24a84c4da44 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:31:17 -0800 Subject: [PATCH] [CLI] Make the number of validators configurable for `sui genesis` and `sui start` (#20511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR enables `sui genesis` and `sui start` to pass the `committee-size` argument specifying the number of validators that should be started in the local network. Note that if `sui start --committee-size 3` is executed with no `.sui` or `genesis` data, on a subsequent run the `--committee-size` arg will be ignored. ## Test plan Locally. Checked the number of generated config files for validators + GraphQL query. ``` sui start --committee-size 3 --with-graphql ➜ sui_config ls -l total 3104 -rw-r--r-- 1 s staff 5338 Dec 4 21:15 127.0.0.1-51144.yaml -rw-r--r-- 1 s staff 5338 Dec 4 21:15 127.0.0.1-51158.yaml -rw-r--r-- 1 s staff 5338 Dec 4 21:15 127.0.0.1-51172.yaml drwxr-xr-x 5 s staff 160 Dec 4 21:15 authorities_db/ --- ``` ```json { "data": { "epoch": { "validatorSet": { "pendingActiveValidatorsSize": 0, "activeValidators": { "nodes": [ { "address": { "address": "0xf6f511d0d450bfe2cc6cfabcc00024fcef2ede7a63b47eefcf0c086249fb4642" }, "name": "validator-1", "description": "" }, { "address": { "address": "0x478572ac85ffeba294d8cbbb87ae4c2b0c223a224ec34e9b75c45b5c210a9fe5" }, "name": "validator-0", "description": "" }, { "address": { "address": "0x7242e93071987c25454a64ae07f6640008d706647e2b9106ac61ac3d2e2d47af" }, "name": "validator-2", "description": "" } ] }, "totalStake": "0" } } } } ``` ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [x] CLI: Added the `--committee-size` argument to `sui start` and `sui genesis` to configure the number of validators to start with the local network / when generating a genesis. - [ ] Rust SDK: - [ ] REST API: --- crates/sui/src/sui_commands.rs | 94 +++++++++++++++++++++++++++++----- crates/sui/tests/cli_tests.rs | 3 ++ 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/crates/sui/src/sui_commands.rs b/crates/sui/src/sui_commands.rs index 7a9a80eb48ded..2b5920f99f89b 100644 --- a/crates/sui/src/sui_commands.rs +++ b/crates/sui/src/sui_commands.rs @@ -213,6 +213,11 @@ pub enum SuiCommand { /// Start the network without a fullnode #[clap(long = "no-full-node")] no_full_node: bool, + /// Set the number of validators in the network. If a genesis was already generated with a + /// specific number of validators, this will not override it; the user should recreate the + /// genesis with the desired number of validators. + #[clap(long)] + committee_size: Option, }, #[clap(name = "network")] Network { @@ -250,6 +255,9 @@ pub enum SuiCommand { help = "Creates an extra faucet configuration for sui persisted runs." )] with_faucet: bool, + /// Set number of validators in the network. + #[clap(long)] + committee_size: Option, }, GenesisCeremony(Ceremony), /// Sui keystore tool. @@ -378,6 +386,7 @@ impl SuiCommand { data_ingestion_dir, no_full_node, epoch_duration_ms, + committee_size, } => { start( config_dir.clone(), @@ -388,6 +397,7 @@ impl SuiCommand { fullnode_rpc_port, data_ingestion_dir, no_full_node, + committee_size, ) .await?; @@ -401,6 +411,7 @@ impl SuiCommand { epoch_duration_ms, benchmark_ips, with_faucet, + committee_size, } => { genesis( from_config, @@ -410,6 +421,7 @@ impl SuiCommand { epoch_duration_ms, benchmark_ips, with_faucet, + committee_size, ) .await } @@ -612,6 +624,7 @@ async fn start( fullnode_rpc_port: u16, mut data_ingestion_dir: Option, no_full_node: bool, + committee_size: Option, ) -> Result<(), anyhow::Error> { if force_regenesis { ensure!( @@ -656,8 +669,12 @@ async fn start( // If this is set, then no data will be persisted between runs, and a new genesis will be // generated each run. let config_dir = if force_regenesis { - swarm_builder = - swarm_builder.committee_size(NonZeroUsize::new(DEFAULT_NUMBER_OF_AUTHORITIES).unwrap()); + let committee_size = match committee_size { + Some(x) => NonZeroUsize::new(x), + None => NonZeroUsize::new(DEFAULT_NUMBER_OF_AUTHORITIES), + } + .ok_or_else(|| anyhow!("Committee size must be at least 1."))?; + swarm_builder = swarm_builder.committee_size(committee_size); let genesis_config = GenesisConfig::custom_genesis(1, 100); swarm_builder = swarm_builder.with_genesis_config(genesis_config); let epoch_duration_ms = epoch_duration_ms.unwrap_or(DEFAULT_EPOCH_DURATION_MS); @@ -674,30 +691,76 @@ async fn start( .extension() .is_some_and(|e| e == "yml" || e == "yaml") => { + if committee_size.is_some() { + eprintln!( + "{}", + "[warning] The committee-size arg wil be ignored as a network \ + configuration already exists. To change the committee-size, you'll \ + have to adjust the network configuration file or regenerate a genesis \ + with the desired committee size. See `sui genesis --help` for more \ + information." + .yellow() + .bold() + ); + } (config, sui_config_dir()?) } - Some(config) => (config.join(SUI_NETWORK_CONFIG), config), + Some(config) => { + if committee_size.is_some() { + eprintln!( + "{}", + "[warning] The committee-size arg wil be ignored as a network \ + configuration already exists. To change the committee-size, you'll \ + have to adjust the network configuration file or regenerate a genesis \ + with the desired committee size. See `sui genesis --help` for more \ + information." + .yellow() + .bold() + ); + } + (config.join(SUI_NETWORK_CONFIG), config) + } None => { let sui_config = sui_config_dir()?; let network_config = sui_config.join(SUI_NETWORK_CONFIG); if !network_config.exists() { - genesis(None, None, None, false, epoch_duration_ms, None, false) - .await - .map_err(|_| { - anyhow!( - "Cannot run genesis with non-empty Sui config directory: {}.\n\n\ + genesis( + None, + None, + None, + false, + epoch_duration_ms, + None, + false, + committee_size, + ) + .await + .map_err(|_| { + anyhow!( + "Cannot run genesis with non-empty Sui config directory: {}.\n\n\ If you are trying to run a local network without persisting the \ data (so a new genesis that is randomly generated and will not be \ saved once the network is shut down), use --force-regenesis flag.\n\ If you are trying to persist the network data and start from a new \ genesis, use sui genesis --help to see how to generate a new \ genesis.", - sui_config.display(), - ) - })?; + sui_config.display(), + ) + })?; + } else if committee_size.is_some() { + eprintln!( + "{}", + "[warning] The committee-size arg wil be ignored as a network \ + configuration already exists. To change the committee-size, you'll \ + have to adjust the network configuration file or regenerate a genesis \ + with the desired committee size. See `sui genesis --help` for more \ + information." + .yellow() + .bold() + ); } (network_config, sui_config) @@ -888,6 +951,7 @@ async fn genesis( epoch_duration_ms: Option, benchmark_ips: Option>, with_faucet: bool, + committee_size: Option, ) -> Result<(), anyhow::Error> { let sui_config_dir = &match working_dir { // if a directory is specified, it must exist (it @@ -994,6 +1058,12 @@ async fn genesis( if let Some(epoch_duration_ms) = epoch_duration_ms { genesis_conf.parameters.epoch_duration_ms = epoch_duration_ms; } + let committee_size = match committee_size { + Some(x) => NonZeroUsize::new(x), + None => NonZeroUsize::new(DEFAULT_NUMBER_OF_AUTHORITIES), + } + .ok_or_else(|| anyhow!("Committee size must be at least 1."))?; + let mut network_config = if let Some(validators) = validator_info { builder .with_genesis_config(genesis_conf) @@ -1001,7 +1071,7 @@ async fn genesis( .build() } else { builder - .committee_size(NonZeroUsize::new(DEFAULT_NUMBER_OF_AUTHORITIES).unwrap()) + .committee_size(committee_size) .with_genesis_config(genesis_conf) .build() }; diff --git a/crates/sui/tests/cli_tests.rs b/crates/sui/tests/cli_tests.rs index 3a1a86fef4d31..92a658b16b3eb 100644 --- a/crates/sui/tests/cli_tests.rs +++ b/crates/sui/tests/cli_tests.rs @@ -76,6 +76,7 @@ async fn test_genesis() -> Result<(), anyhow::Error> { fullnode_rpc_port: 9000, epoch_duration_ms: None, no_full_node: false, + committee_size: None, indexer_feature_args: IndexerArgs::for_testing(), } .execute() @@ -90,6 +91,7 @@ async fn test_genesis() -> Result<(), anyhow::Error> { epoch_duration_ms: None, benchmark_ips: None, with_faucet: false, + committee_size: None, } .execute() .await?; @@ -129,6 +131,7 @@ async fn test_genesis() -> Result<(), anyhow::Error> { epoch_duration_ms: None, benchmark_ips: None, with_faucet: false, + committee_size: None, } .execute() .await;