From dafbd733f07144762747d9a97e91d7fb3f24038f Mon Sep 17 00:00:00 2001 From: Chad Ostrowski <221614+chadoh@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:44:18 -0400 Subject: [PATCH] feat: add (local|testnet|mainnet) default networks (#1408) Similar to how we support `--network futurenet` before you even set it. No need to make people go look it up on https://developers.stellar.org/docs/reference/networks This adds a dependency on [phf](https://crates.io/crates/phf) to construct a compile-time Map of named networks to named network settings. It also lists default networks with the `network ls [-l]` commands, and slightly updates (fixes, really) the behavior of `network ls`. Previously, if you had a local and global network named the same thing, `network ls` would show that name twice. Now it will only show them once. When you `network ls -l`, it will show all name collisions. $ cargo run -q -- network ls future futurenet local testnet mainnet $ cargo run -q -- network ls -l Local "/Users/chadoh/code/s/cli/.soroban/network/future.toml" Name: future Network { rpc_url: "https://rpc-futurenet.stellar.org:443", network_passphrase: "Test SDF Future Network ; October 2022", } Local "/Users/chadoh/code/s/cli/.soroban/network/futurenet.toml" Name: futurenet Network { rpc_url: "https://rpc-futurenet.stellar.org:443", network_passphrase: "Test SDF Future Network ; October 2022", } Global "/Users/chadoh/.config/soroban/network/future.toml" Name: future Network { rpc_url: "https://rpc-futurenet.stellar.org:443", network_passphrase: "Test SDF Future Network ; October 2022", } Global "/Users/chadoh/.config/soroban/network/local.toml" Name: local Network { rpc_url: "http://localhost:8000/rpc", network_passphrase: "Standalone Network ; February 2017", } Global "/Users/chadoh/.config/soroban/network/testnet.toml" Name: testnet Network { rpc_url: "https://soroban-testnet.stellar.org", network_passphrase: "Test SDF Network ; September 2015", } Default Name: local Network { rpc_url: "http://localhost:8000/rpc", network_passphrase: "Standalone Network ; February 2017", } Default Name: futurenet Network { rpc_url: "https://soroban-testnet.stellar.org", network_passphrase: "Test SDF Network ; September 2015", } Default Name: mainnet Network { rpc_url: "https://example.com/bring-your-own", network_passphrase: "Public Global Stellar Network ; September 2015", } Default Name: testnet Network { rpc_url: "https://rpc-futurenet.stellar.org:443", network_passphrase: "Test SDF Future Network ; October 2022", } --- Cargo.lock | 5 ++- cmd/crates/soroban-test/tests/it/config.rs | 30 ++++++++++++++ cmd/soroban-cli/Cargo.toml | 1 + .../src/commands/config/locator.rs | 39 ++++++++++++------- .../commands/contract/bindings/typescript.rs | 11 +++--- cmd/soroban-cli/src/commands/network/ls.rs | 3 +- cmd/soroban-cli/src/commands/network/mod.rs | 31 ++++++++++++--- 7 files changed, 91 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 546f478fcc..2c7da0e91a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3123,9 +3123,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ledger-apdu" @@ -4693,6 +4693,7 @@ dependencies = [ "num-bigint", "openssl", "pathdiff", + "phf", "predicates 2.1.5", "rand", "regex", diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index dd33713aa1..5704237ed4 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -42,6 +42,36 @@ fn set_and_remove_network() { }); } +#[test] +fn use_default_futurenet() { + TestEnv::with_default(|sandbox| { + sandbox + .new_assert_cmd("keys") + .args(["generate", "alice", "--network", "futurenet"]) + .assert() + .success(); + let dir = sandbox.dir().join(".soroban").join("network"); + let mut read_dir = std::fs::read_dir(dir).unwrap(); + let file = read_dir.next().unwrap().unwrap(); + assert_eq!(file.file_name().to_str().unwrap(), "futurenet.toml"); + }); +} + +#[test] +fn use_default_testnet() { + TestEnv::with_default(|sandbox| { + sandbox + .new_assert_cmd("keys") + .args(["generate", "alice", "--network", "testnet"]) + .assert() + .success(); + let dir = sandbox.dir().join(".soroban").join("network"); + let mut read_dir = std::fs::read_dir(dir).unwrap(); + let file = read_dir.next().unwrap().unwrap(); + assert_eq!(file.file_name().to_str().unwrap(), "testnet.toml"); + }); +} + fn add_network(sandbox: &TestEnv, name: &str) { sandbox .new_assert_cmd("network") diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index a48ad29943..a650402e42 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -114,6 +114,7 @@ rust-embed = { version = "8.2.0", features = ["debug-embed"] } bollard = { workspace=true } futures-util = "0.3.30" home = "0.5.9" +phf = { version = "0.11.2", features = ["macros"] } # For hyper-tls [target.'cfg(unix)'.dependencies] openssl = { version = "=0.10.55", features = ["vendored"] } diff --git a/cmd/soroban-cli/src/commands/config/locator.rs b/cmd/soroban-cli/src/commands/config/locator.rs index a5f6b02f17..5dc4dae852 100644 --- a/cmd/soroban-cli/src/commands/config/locator.rs +++ b/cmd/soroban-cli/src/commands/config/locator.rs @@ -1,4 +1,5 @@ use clap::arg; +use itertools::Itertools; use serde::de::DeserializeOwned; use std::{ ffi::OsStr, @@ -13,7 +14,11 @@ use stellar_strkey::{Contract, DecodeError}; use crate::{utils::find_config_dir, Pwd}; -use super::{alias, network::Network, secret::Secret}; +use super::{ + alias, + network::{self, Network}, + secret::Secret, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -93,7 +98,7 @@ impl Display for Location { Location::Local(_) => "Local", Location::Global(_) => "Global", }, - self.as_ref().parent().unwrap().parent().unwrap() + self.as_ref() ) } } @@ -175,16 +180,17 @@ impl Args { } pub fn list_networks(&self) -> Result, Error> { - Ok(KeyType::Network + let saved_networks = KeyType::Network .list_paths(&self.local_and_global()?) .into_iter() .flatten() - .map(|x| x.0) - .collect()) + .map(|x| x.0); + let default_networks = network::DEFAULTS.keys().map(ToString::to_string); + Ok(saved_networks.chain(default_networks).unique().collect()) } - pub fn list_networks_long(&self) -> Result, Error> { - Ok(KeyType::Network + pub fn list_networks_long(&self) -> Result, Error> { + let saved_networks = KeyType::Network .list_paths(&self.local_and_global()?) .into_iter() .flatten() @@ -192,11 +198,15 @@ impl Args { Some(( name, KeyType::read_from_path::(location.as_ref()).ok()?, - location, + location.to_string(), )) - }) - .collect::>()) + }); + let default_networks = network::DEFAULTS + .into_iter() + .map(|(name, network)| ((*name).to_string(), network.into(), "Default".to_owned())); + Ok(saved_networks.chain(default_networks).collect()) } + pub fn read_identity(&self, name: &str) -> Result { KeyType::Identity.read_with_global(name, &self.local_config()?) } @@ -204,11 +214,10 @@ impl Args { pub fn read_network(&self, name: &str) -> Result { let res = KeyType::Network.read_with_global(name, &self.local_config()?); if let Err(Error::ConfigMissing(_, _)) = &res { - if name == "futurenet" { - let network = Network::futurenet(); - self.write_network(name, &network)?; - return Ok(network); - } + let Some(network) = network::DEFAULTS.get(name) else { + return res; + }; + return Ok(network.into()); } res } diff --git a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs index f761fff85f..b2b6c3b1cd 100644 --- a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs +++ b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs @@ -125,11 +125,12 @@ impl NetworkRunnable for Cmd { rpc_url, network_passphrase, .. - } = self - .network - .get(&self.locator) - .ok() - .unwrap_or_else(Network::futurenet); + } = self.network.get(&self.locator).ok().unwrap_or_else(|| { + network::DEFAULTS + .get("futurenet") + .expect("why did we remove the default futurenet network?") + .into() + }); let absolute_path = self.output_dir.canonicalize()?; let file_name = absolute_path .file_name() diff --git a/cmd/soroban-cli/src/commands/network/ls.rs b/cmd/soroban-cli/src/commands/network/ls.rs index cc542b3e9f..346210e25a 100644 --- a/cmd/soroban-cli/src/commands/network/ls.rs +++ b/cmd/soroban-cli/src/commands/network/ls.rs @@ -1,7 +1,6 @@ use clap::command; use super::locator; -use crate::commands::config::locator::Location; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -36,7 +35,7 @@ impl Cmd { .list_networks_long()? .iter() .filter_map(|(name, network, location)| { - (!self.config_locator.global || matches!(location, Location::Global(_))) + (!self.config_locator.global || location == "Global") .then(|| Some(format!("{location}\nName: {name}\n{network:#?}\n")))? }) .collect()) diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 1184b5b95e..75af88970d 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use clap::{arg, Parser}; +use phf::phf_map; use serde::{Deserialize, Serialize}; use serde_json::Value; use stellar_strkey::ed25519::PublicKey; @@ -247,11 +248,31 @@ impl Network { } } -impl Network { - pub fn futurenet() -> Self { - Network { - rpc_url: "https://rpc-futurenet.stellar.org:443".to_owned(), - network_passphrase: "Test SDF Future Network ; October 2022".to_owned(), +pub static DEFAULTS: phf::Map<&'static str, (&'static str, &'static str)> = phf_map! { + "local" => ( + "http://localhost:8000/rpc", + "Standalone Network ; February 2017", + ), + "futurenet" => ( + "https://soroban-testnet.stellar.org", + "Test SDF Network ; September 2015", + ), + "testnet" => ( + "https://rpc-futurenet.stellar.org:443", + "Test SDF Future Network ; October 2022", + ), + "mainnet" => ( + "Bring Your Own: https://developers.stellar.org/docs/data/rpc/rpc-providers", + "Public Global Stellar Network ; September 2015", + ), +}; + +impl From<&(&str, &str)> for Network { + /// Convert the return value of `DEFAULTS.get()` into a Network + fn from(n: &(&str, &str)) -> Self { + Self { + rpc_url: n.0.to_string(), + network_passphrase: n.1.to_string(), } } }