From a3529b086983f137d5469ced77c2b0ebb6ad9299 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:28:14 -0500 Subject: [PATCH 1/7] Allow running a faucet from linera net up --- linera-client/src/client_options.rs | 20 +++- linera-service/src/cli_wrappers/mod.rs | 3 +- linera-service/src/cli_wrappers/wallet.rs | 108 ++++++++++++---------- linera-service/src/linera/main.rs | 12 +++ linera-service/src/linera/net_up_utils.rs | 88 ++++++++++++++---- 5 files changed, 162 insertions(+), 69 deletions(-) diff --git a/linera-client/src/client_options.rs b/linera-client/src/client_options.rs index 267fc942950..ed10939d54d 100644 --- a/linera-client/src/client_options.rs +++ b/linera-client/src/client_options.rs @@ -712,7 +712,7 @@ pub enum ClientCommand { config: ChainListenerConfig, /// The port on which to run the server - #[arg(long = "port", default_value = "8080")] + #[arg(long, default_value = "8080")] port: NonZeroU16, }, @@ -723,11 +723,11 @@ pub enum ClientCommand { chain_id: Option, /// The port on which to run the server - #[arg(long = "port", default_value = "8080")] + #[arg(long, default_value = "8080")] port: NonZeroU16, /// The number of tokens to send to each new chain. - #[arg(long = "amount")] + #[arg(long)] amount: Amount, /// The end timestamp: The faucet will rate-limit the token supply so it runs out of money @@ -980,6 +980,7 @@ impl DatabaseToolCommand { } } +#[expect(clippy::large_enum_variant)] #[derive(Clone, clap::Parser)] pub enum NetCommand { /// Start a Local Linera Network @@ -1052,6 +1053,19 @@ pub enum NetCommand { /// External protocol used, either grpc or grpcs. #[arg(long, default_value = "grpc")] external_protocol: String, + + /// If present, a faucet is started using the given chain root nummber (0 for the + /// admin chain, 1 for the first non-admin initial chain, etc). + #[arg(long)] + with_faucet_chain: Option, + + /// The port on which to run the faucet server + #[arg(long, default_value = "8080")] + faucet_port: NonZeroU16, + + /// The number of tokens to send to each new chain created by the faucet. + #[arg(long, default_value = "1000")] + faucet_amount: Amount, }, /// Print a bash helper script to make `linera net up` easier to use. The script is diff --git a/linera-service/src/cli_wrappers/mod.rs b/linera-service/src/cli_wrappers/mod.rs index 0462aac4c96..bd05d3ab1d3 100644 --- a/linera-service/src/cli_wrappers/mod.rs +++ b/linera-service/src/cli_wrappers/mod.rs @@ -35,7 +35,8 @@ use anyhow::Result; use async_trait::async_trait; use linera_execution::ResourceControlPolicy; pub use wallet::{ - ApplicationWrapper, ClientWrapper, Faucet, FaucetOption, NodeService, OnClientDrop, + ApplicationWrapper, ClientWrapper, Faucet, FaucetOption, FaucetService, NodeService, + OnClientDrop, }; /// The information needed to start a Linera net of a particular kind. diff --git a/linera-service/src/cli_wrappers/wallet.rs b/linera-service/src/cli_wrappers/wallet.rs index c58655a8cf9..e4c9210dcfb 100644 --- a/linera-service/src/cli_wrappers/wallet.rs +++ b/linera-service/src/cli_wrappers/wallet.rs @@ -32,7 +32,7 @@ use serde::{de::DeserializeOwned, ser::Serialize}; use serde_json::{json, Value}; use tempfile::TempDir; use tokio::process::{Child, Command}; -use tracing::{error, info, warn}; +use tracing::{info, warn}; use crate::{ cli_wrappers::{ @@ -101,6 +101,53 @@ impl ClientWrapper { } } + /// Runs `linera wallet show --short --owned` to find the list of chain ids in the + /// wallet. Not async to be usable in `Drop`. + pub fn find_owned_chains(&self) -> Result> { + let binary_path = self.binary_path.lock().unwrap(); + + let Some(binary_path) = binary_path.as_ref() else { + bail!( + "The command binary was never resolved. \ + Make sure that the client has been used once before." + ); + }; + + let working_directory = self.path_provider.path(); + let mut wallet_show_command = std::process::Command::new(binary_path); + + for argument in self.command_arguments() { + wallet_show_command.arg(&*argument); + } + + let Ok(wallet_show_output) = wallet_show_command + .current_dir(working_directory) + .args(["wallet", "show", "--short", "--owned"]) + .output() + else { + bail!("Failed to execute `wallet show --short` to list chains in the wallet"); + }; + + if !wallet_show_output.status.success() { + bail!("Failed to list chains in the wallet"); + } + + let Ok(chain_list_string) = String::from_utf8(wallet_show_output.stdout) else { + bail!( + "Failed to list chains because `linera wallet show --short` \ + returned a non-UTF-8 output" + ); + }; + + let chain_ids = chain_list_string + .split('\n') + .map(|line| line.trim().to_string()) + .filter(|line| !line.is_empty()) + .collect(); + + Ok(chain_ids) + } + /// Runs `linera project new`. pub async fn project_new(&self, project_name: &str, linera_root: &Path) -> Result { let tmp = TempDir::new()?; @@ -958,61 +1005,25 @@ impl ClientWrapper { impl Drop for ClientWrapper { fn drop(&mut self) { - use std::process::Command as SyncCommand; - if self.on_drop != OnClientDrop::CloseChains { return; } - let Ok(binary_path) = self.binary_path.lock() else { - error!("Failed to close chains because a thread panicked with a lock to `binary_path`"); - return; + let chain_ids = match self.find_owned_chains() { + Ok(ids) => ids, + Err(err) => { + warn!("Skipping closing chains because of error: {}", err); + return; + } }; - let Some(binary_path) = binary_path.as_ref() else { - warn!( - "Assuming no chains need to be closed, because the command binary was never \ - resolved and therefore presumably never called" - ); - return; - }; + let binary_path = self.binary_path.lock().unwrap(); + let binary_path = binary_path.as_ref().expect("Binary was used successfully"); let working_directory = self.path_provider.path(); - let mut wallet_show_command = SyncCommand::new(binary_path); - - for argument in self.command_arguments() { - wallet_show_command.arg(&*argument); - } - - let Ok(wallet_show_output) = wallet_show_command - .current_dir(working_directory) - .args(["wallet", "show", "--short", "--owned"]) - .output() - else { - warn!("Failed to execute `wallet show --short` to list chains to close"); - return; - }; - - if !wallet_show_output.status.success() { - warn!("Failed to list chains in the wallet to close them"); - return; - } - - let Ok(chain_list_string) = String::from_utf8(wallet_show_output.stdout) else { - warn!( - "Failed to close chains because `linera wallet show --short` \ - returned a non-UTF-8 output" - ); - return; - }; - - let chain_ids = chain_list_string - .split('\n') - .map(|line| line.trim()) - .filter(|line| !line.is_empty()); for chain_id in chain_ids { - let mut close_chain_command = SyncCommand::new(binary_path); + let mut close_chain_command = std::process::Command::new(binary_path); for argument in self.command_arguments() { close_chain_command.arg(&*argument); @@ -1020,7 +1031,10 @@ impl Drop for ClientWrapper { close_chain_command.current_dir(working_directory); - match close_chain_command.args(["close-chain", chain_id]).status() { + match close_chain_command + .args(["close-chain", &chain_id]) + .status() + { Ok(status) if status.success() => (), Ok(failure) => warn!("Failed to close chain {chain_id}: {failure}"), Err(error) => warn!("Failed to close chain {chain_id}: {error}"), diff --git a/linera-service/src/linera/main.rs b/linera-service/src/linera/main.rs index dc8b9f2bf69..005e9d356ef 100644 --- a/linera-service/src/linera/main.rs +++ b/linera-service/src/linera/main.rs @@ -1639,6 +1639,9 @@ async fn run(options: &ClientOptions) -> Result { path: _, storage: _, external_protocol: _, + with_faucet_chain, + faucet_port, + faucet_amount, } => { net_up_utils::handle_net_up_kubernetes( *extra_wallets, @@ -1651,6 +1654,9 @@ async fn run(options: &ClientOptions) -> Result { *no_build, docker_image_name.clone(), policy_config.into_policy(), + *with_faucet_chain, + *faucet_port, + *faucet_amount, ) .boxed() .await?; @@ -1668,6 +1674,9 @@ async fn run(options: &ClientOptions) -> Result { path, storage, external_protocol, + with_faucet_chain, + faucet_port, + faucet_amount, .. } => { net_up_utils::handle_net_up_service( @@ -1681,6 +1690,9 @@ async fn run(options: &ClientOptions) -> Result { path, storage, external_protocol.clone(), + *with_faucet_chain, + *faucet_port, + *faucet_amount, ) .boxed() .await?; diff --git a/linera-service/src/linera/net_up_utils.rs b/linera-service/src/linera/net_up_utils.rs index 669527bc2e4..3f3ee59304c 100644 --- a/linera-service/src/linera/net_up_utils.rs +++ b/linera-service/src/linera/net_up_utils.rs @@ -1,7 +1,7 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; +use std::{num::NonZeroU16, str::FromStr}; use colored::Colorize as _; use linera_base::{data_types::Amount, time::Duration}; @@ -10,7 +10,8 @@ use linera_execution::ResourceControlPolicy; use linera_service::{ cli_wrappers::{ local_net::{Database, LocalNetConfig, PathProvider, StorageConfigBuilder}, - ClientWrapper, FaucetOption, LineraNet, LineraNetConfig, Network, NetworkConfig, + ClientWrapper, FaucetOption, FaucetService, LineraNet, LineraNetConfig, Network, + NetworkConfig, }, util::listen_for_shutdown_signals, }; @@ -113,6 +114,9 @@ pub async fn handle_net_up_kubernetes( no_build: bool, docker_image_name: String, policy: ResourceControlPolicy, + with_faucet_chain: Option, + faucet_port: NonZeroU16, + faucet_amount: Amount, ) -> anyhow::Result<()> { if num_initial_validators < 1 { panic!("The local test network must have at least one validator."); @@ -136,9 +140,17 @@ pub async fn handle_net_up_kubernetes( docker_image_name, policy, }; - let (mut net, client1) = config.instantiate().await?; - net_up(extra_wallets, &mut net, client1).await?; - wait_for_shutdown(shutdown_notifier, &mut net).await + let (mut net, client) = config.instantiate().await?; + let faucet_service = create_wallets_and_faucets( + extra_wallets, + &mut net, + client, + with_faucet_chain, + faucet_port, + faucet_amount, + ) + .await?; + wait_for_shutdown(shutdown_notifier, &mut net, faucet_service).await } #[expect(clippy::too_many_arguments)] @@ -153,6 +165,9 @@ pub async fn handle_net_up_service( path: &Option, storage: &Option, external_protocol: String, + with_faucet_chain: Option, + faucet_port: NonZeroU16, + faucet_amount: Amount, ) -> anyhow::Result<()> { if num_initial_validators < 1 { panic!("The local test network must have at least one validator."); @@ -190,29 +205,46 @@ pub async fn handle_net_up_service( storage_config_builder, path_provider, }; - let (mut net, client1) = config.instantiate().await?; - net_up(extra_wallets, &mut net, client1).await?; - wait_for_shutdown(shutdown_notifier, &mut net).await + let (mut net, client) = config.instantiate().await?; + let faucet_service = create_wallets_and_faucets( + extra_wallets, + &mut net, + client, + with_faucet_chain, + faucet_port, + faucet_amount, + ) + .await?; + wait_for_shutdown(shutdown_notifier, &mut net, faucet_service).await } async fn wait_for_shutdown( shutdown_notifier: CancellationToken, net: &mut impl LineraNet, + faucet_service: Option, ) -> anyhow::Result<()> { shutdown_notifier.cancelled().await; - eprintln!("\nTerminating the local test network"); + eprintln!(); + if let Some(service) = faucet_service { + eprintln!("Terminating the faucet service"); + service.terminate().await?; + } + eprintln!("Terminating the local test network"); net.terminate().await?; - eprintln!("\nDone."); + eprintln!("Done."); Ok(()) } -async fn net_up( +async fn create_wallets_and_faucets( extra_wallets: Option, net: &mut impl LineraNet, - client1: ClientWrapper, -) -> Result<(), anyhow::Error> { - let default_chain = client1 + client: ClientWrapper, + with_faucet_chain: Option, + faucet_port: NonZeroU16, + faucet_amount: Amount, +) -> Result, anyhow::Error> { + let default_chain = client .default_chain() .expect("Initialized clients should always have a default chain"); @@ -241,7 +273,7 @@ async fn net_up( "{}", format!( "export LINERA_WALLET{suffix}=\"{}\"", - client1.wallet_path().display() + client.wallet_path().display() ) .bold() ); @@ -249,7 +281,7 @@ async fn net_up( "{}", format!( "export LINERA_STORAGE{suffix}=\"{}\"\n", - client1.storage_path() + client.storage_path() ) .bold() ); @@ -260,7 +292,7 @@ async fn net_up( let extra_wallet = net.make_client().await; extra_wallet.wallet_init(&[], FaucetOption::None).await?; let unassigned_key = extra_wallet.keygen().await?; - let new_chain_msg_id = client1 + let new_chain_msg_id = client .open_chain(default_chain, Some(unassigned_key), Amount::ZERO) .await? .0; @@ -286,9 +318,29 @@ async fn net_up( } } + // Run the faucet, + let faucet_service = if let Some(faucet_chain) = with_faucet_chain { + let chain_ids = client + .find_owned_chains() + .expect("Cannot retrieve chains from linera wallet"); + let chain_id = chain_ids.get(faucet_chain as usize).expect( + "Was expecting a number `faucet_chain` in the range `0..=num_other_initial_chains`", + ); + let service = client + .run_faucet( + Some(faucet_port.into()), + str::parse(chain_id)?, + faucet_amount, + ) + .await?; + Some(service) + } else { + None + }; + eprintln!( "\nREADY!\nPress ^C to terminate the local test network and clean the temporary directory." ); - Ok(()) + Ok(faucet_service) } From 1b3b1498e4a321a155c66eb4388053fc41f452a2 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Sun, 12 Jan 2025 19:03:35 -0500 Subject: [PATCH 2/7] add test --- linera-service/tests/local_net_tests.rs | 59 ++++++++++++++++--------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/linera-service/tests/local_net_tests.rs b/linera-service/tests/local_net_tests.rs index 861c70cd87b..ef953ac03ce 100644 --- a/linera-service/tests/local_net_tests.rs +++ b/linera-service/tests/local_net_tests.rs @@ -23,7 +23,7 @@ use linera_service::{ local_net::{ get_node_port, Database, LocalNet, LocalNetConfig, PathProvider, ProcessInbox, }, - ClientWrapper, FaucetOption, LineraNet, LineraNetConfig, Network, OnClientDrop, + ClientWrapper, Faucet, FaucetOption, LineraNet, LineraNetConfig, Network, OnClientDrop, }, faucet::ClaimOutcome, test_name, @@ -733,7 +733,14 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { tracing::info!("Starting test {}", test_name!()); let mut command = Command::new(env!("CARGO_BIN_EXE_linera")); - command.args(["net", "up"]); + command.args([ + "net", + "up", + "--with-faucet-chain", + "1", + "--faucet-port", + "7999", + ]); let mut child = command .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -742,31 +749,39 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { let stdout = BufReader::new(child.stdout.take().unwrap()); let stderr = BufReader::new(child.stderr.take().unwrap()); + let mut is_ready = false; for line in stderr.lines() { let line = line?; if line.starts_with("READY!") { - let mut exports = stdout.lines(); - assert!(exports - .next() - .unwrap()? - .starts_with("export LINERA_WALLET=")); - assert!(exports - .next() - .unwrap()? - .starts_with("export LINERA_STORAGE=")); - assert_eq!(exports.next().unwrap()?, ""); - - // Send SIGINT to the child process. - Command::new("kill") - .args(["-s", "INT", &child.id().to_string()]) - .output()?; - - assert!(exports.next().is_none()); - assert!(child.wait()?.success()); - return Ok(()); + is_ready = true; + break; } } - panic!("Unexpected EOF for stderr"); + assert!(is_ready, "Unexpected EOF for stderr"); + + let mut exports = stdout.lines(); + assert!(exports + .next() + .unwrap()? + .starts_with("export LINERA_WALLET=")); + assert!(exports + .next() + .unwrap()? + .starts_with("export LINERA_STORAGE=")); + assert_eq!(exports.next().unwrap()?, ""); + + // Test faucet. + let faucet = Faucet::new("http://localhost:7999/".to_string()); + faucet.version_info().await.unwrap(); + + // Send SIGINT to the child process. + Command::new("kill") + .args(["-s", "INT", &child.id().to_string()]) + .output()?; + + assert!(exports.next().is_none()); + assert!(child.wait()?.success()); + return Ok(()); } #[cfg(feature = "benchmark")] From 5615151d9f35374726ae0c144b1a270e9b27d4d6 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Sun, 12 Jan 2025 19:32:48 -0500 Subject: [PATCH 3/7] fix CLI.md --- CLI.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CLI.md b/CLI.md index ac1df3a46ad..fe9df2a2f44 100644 --- a/CLI.md +++ b/CLI.md @@ -903,6 +903,13 @@ Start a Local Linera Network * `--external-protocol ` — External protocol used, either grpc or grpcs Default value: `grpc` +* `--with-faucet-chain ` — If present, a faucet is started using the given chain root nummber (0 for the admin chain, 1 for the first non-admin initial chain, etc) +* `--faucet-port ` — The port on which to run the faucet server + + Default value: `8080` +* `--faucet-amount ` — The number of tokens to send to each new chain created by the faucet + + Default value: `1000` From 9e9eea2c39fc5b31561cc7408953493fb410b68d Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Sun, 12 Jan 2025 19:39:58 -0500 Subject: [PATCH 4/7] nits --- linera-service/src/cli_wrappers/wallet.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/linera-service/src/cli_wrappers/wallet.rs b/linera-service/src/cli_wrappers/wallet.rs index e4c9210dcfb..74ffc87900f 100644 --- a/linera-service/src/cli_wrappers/wallet.rs +++ b/linera-service/src/cli_wrappers/wallet.rs @@ -1012,14 +1012,12 @@ impl Drop for ClientWrapper { let chain_ids = match self.find_owned_chains() { Ok(ids) => ids, Err(err) => { - warn!("Skipping closing chains because of error: {}", err); + warn!("Not closing chains because an error occurred: {}", err); return; } }; - let binary_path = self.binary_path.lock().unwrap(); - let binary_path = binary_path.as_ref().expect("Binary was used successfully"); - + let binary_path = binary_path.as_ref().expect("Binary was used before"); let working_directory = self.path_provider.path(); for chain_id in chain_ids { From ba70041ba1845e8f9a4ff8ac01adf8623aa8ff35 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Sun, 12 Jan 2025 21:03:27 -0500 Subject: [PATCH 5/7] fix CI --- linera-client/src/client_options.rs | 2 +- linera-service/tests/local_net_tests.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/linera-client/src/client_options.rs b/linera-client/src/client_options.rs index ed10939d54d..7ed763148a6 100644 --- a/linera-client/src/client_options.rs +++ b/linera-client/src/client_options.rs @@ -980,7 +980,7 @@ impl DatabaseToolCommand { } } -#[expect(clippy::large_enum_variant)] +#[allow(clippy::large_enum_variant)] #[derive(Clone, clap::Parser)] pub enum NetCommand { /// Start a Local Linera Network diff --git a/linera-service/tests/local_net_tests.rs b/linera-service/tests/local_net_tests.rs index ef953ac03ce..11bdb4c1e09 100644 --- a/linera-service/tests/local_net_tests.rs +++ b/linera-service/tests/local_net_tests.rs @@ -23,12 +23,14 @@ use linera_service::{ local_net::{ get_node_port, Database, LocalNet, LocalNetConfig, PathProvider, ProcessInbox, }, - ClientWrapper, Faucet, FaucetOption, LineraNet, LineraNetConfig, Network, OnClientDrop, + ClientWrapper, FaucetOption, LineraNet, LineraNetConfig, Network, OnClientDrop, }, faucet::ClaimOutcome, test_name, }; use test_case::test_case; +#[cfg(feature = "storage-service")] +use {linera_base::port::get_free_port, linera_service::cli_wrappers::Faucet}; #[cfg(feature = "benchmark")] fn get_fungible_account_owner(client: &ClientWrapper) -> AccountOwner { @@ -732,6 +734,8 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { let _guard = INTEGRATION_TEST_GUARD.lock().await; tracing::info!("Starting test {}", test_name!()); + let port = get_free_port().await?; + let mut command = Command::new(env!("CARGO_BIN_EXE_linera")); command.args([ "net", @@ -739,7 +743,7 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { "--with-faucet-chain", "1", "--faucet-port", - "7999", + &port.to_string(), ]); let mut child = command .stdout(Stdio::piped()) @@ -771,7 +775,7 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { assert_eq!(exports.next().unwrap()?, ""); // Test faucet. - let faucet = Faucet::new("http://localhost:7999/".to_string()); + let faucet = Faucet::new(format!("http://localhost:{}/", port)); faucet.version_info().await.unwrap(); // Send SIGINT to the child process. From 145a68b2638cff96c90f9417fbd5e242fc1d5d2f Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Sun, 12 Jan 2025 22:03:18 -0500 Subject: [PATCH 6/7] one more --- linera-service/tests/local_net_tests.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/linera-service/tests/local_net_tests.rs b/linera-service/tests/local_net_tests.rs index 11bdb4c1e09..cff06b5cbbb 100644 --- a/linera-service/tests/local_net_tests.rs +++ b/linera-service/tests/local_net_tests.rs @@ -752,9 +752,10 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { let stdout = BufReader::new(child.stdout.take().unwrap()); let stderr = BufReader::new(child.stderr.take().unwrap()); + let mut lines = stderr.lines(); let mut is_ready = false; - for line in stderr.lines() { + for line in &mut lines { let line = line?; if line.starts_with("READY!") { is_ready = true; @@ -763,6 +764,14 @@ async fn test_storage_service_linera_net_up_simple() -> Result<()> { } assert!(is_ready, "Unexpected EOF for stderr"); + // Echo faucet stderr for debugging and to empty the buffer. + std::thread::spawn(move || { + for line in lines { + let line = line.unwrap(); + eprintln!("{}", line); + } + }); + let mut exports = stdout.lines(); assert!(exports .next() From 50fe28e22001a67ee14b675a6d5d82d3282ed7e6 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:11:48 -0500 Subject: [PATCH 7/7] address reviewer's comment --- CLI.md | 2 +- linera-client/src/client_options.rs | 2 +- linera-service/src/cli_wrappers/wallet.rs | 110 ++++++++++------------ linera-service/src/linera/net_up_utils.rs | 10 +- 4 files changed, 53 insertions(+), 71 deletions(-) diff --git a/CLI.md b/CLI.md index fe9df2a2f44..8593878f6d4 100644 --- a/CLI.md +++ b/CLI.md @@ -903,7 +903,7 @@ Start a Local Linera Network * `--external-protocol ` — External protocol used, either grpc or grpcs Default value: `grpc` -* `--with-faucet-chain ` — If present, a faucet is started using the given chain root nummber (0 for the admin chain, 1 for the first non-admin initial chain, etc) +* `--with-faucet-chain ` — If present, a faucet is started using the given chain root number (0 for the admin chain, 1 for the first non-admin initial chain, etc) * `--faucet-port ` — The port on which to run the faucet server Default value: `8080` diff --git a/linera-client/src/client_options.rs b/linera-client/src/client_options.rs index 7ed763148a6..5c7195cc734 100644 --- a/linera-client/src/client_options.rs +++ b/linera-client/src/client_options.rs @@ -1054,7 +1054,7 @@ pub enum NetCommand { #[arg(long, default_value = "grpc")] external_protocol: String, - /// If present, a faucet is started using the given chain root nummber (0 for the + /// If present, a faucet is started using the given chain root number (0 for the /// admin chain, 1 for the first non-admin initial chain, etc). #[arg(long)] with_faucet_chain: Option, diff --git a/linera-service/src/cli_wrappers/wallet.rs b/linera-service/src/cli_wrappers/wallet.rs index 74ffc87900f..c58655a8cf9 100644 --- a/linera-service/src/cli_wrappers/wallet.rs +++ b/linera-service/src/cli_wrappers/wallet.rs @@ -32,7 +32,7 @@ use serde::{de::DeserializeOwned, ser::Serialize}; use serde_json::{json, Value}; use tempfile::TempDir; use tokio::process::{Child, Command}; -use tracing::{info, warn}; +use tracing::{error, info, warn}; use crate::{ cli_wrappers::{ @@ -101,53 +101,6 @@ impl ClientWrapper { } } - /// Runs `linera wallet show --short --owned` to find the list of chain ids in the - /// wallet. Not async to be usable in `Drop`. - pub fn find_owned_chains(&self) -> Result> { - let binary_path = self.binary_path.lock().unwrap(); - - let Some(binary_path) = binary_path.as_ref() else { - bail!( - "The command binary was never resolved. \ - Make sure that the client has been used once before." - ); - }; - - let working_directory = self.path_provider.path(); - let mut wallet_show_command = std::process::Command::new(binary_path); - - for argument in self.command_arguments() { - wallet_show_command.arg(&*argument); - } - - let Ok(wallet_show_output) = wallet_show_command - .current_dir(working_directory) - .args(["wallet", "show", "--short", "--owned"]) - .output() - else { - bail!("Failed to execute `wallet show --short` to list chains in the wallet"); - }; - - if !wallet_show_output.status.success() { - bail!("Failed to list chains in the wallet"); - } - - let Ok(chain_list_string) = String::from_utf8(wallet_show_output.stdout) else { - bail!( - "Failed to list chains because `linera wallet show --short` \ - returned a non-UTF-8 output" - ); - }; - - let chain_ids = chain_list_string - .split('\n') - .map(|line| line.trim().to_string()) - .filter(|line| !line.is_empty()) - .collect(); - - Ok(chain_ids) - } - /// Runs `linera project new`. pub async fn project_new(&self, project_name: &str, linera_root: &Path) -> Result { let tmp = TempDir::new()?; @@ -1005,23 +958,61 @@ impl ClientWrapper { impl Drop for ClientWrapper { fn drop(&mut self) { + use std::process::Command as SyncCommand; + if self.on_drop != OnClientDrop::CloseChains { return; } - let chain_ids = match self.find_owned_chains() { - Ok(ids) => ids, - Err(err) => { - warn!("Not closing chains because an error occurred: {}", err); - return; - } + let Ok(binary_path) = self.binary_path.lock() else { + error!("Failed to close chains because a thread panicked with a lock to `binary_path`"); + return; }; - let binary_path = self.binary_path.lock().unwrap(); - let binary_path = binary_path.as_ref().expect("Binary was used before"); + + let Some(binary_path) = binary_path.as_ref() else { + warn!( + "Assuming no chains need to be closed, because the command binary was never \ + resolved and therefore presumably never called" + ); + return; + }; + let working_directory = self.path_provider.path(); + let mut wallet_show_command = SyncCommand::new(binary_path); + + for argument in self.command_arguments() { + wallet_show_command.arg(&*argument); + } + + let Ok(wallet_show_output) = wallet_show_command + .current_dir(working_directory) + .args(["wallet", "show", "--short", "--owned"]) + .output() + else { + warn!("Failed to execute `wallet show --short` to list chains to close"); + return; + }; + + if !wallet_show_output.status.success() { + warn!("Failed to list chains in the wallet to close them"); + return; + } + + let Ok(chain_list_string) = String::from_utf8(wallet_show_output.stdout) else { + warn!( + "Failed to close chains because `linera wallet show --short` \ + returned a non-UTF-8 output" + ); + return; + }; + + let chain_ids = chain_list_string + .split('\n') + .map(|line| line.trim()) + .filter(|line| !line.is_empty()); for chain_id in chain_ids { - let mut close_chain_command = std::process::Command::new(binary_path); + let mut close_chain_command = SyncCommand::new(binary_path); for argument in self.command_arguments() { close_chain_command.arg(&*argument); @@ -1029,10 +1020,7 @@ impl Drop for ClientWrapper { close_chain_command.current_dir(working_directory); - match close_chain_command - .args(["close-chain", &chain_id]) - .status() - { + match close_chain_command.args(["close-chain", chain_id]).status() { Ok(status) if status.success() => (), Ok(failure) => warn!("Failed to close chain {chain_id}: {failure}"), Err(error) => warn!("Failed to close chain {chain_id}: {error}"), diff --git a/linera-service/src/linera/net_up_utils.rs b/linera-service/src/linera/net_up_utils.rs index 3f3ee59304c..ce0392b4dab 100644 --- a/linera-service/src/linera/net_up_utils.rs +++ b/linera-service/src/linera/net_up_utils.rs @@ -4,7 +4,7 @@ use std::{num::NonZeroU16, str::FromStr}; use colored::Colorize as _; -use linera_base::{data_types::Amount, time::Duration}; +use linera_base::{data_types::Amount, identifiers::ChainId, time::Duration}; use linera_client::storage::{StorageConfig, StorageConfigNamespace}; use linera_execution::ResourceControlPolicy; use linera_service::{ @@ -320,16 +320,10 @@ async fn create_wallets_and_faucets( // Run the faucet, let faucet_service = if let Some(faucet_chain) = with_faucet_chain { - let chain_ids = client - .find_owned_chains() - .expect("Cannot retrieve chains from linera wallet"); - let chain_id = chain_ids.get(faucet_chain as usize).expect( - "Was expecting a number `faucet_chain` in the range `0..=num_other_initial_chains`", - ); let service = client .run_faucet( Some(faucet_port.into()), - str::parse(chain_id)?, + ChainId::root(faucet_chain), faucet_amount, ) .await?;