From e4319e03923e45279fac09cde88ca9d02888c235 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 7 Mar 2024 15:01:54 -0500 Subject: [PATCH 01/28] feat: use data directory and store simulations and transaction responses --- Cargo.lock | 53 ++++++- Cargo.toml | 2 + cmd/soroban-cli/Cargo.toml | 5 + cmd/soroban-cli/src/commands/config/data.rs | 138 ++++++++++++++++++ cmd/soroban-cli/src/commands/config/mod.rs | 1 + .../src/commands/contract/invoke.rs | 16 +- cmd/soroban-cli/src/commands/data/ls.rs | 60 ++++++++ cmd/soroban-cli/src/commands/data/mod.rs | 1 + cmd/soroban-cli/src/commands/mod.rs | 1 + cmd/soroban-cli/src/commands/network/mod.rs | 7 + cmd/soroban-cli/src/lib.rs | 1 + 11 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/config/data.rs create mode 100644 cmd/soroban-cli/src/commands/data/ls.rs create mode 100644 cmd/soroban-cli/src/commands/data/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c245e03fc..d6ef884b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -818,13 +818,22 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] @@ -838,6 +847,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -2717,6 +2738,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -3637,6 +3664,7 @@ dependencies = [ "clap_complete", "crate-git-revision 0.0.4", "csv", + "directories", "dirs", "dotenvy", "ed25519-dalek 2.0.0", @@ -3690,6 +3718,7 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", + "ulid", "ureq", "wasm-opt", "wasmparser 0.90.0", @@ -4569,6 +4598,18 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ulid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" +dependencies = [ + "getrandom", + "rand", + "serde", + "web-time", +] + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -4864,6 +4905,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" diff --git a/Cargo.toml b/Cargo.toml index b2ea0177b..0f91b7153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,8 @@ tracing-subscriber = "0.3.16" tracing-appender = "0.2.2" which = "4.4.0" wasmparser = "0.90.0" +directories = "5.0.1" +ulid = { version = "1.1" } termcolor = "1.1.3" termcolor_output = "1.0.1" ed25519-dalek = "2.0.0" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index b20d22301..df2570d07 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -95,6 +95,11 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } cargo_metadata = "0.15.4" pathdiff = "0.2.1" dotenvy = "0.15.7" +directories = { workspace = true } +# For unique identifiers +ulid.workspace = true +# For unique identifiers +ulid.features = ["serde"] strum = "0.17.1" strum_macros = "0.17.1" gix = { version = "0.58.0", default-features = false, features = [ diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs new file mode 100644 index 000000000..121b9fd38 --- /dev/null +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -0,0 +1,138 @@ +use crate::rpc::{GetTransactionResponse, GetTransactionResponseRaw, SimulateTransactionResponse}; +use directories::ProjectDirs; +use http::Uri; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +use crate::xdr::{self, WriteXdr}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to find project directories")] + FiledToFindProjectDirs, + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), + #[error(transparent)] + Http(#[from] http::uri::InvalidUri), + #[error(transparent)] + Ulid(#[from] ulid::DecodeError), +} + +pub const XDG_DATA_HOME: &str = "XDG_DATA_HOME"; + +pub fn project_dir() -> Result { + std::env::var(XDG_DATA_HOME) + .map_or_else( + |_| ProjectDirs::from("com", "stellar", "soroban-cli"), + |data_home| ProjectDirs::from_path(std::path::PathBuf::from(data_home)), + ) + .ok_or(Error::FiledToFindProjectDirs) +} + +pub fn actions_dir() -> Result { + let dir = project_dir()?.data_local_dir().join("actions"); + std::fs::create_dir_all(&dir)?; + Ok(dir) +} + +pub fn write(action: Action, rpc_url: Uri) -> Result { + let data = Data { + action, + rpc_url: rpc_url.to_string(), + }; + let id = ulid::Ulid::new(); + let file = actions_dir()?.join(id.to_string()).with_extension("json"); + std::fs::write(file, serde_json::to_string(&data)?)?; + Ok(id) +} + +pub fn read(id: &ulid::Ulid) -> Result<(Action, Uri), Error> { + let file = actions_dir()?.join(id.to_string()).with_extension("json"); + let data: Data = serde_json::from_str(&std::fs::read_to_string(file)?)?; + Ok((data.action, http::Uri::from_str(&data.rpc_url)?)) +} + +pub fn list_ulids() -> Result, Error> { + let dir = actions_dir()?; + let mut list = std::fs::read_dir(dir)? + .map(|entry| { + entry + .map(|e| e.file_name().into_string().unwrap()) + .map_err(Error::from) + }) + .collect::, Error>>()?; + list.sort(); + Ok(list + .iter() + .map(|s|ulid::Ulid::from_str(s)) + .collect::, _>>()?) +} + +pub fn list_actions() -> Result, Error> { + list_ulids()?.into_iter() + .map(|id| { + let (action, uri) = read(&id)?; + Ok((id, action, uri)) + }) + .collect::,Error>>() +} + +#[derive(Serialize, Deserialize)] +struct Data { + action: Action, + rpc_url: String, +} + +#[derive(Serialize, Deserialize, Clone)] +pub enum Action { + Simulation(SimulateTransactionResponse), + Transaction(GetTransactionResponseRaw), +} + +impl From for Action { + fn from(res: SimulateTransactionResponse) -> Self { + Self::Simulation(res) + } +} + +impl TryFrom for Action { + type Error = xdr::Error; + fn try_from(res: GetTransactionResponse) -> Result { + Ok(Self::Transaction(GetTransactionResponseRaw { + status: res.status, + envelope_xdr: res.envelope.map(to_xdr).transpose()?, + result_xdr: res.result.map(to_xdr).transpose()?, + result_meta_xdr: res.result_meta.map(to_xdr).transpose()?, + })) + } +} + +fn to_xdr(data: impl WriteXdr) -> Result { + data.to_xdr_base64(xdr::Limits::none()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_write_read() { + let t = assert_fs::TempDir::new().unwrap(); + std::env::set_var(XDG_DATA_HOME, t.path().to_str().unwrap()); + let rpc_uri = http::uri::Uri::from_str("http://localhost:8000").unwrap(); + let sim = SimulateTransactionResponse::default(); + let original_action: Action = sim.into(); + + let id = write(original_action.clone(), rpc_uri.clone()).unwrap(); + let (action, new_rpc_uri) = read(&id).unwrap(); + assert_eq!(rpc_uri, new_rpc_uri); + match (action, original_action) { + (Action::Simulation(a), Action::Simulation(b)) => { + assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); + } + _ => panic!("Action mismatch"), + } + } +} diff --git a/cmd/soroban-cli/src/commands/config/mod.rs b/cmd/soroban-cli/src/commands/config/mod.rs index 3c5f4cca4..0125ff115 100644 --- a/cmd/soroban-cli/src/commands/config/mod.rs +++ b/cmd/soroban-cli/src/commands/config/mod.rs @@ -9,6 +9,7 @@ use self::{network::Network, secret::Secret}; use super::{keys, network}; +pub mod data; pub mod locator; pub mod secret; diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index cd42d818f..c68a4f9c1 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -29,7 +29,7 @@ use super::super::{ events, }; use crate::commands::NetworkRunnable; -use crate::{commands::global, rpc, Pwd}; +use crate::{commands::{global, config::data, network}, rpc, Pwd}; use soroban_spec_tools::{contract, Spec}; #[derive(Parser, Debug, Default, Clone)] @@ -139,6 +139,12 @@ pub enum Error { ContractSpec(#[from] contract::Error), #[error("")] MissingFileArg(PathBuf), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Network(#[from] network::Error), } impl From for Error { @@ -336,10 +342,13 @@ impl NetworkRunnable for Cmd { )?; let txn = client.create_assembled_transaction(&tx).await?; let txn = self.fee.apply_to_assembled_txn(txn); + let sim_res = txn.sim_response(); + data::write(sim_res.clone().into(), network.rpc_uri()?)?; let (return_value, events) = if self.is_view() { ( - txn.sim_response().results()?[0].xdr.clone(), - txn.sim_response().events()?, + sim_res.results()?[0].xdr.clone(), + sim_res.events()?, + ) } else { let global::Args { @@ -357,6 +366,7 @@ impl NetworkRunnable for Cmd { (verbose || very_verbose || self.fee.cost).then_some(log_resources), ) .await?; + data::write(res.clone().try_into()?, network.rpc_uri()?)?; (res.return_value()?, res.contract_events()?) }; diff --git a/cmd/soroban-cli/src/commands/data/ls.rs b/cmd/soroban-cli/src/commands/data/ls.rs new file mode 100644 index 000000000..da2a3960a --- /dev/null +++ b/cmd/soroban-cli/src/commands/data/ls.rs @@ -0,0 +1,60 @@ +use clap::command; + +use crate::commands::config::data::{self, Action}; + +use super::super::config::locator; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), + #[error(transparent)] + Data(#[from] data::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub config_locator: locator::Args, + + #[arg(long, short = 'l')] + pub long: bool, +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + let res = if self.long { self.ls_l() } else { self.ls() }?.join("\n"); + println!("{res}"); + Ok(()) + } + + pub fn ls(&self) -> Result, Error> { + data::list_actions()? + .iter() + .map(|(id, action, uri)| { + Ok(format!( + "{} {} {uri}\n", + to_datatime(id), + action_type(action) + )) + }) + .collect() + } + + pub fn ls_l(&self) -> Result, Error> { + todo!() + } +} + +fn to_datatime(id: &ulid::Ulid) -> chrono::DateTime { + chrono::DateTime::from_timestamp_millis(id.timestamp_ms().try_into().unwrap()).unwrap() +} + +fn action_type(a: &Action) -> String { + match a { + Action::Simulation(_) => "Simulation", + Action::Transaction(_) => "Transaction", + } + .to_string() +} diff --git a/cmd/soroban-cli/src/commands/data/mod.rs b/cmd/soroban-cli/src/commands/data/mod.rs new file mode 100644 index 000000000..923426f42 --- /dev/null +++ b/cmd/soroban-cli/src/commands/data/mod.rs @@ -0,0 +1 @@ +pub mod ls; \ No newline at end of file diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index fc052fee0..17d45d1af 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -6,6 +6,7 @@ use clap::{command, error::ErrorKind, CommandFactory, FromArgMatches, Parser}; pub mod completion; pub mod config; pub mod contract; +pub mod data; pub mod events; pub mod global; pub mod keys; diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 8e0059596..e56d99068 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -179,7 +179,10 @@ impl Network { .build()?) } else { let client = Client::new(&self.rpc_url)?; + let network = client.get_network().await?; + tracing::debug!("network {network:?}"); let uri = client.friendbot_url().await?; + tracing::debug!("URI {uri:?}"); Uri::from_str(&format!("{uri}?addr={addr}")).map_err(|e| { tracing::error!("{e}"); Error::InvalidUrl(uri.to_string()) @@ -217,6 +220,10 @@ impl Network { } Ok(()) } + + pub fn rpc_uri(&self) -> Result { + http::Uri::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) + } } impl Network { diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs index ef443853b..1a47e7967 100644 --- a/cmd/soroban-cli/src/lib.rs +++ b/cmd/soroban-cli/src/lib.rs @@ -4,6 +4,7 @@ clippy::missing_panics_doc )] pub(crate) use soroban_rpc as rpc; +pub(crate) use soroban_env_host::xdr; use std::path::Path; pub mod commands; From 2efce948eb7403bba56ed9e80e3c577bec3a1c77 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 7 Mar 2024 17:12:43 -0500 Subject: [PATCH 02/28] feat: add data logging to all commands that use a transaction --- cmd/soroban-cli/src/commands/config/data.rs | 7 ++++--- .../src/commands/contract/deploy/asset.rs | 19 +++++++++++++++---- .../src/commands/contract/deploy/wasm.rs | 18 ++++++++++++++---- .../src/commands/contract/extend.rs | 10 +++++++++- .../src/commands/contract/install.rs | 18 +++++++++++------- .../src/commands/contract/invoke.rs | 11 +++++------ .../src/commands/contract/restore.rs | 10 +++++++--- cmd/soroban-cli/src/commands/data/mod.rs | 2 +- cmd/soroban-cli/src/lib.rs | 2 +- 9 files changed, 67 insertions(+), 30 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 121b9fd38..7fd9efb09 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -66,17 +66,18 @@ pub fn list_ulids() -> Result, Error> { list.sort(); Ok(list .iter() - .map(|s|ulid::Ulid::from_str(s)) + .map(|s| ulid::Ulid::from_str(s)) .collect::, _>>()?) } pub fn list_actions() -> Result, Error> { - list_ulids()?.into_iter() + list_ulids()? + .into_iter() .map(|id| { let (action, uri) = read(&id)?; Ok((id, action, uri)) }) - .collect::,Error>>() + .collect::, Error>>() } #[derive(Serialize, Deserialize)] diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index dbca1940c..d0d518669 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -12,7 +12,10 @@ use std::convert::Infallible; use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError}; use crate::{ - commands::{config, global, NetworkRunnable}, + commands::{ + config::{self, data}, + global, network, NetworkRunnable, + }, rpc::{Client, Error as SorobanRpcError}, utils::{contract_id_hash_from_asset, parsing::parse_asset}, }; @@ -35,6 +38,10 @@ pub enum Error { Config(#[from] config::Error), #[error(transparent)] ParseAssetError(#[from] crate::utils::parsing::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Network(#[from] network::Error), } impl From for Error { @@ -103,9 +110,13 @@ impl NetworkRunnable for Cmd { )?; let txn = client.create_assembled_transaction(&tx).await?; let txn = self.fee.apply_to_assembled_txn(txn); - client - .send_assembled_transaction(txn, &key, &[], network_passphrase, None, None) - .await?; + data::write( + client + .send_assembled_transaction(txn, &key, &[], network_passphrase, None, None) + .await? + .try_into()?, + network.rpc_uri()?, + )?; Ok(stellar_strkey::Contract(contract_id.0).to_string()) } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index bac7fe2f0..fc03b9114 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -15,8 +15,9 @@ use soroban_env_host::{ }; use crate::commands::{ + config::data, contract::{self, id::wasm::get_contract_id}, - global, NetworkRunnable, + global, network, NetworkRunnable, }; use crate::{ commands::{config, contract::install, HEADING_RPC}, @@ -91,6 +92,10 @@ pub enum Error { Infallible(#[from] std::convert::Infallible), #[error(transparent)] WasmId(#[from] contract::id::wasm::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Network(#[from] network::Error), } impl Cmd { @@ -166,9 +171,14 @@ impl NetworkRunnable for Cmd { )?; let txn = client.create_assembled_transaction(&txn).await?; let txn = self.fee.apply_to_assembled_txn(txn); - client - .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) - .await?; + data::write( + client + .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) + .await? + .try_into() + .unwrap(), + network.rpc_uri()?, + )?; Ok(stellar_strkey::Contract(contract_id.0).to_string()) } } diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index bcf8a90d5..f390a8e2a 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -9,7 +9,10 @@ use soroban_env_host::xdr::{ }; use crate::{ - commands::{config, global, NetworkRunnable}, + commands::{ + config::{self, data}, + global, network, NetworkRunnable, + }, key, rpc::{self, Client}, wasm, Pwd, @@ -75,6 +78,10 @@ pub enum Error { Wasm(#[from] wasm::Error), #[error(transparent)] Key(#[from] key::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Network(#[from] network::Error), } impl Cmd { @@ -158,6 +165,7 @@ impl NetworkRunnable for Cmd { let res = client .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; + data::write(res.clone().try_into()?, network.rpc_uri()?)?; let events = res.events()?; if !events.is_empty() { diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 47a47e1d2..b4f7d39b2 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -10,7 +10,8 @@ use soroban_env_host::xdr::{ }; use super::restore; -use crate::commands::{global, NetworkRunnable}; +use crate::commands::network; +use crate::commands::{config::data, global, NetworkRunnable}; use crate::key; use crate::rpc::{self, Client}; use crate::{commands::config, utils, wasm}; @@ -62,6 +63,10 @@ pub enum Error { wasm: std::path::PathBuf, version: String, }, + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Data(#[from] data::Error), } impl Cmd { @@ -130,16 +135,15 @@ impl NetworkRunnable for Cmd { .create_assembled_transaction(&tx_without_preflight) .await?; let txn = self.fee.apply_to_assembled_txn(txn); - + let txn_resp = client + .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) + .await?; + data::write(txn_resp.clone().try_into().unwrap(), network.rpc_uri()?)?; // Currently internal errors are not returned if the contract code is expired if let Some(TransactionResult { result: TransactionResultResult::TxInternalError, .. - }) = client - .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) - .await? - .result - .as_ref() + }) = txn_resp.result.as_ref() { // Now just need to restore it and don't have to install again restore::Cmd { diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index c68a4f9c1..717e7e99c 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -29,7 +29,10 @@ use super::super::{ events, }; use crate::commands::NetworkRunnable; -use crate::{commands::{global, config::data, network}, rpc, Pwd}; +use crate::{ + commands::{config::data, global, network}, + rpc, Pwd, +}; use soroban_spec_tools::{contract, Spec}; #[derive(Parser, Debug, Default, Clone)] @@ -345,11 +348,7 @@ impl NetworkRunnable for Cmd { let sim_res = txn.sim_response(); data::write(sim_res.clone().into(), network.rpc_uri()?)?; let (return_value, events) = if self.is_view() { - ( - sim_res.results()?[0].xdr.clone(), - sim_res.events()?, - - ) + (sim_res.results()?[0].xdr.clone(), sim_res.events()?) } else { let global::Args { verbose, diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 869d26015..769991797 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -11,9 +11,9 @@ use stellar_strkey::DecodeError; use crate::{ commands::{ - config::{self, locator}, + config::{self, data, locator}, contract::extend, - global, NetworkRunnable, + global, network, NetworkRunnable, }, key, rpc::{self, Client}, @@ -83,6 +83,10 @@ pub enum Error { Key(#[from] key::Error), #[error(transparent)] Extend(#[from] extend::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Network(#[from] network::Error), } impl Cmd { @@ -162,7 +166,7 @@ impl NetworkRunnable for Cmd { let res = client .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; - + data::write(res.clone().try_into()?, network.rpc_uri()?)?; let meta = res .result_meta .as_ref() diff --git a/cmd/soroban-cli/src/commands/data/mod.rs b/cmd/soroban-cli/src/commands/data/mod.rs index 923426f42..1ea63c501 100644 --- a/cmd/soroban-cli/src/commands/data/mod.rs +++ b/cmd/soroban-cli/src/commands/data/mod.rs @@ -1 +1 @@ -pub mod ls; \ No newline at end of file +pub mod ls; diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs index 1a47e7967..d4118a6be 100644 --- a/cmd/soroban-cli/src/lib.rs +++ b/cmd/soroban-cli/src/lib.rs @@ -3,8 +3,8 @@ clippy::must_use_candidate, clippy::missing_panics_doc )] -pub(crate) use soroban_rpc as rpc; pub(crate) use soroban_env_host::xdr; +pub(crate) use soroban_rpc as rpc; use std::path::Path; pub mod commands; From e93a3a22c9dc94b082a38982eb619c5becc0c970 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Sat, 9 Mar 2024 14:47:38 -0500 Subject: [PATCH 03/28] feat: add "--no-cache" global arg --- cmd/crates/soroban-test/src/lib.rs | 1 + .../src/commands/contract/deploy/asset.rs | 16 ++++++++-------- .../src/commands/contract/deploy/wasm.rs | 15 +++++++-------- cmd/soroban-cli/src/commands/contract/extend.rs | 6 ++++-- cmd/soroban-cli/src/commands/contract/install.rs | 4 +++- cmd/soroban-cli/src/commands/contract/invoke.rs | 9 +++++++-- cmd/soroban-cli/src/commands/contract/restore.rs | 6 ++++-- cmd/soroban-cli/src/commands/global.rs | 4 ++++ 8 files changed, 38 insertions(+), 23 deletions(-) diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 9ac884193..e4e24f568 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -246,6 +246,7 @@ impl TestEnv { verbose: false, very_verbose: false, list: false, + no_cache: false, }), Some(&config), ) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index d0d518669..75f655940 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -78,7 +78,7 @@ impl NetworkRunnable for Cmd { async fn run_against_rpc_server( &self, - _: Option<&global::Args>, + args: Option<&global::Args>, config: Option<&config::Args>, ) -> Result { let config = config.unwrap_or(&self.config); @@ -110,13 +110,13 @@ impl NetworkRunnable for Cmd { )?; let txn = client.create_assembled_transaction(&tx).await?; let txn = self.fee.apply_to_assembled_txn(txn); - data::write( - client - .send_assembled_transaction(txn, &key, &[], network_passphrase, None, None) - .await? - .try_into()?, - network.rpc_uri()?, - )?; + let get_txn_resp = client + .send_assembled_transaction(txn, &key, &[], network_passphrase, None, None) + .await? + .try_into()?; + if args.map_or(true, |a| !a.no_cache) { + data::write(get_txn_resp, network.rpc_uri()?)?; + } Ok(stellar_strkey::Contract(contract_id.0).to_string()) } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index fc03b9114..1bedfc730 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -171,14 +171,13 @@ impl NetworkRunnable for Cmd { )?; let txn = client.create_assembled_transaction(&txn).await?; let txn = self.fee.apply_to_assembled_txn(txn); - data::write( - client - .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) - .await? - .try_into() - .unwrap(), - network.rpc_uri()?, - )?; + let get_txn_resp = client + .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) + .await? + .try_into()?; + if global_args.map_or(true, |a| !a.no_cache) { + data::write(get_txn_resp, network.rpc_uri()?)?; + } Ok(stellar_strkey::Contract(contract_id.0).to_string()) } } diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index f390a8e2a..0c51a88d0 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -115,7 +115,7 @@ impl NetworkRunnable for Cmd { async fn run_against_rpc_server( &self, - _args: Option<&global::Args>, + args: Option<&global::Args>, config: Option<&config::Args>, ) -> Result { let config = config.unwrap_or(&self.config); @@ -165,7 +165,9 @@ impl NetworkRunnable for Cmd { let res = client .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + if args.map_or(true, |a| !a.no_cache) { + data::write(res.clone().try_into()?, network.rpc_uri()?)?; + } let events = res.events()?; if !events.is_empty() { diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index b4f7d39b2..1b077bd3f 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -138,7 +138,9 @@ impl NetworkRunnable for Cmd { let txn_resp = client .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) .await?; - data::write(txn_resp.clone().try_into().unwrap(), network.rpc_uri()?)?; + if args.map_or(true, |a| !a.no_cache) { + data::write(txn_resp.clone().try_into().unwrap(), network.rpc_uri()?)?; + } // Currently internal errors are not returned if the contract code is expired if let Some(TransactionResult { result: TransactionResultResult::TxInternalError, diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 717e7e99c..68eadc922 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -346,13 +346,16 @@ impl NetworkRunnable for Cmd { let txn = client.create_assembled_transaction(&tx).await?; let txn = self.fee.apply_to_assembled_txn(txn); let sim_res = txn.sim_response(); - data::write(sim_res.clone().into(), network.rpc_uri()?)?; + if global_args.map_or(true, |a| !a.no_cache) { + data::write(sim_res.clone().into(), network.rpc_uri()?)?; + } let (return_value, events) = if self.is_view() { (sim_res.results()?[0].xdr.clone(), sim_res.events()?) } else { let global::Args { verbose, very_verbose, + no_cache, .. } = global_args.map(Clone::clone).unwrap_or_default(); let res = client @@ -365,7 +368,9 @@ impl NetworkRunnable for Cmd { (verbose || very_verbose || self.fee.cost).then_some(log_resources), ) .await?; - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + if !no_cache { + data::write(res.clone().try_into()?, network.rpc_uri()?)?; + } (res.return_value()?, res.contract_events()?) }; diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 769991797..a335d9860 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -119,7 +119,7 @@ impl NetworkRunnable for Cmd { async fn run_against_rpc_server( &self, - _: Option<&global::Args>, + args: Option<&global::Args>, config: Option<&config::Args>, ) -> Result { let config = config.unwrap_or(&self.config); @@ -166,7 +166,9 @@ impl NetworkRunnable for Cmd { let res = client .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + if args.map_or(true, |a| !a.no_cache) { + data::write(res.clone().try_into()?, network.rpc_uri()?)?; + } let meta = res .result_meta .as_ref() diff --git a/cmd/soroban-cli/src/commands/global.rs b/cmd/soroban-cli/src/commands/global.rs index c606bd1bd..bb530f043 100644 --- a/cmd/soroban-cli/src/commands/global.rs +++ b/cmd/soroban-cli/src/commands/global.rs @@ -29,6 +29,10 @@ pub struct Args { /// List installed plugins. E.g. `soroban-hello` #[arg(long)] pub list: bool, + + /// Do not cache your simulations and transactions + #[arg(long, env = "SOROBAN_NO_CACHE")] + pub no_cache: bool, } #[derive(thiserror::Error, Debug)] From cbf8d70899bdd5a8e6a3a19ca8cd6d1e96eb382b Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 11 Mar 2024 15:17:24 -0400 Subject: [PATCH 04/28] feat: add spec cache, speeding up `invoke` --- cmd/soroban-cli/src/commands/config/data.rs | 25 ++++++++++++++ .../src/commands/contract/install.rs | 4 ++- .../src/commands/contract/invoke.rs | 33 +++++++++++++++---- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 7fd9efb09..9c5b4ec1f 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -18,6 +18,8 @@ pub enum Error { Http(#[from] http::uri::InvalidUri), #[error(transparent)] Ulid(#[from] ulid::DecodeError), + #[error(transparent)] + Xdr(#[from] xdr::Error), } pub const XDG_DATA_HOME: &str = "XDG_DATA_HOME"; @@ -37,6 +39,12 @@ pub fn actions_dir() -> Result { Ok(dir) } +pub fn spec_dir() -> Result { + let dir = project_dir()?.data_local_dir().join("spec"); + std::fs::create_dir_all(&dir)?; + Ok(dir) +} + pub fn write(action: Action, rpc_url: Uri) -> Result { let data = Data { action, @@ -54,6 +62,23 @@ pub fn read(id: &ulid::Ulid) -> Result<(Action, Uri), Error> { Ok((data.action, http::Uri::from_str(&data.rpc_url)?)) } +pub fn write_spec(hash: &str, spec_entries: &[xdr::ScSpecEntry]) -> Result<(), Error> { + let file = spec_dir()?.join(hash); + tracing::trace!("writing spec to {:?}", file); + let mut contents: Vec = Vec::new(); + for entry in spec_entries { + contents.extend(entry.to_xdr(xdr::Limits::none())?); + } + std::fs::write(file, contents)?; + Ok(()) +} + +pub fn read_spec(hash: &str) -> Result, Error> { + let file = spec_dir()?.join(hash); + tracing::trace!("reading spec from {:?}", file); + Ok(soroban_spec::read::parse_raw(&std::fs::read(file)?)?) +} + pub fn list_ulids() -> Result, Error> { let dir = actions_dir()?; let mut list = std::fs::read_dir(dir)? diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 1b077bd3f..ec9de8805 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -165,7 +165,9 @@ impl NetworkRunnable for Cmd { .run_against_rpc_server(args, None) .await?; } - + if args.map_or(true, |a| !a.no_cache) { + data::write_spec(&hash.to_string(), &wasm_spec.spec)?; + } Ok(hash) } } diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 68eadc922..d7a890334 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -12,11 +12,11 @@ use heck::ToKebabCase; use soroban_env_host::{ xdr::{ - self, Error as XdrError, Hash, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, - LedgerEntryData, LedgerFootprint, Memo, MuxedAccount, Operation, OperationBody, - Preconditions, ScAddress, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, - SequenceNumber, SorobanAuthorizationEntry, SorobanResources, Transaction, TransactionExt, - Uint256, VecM, + self, ContractDataEntry, Error as XdrError, Hash, HostFunction, InvokeContractArgs, + InvokeHostFunctionOp, LedgerEntryData, LedgerFootprint, Memo, MuxedAccount, Operation, + OperationBody, Preconditions, ScAddress, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, + ScVal, ScVec, SequenceNumber, SorobanAuthorizationEntry, SorobanResources, Transaction, + TransactionExt, Uint256, VecM, }, HostError, }; @@ -331,8 +331,29 @@ impl NetworkRunnable for Cmd { let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); + let ContractDataEntry { + val: + xdr::ScVal::ContractInstance(xdr::ScContractInstance { + executable: xdr::ContractExecutable::Wasm(hash), + .. + }), + .. + } = client.get_contract_data(&contract_id).await? + else { + return Err(Error::MissingResult); + }; + let hash = hash.to_string(); + // Get the contract - let spec_entries = client.get_remote_contract_spec(&contract_id).await?; + let spec_entries = if let Ok(entries) = data::read_spec(&hash) { + entries + } else { + let res = client.get_remote_contract_spec(&contract_id).await?; + if global_args.map_or(true, |a| !a.no_cache) { + data::write_spec(&hash, &res)?; + } + res + }; // Get the ledger footprint let (function, spec, host_function_params, signers) = From 6a8891c6b0f7074e1b32fb23c2c7c70c992e1873 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 11 Mar 2024 15:57:02 -0400 Subject: [PATCH 05/28] fix: clippy and fmt --- cmd/soroban-cli/src/commands/config/data.rs | 12 ++++++------ .../src/commands/contract/deploy/asset.rs | 2 +- cmd/soroban-cli/src/commands/contract/deploy/wasm.rs | 2 +- cmd/soroban-cli/src/commands/contract/extend.rs | 2 +- cmd/soroban-cli/src/commands/contract/install.rs | 2 +- cmd/soroban-cli/src/commands/contract/invoke.rs | 4 ++-- cmd/soroban-cli/src/commands/contract/restore.rs | 2 +- docs/soroban-cli-full-docs.md | 4 ++++ 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 9c5b4ec1f..b8d86eee2 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -45,7 +45,7 @@ pub fn spec_dir() -> Result { Ok(dir) } -pub fn write(action: Action, rpc_url: Uri) -> Result { +pub fn write(action: Action, rpc_url: &Uri) -> Result { let data = Data { action, rpc_url: rpc_url.to_string(), @@ -128,14 +128,14 @@ impl TryFrom for Action { fn try_from(res: GetTransactionResponse) -> Result { Ok(Self::Transaction(GetTransactionResponseRaw { status: res.status, - envelope_xdr: res.envelope.map(to_xdr).transpose()?, - result_xdr: res.result.map(to_xdr).transpose()?, - result_meta_xdr: res.result_meta.map(to_xdr).transpose()?, + envelope_xdr: res.envelope.as_ref().map(to_xdr).transpose()?, + result_xdr: res.result.as_ref().map(to_xdr).transpose()?, + result_meta_xdr: res.result_meta.as_ref().map(to_xdr).transpose()?, })) } } -fn to_xdr(data: impl WriteXdr) -> Result { +fn to_xdr(data: &impl WriteXdr) -> Result { data.to_xdr_base64(xdr::Limits::none()) } @@ -151,7 +151,7 @@ mod test { let sim = SimulateTransactionResponse::default(); let original_action: Action = sim.into(); - let id = write(original_action.clone(), rpc_uri.clone()).unwrap(); + let id = write(original_action.clone(), &rpc_uri.clone()).unwrap(); let (action, new_rpc_uri) = read(&id).unwrap(); assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 75f655940..41e7367dc 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -115,7 +115,7 @@ impl NetworkRunnable for Cmd { .await? .try_into()?; if args.map_or(true, |a| !a.no_cache) { - data::write(get_txn_resp, network.rpc_uri()?)?; + data::write(get_txn_resp, &network.rpc_uri()?)?; } Ok(stellar_strkey::Contract(contract_id.0).to_string()) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 1bedfc730..40369b230 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -176,7 +176,7 @@ impl NetworkRunnable for Cmd { .await? .try_into()?; if global_args.map_or(true, |a| !a.no_cache) { - data::write(get_txn_resp, network.rpc_uri()?)?; + data::write(get_txn_resp, &network.rpc_uri()?)?; } Ok(stellar_strkey::Contract(contract_id.0).to_string()) } diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 0c51a88d0..ab7834959 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -166,7 +166,7 @@ impl NetworkRunnable for Cmd { .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; if args.map_or(true, |a| !a.no_cache) { - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + data::write(res.clone().try_into()?, &network.rpc_uri()?)?; } let events = res.events()?; diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index ec9de8805..d4a0d541f 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -139,7 +139,7 @@ impl NetworkRunnable for Cmd { .send_assembled_transaction(txn, &key, &[], &network.network_passphrase, None, None) .await?; if args.map_or(true, |a| !a.no_cache) { - data::write(txn_resp.clone().try_into().unwrap(), network.rpc_uri()?)?; + data::write(txn_resp.clone().try_into().unwrap(), &network.rpc_uri()?)?; } // Currently internal errors are not returned if the contract code is expired if let Some(TransactionResult { diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index d7a890334..2e8414285 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -368,7 +368,7 @@ impl NetworkRunnable for Cmd { let txn = self.fee.apply_to_assembled_txn(txn); let sim_res = txn.sim_response(); if global_args.map_or(true, |a| !a.no_cache) { - data::write(sim_res.clone().into(), network.rpc_uri()?)?; + data::write(sim_res.clone().into(), &network.rpc_uri()?)?; } let (return_value, events) = if self.is_view() { (sim_res.results()?[0].xdr.clone(), sim_res.events()?) @@ -390,7 +390,7 @@ impl NetworkRunnable for Cmd { ) .await?; if !no_cache { - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + data::write(res.clone().try_into()?, &network.rpc_uri()?)?; } (res.return_value()?, res.contract_events()?) }; diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index a335d9860..8b5921a1a 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -167,7 +167,7 @@ impl NetworkRunnable for Cmd { .prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None) .await?; if args.map_or(true, |a| !a.no_cache) { - data::write(res.clone().try_into()?, network.rpc_uri()?)?; + data::write(res.clone().try_into()?, &network.rpc_uri()?)?; } let meta = res .result_meta diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 68b9adc98..72abb00af 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -135,6 +135,10 @@ Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soro Possible values: `true`, `false` +* `--no-cache` — Do not cache your simulations and transactions + + Possible values: `true`, `false` + From 9cf10f35c91f6b403db16bda3677eeaf0bc067e7 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 12 Mar 2024 15:54:02 -0400 Subject: [PATCH 06/28] feat: implement display for Dated Action to see formated list of actions --- cmd/soroban-cli/src/commands/config/data.rs | 33 +++++++++++++++++++-- cmd/soroban-cli/src/commands/data/ls.rs | 32 +++++--------------- cmd/soroban-cli/src/commands/data/mod.rs | 22 ++++++++++++++ cmd/soroban-cli/src/commands/mod.rs | 6 ++++ 4 files changed, 66 insertions(+), 27 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index b8d86eee2..2ca1c49d0 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -91,20 +91,37 @@ pub fn list_ulids() -> Result, Error> { list.sort(); Ok(list .iter() - .map(|s| ulid::Ulid::from_str(s)) + .map(|s| ulid::Ulid::from_str(s.trim_end_matches(".json"))) .collect::, _>>()?) } -pub fn list_actions() -> Result, Error> { +pub fn list_actions() -> Result, Error> { list_ulids()? .into_iter() + .rev() .map(|id| { let (action, uri) = read(&id)?; - Ok((id, action, uri)) + Ok(DatedAction(id, action, uri)) }) .collect::, Error>>() } +pub struct DatedAction(ulid::Ulid, Action, Uri); + +impl std::fmt::Display for DatedAction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (id, a, uri) = (&self.0, &self.1, &self.2); + let datetime = to_datatime(id).format("%b %d %H:%M"); + write!(f, "{datetime} {uri} {}", a.type_str(),) + } +} + +impl DatedAction {} + +fn to_datatime(id: &ulid::Ulid) -> chrono::DateTime { + chrono::DateTime::from_timestamp_millis(id.timestamp_ms().try_into().unwrap()).unwrap() +} + #[derive(Serialize, Deserialize)] struct Data { action: Action, @@ -117,6 +134,16 @@ pub enum Action { Transaction(GetTransactionResponseRaw), } +impl Action { + pub fn type_str(&self) -> String { + match self { + Action::Simulation(_) => "Sim", + Action::Transaction(_) => "Txn", + } + .to_string() + } +} + impl From for Action { fn from(res: SimulateTransactionResponse) -> Self { Self::Simulation(res) diff --git a/cmd/soroban-cli/src/commands/data/ls.rs b/cmd/soroban-cli/src/commands/data/ls.rs index da2a3960a..c798c557b 100644 --- a/cmd/soroban-cli/src/commands/data/ls.rs +++ b/cmd/soroban-cli/src/commands/data/ls.rs @@ -1,7 +1,6 @@ use clap::command; -use crate::commands::config::data::{self, Action}; - +use crate::commands::config::data; use super::super::config::locator; #[derive(thiserror::Error, Debug)] @@ -30,31 +29,16 @@ impl Cmd { } pub fn ls(&self) -> Result, Error> { - data::list_actions()? + Ok(data::list_actions()? .iter() - .map(|(id, action, uri)| { - Ok(format!( - "{} {} {uri}\n", - to_datatime(id), - action_type(action) - )) - }) - .collect() + .map(ToString::to_string) + .collect()) } pub fn ls_l(&self) -> Result, Error> { - todo!() - } -} - -fn to_datatime(id: &ulid::Ulid) -> chrono::DateTime { - chrono::DateTime::from_timestamp_millis(id.timestamp_ms().try_into().unwrap()).unwrap() -} - -fn action_type(a: &Action) -> String { - match a { - Action::Simulation(_) => "Simulation", - Action::Transaction(_) => "Transaction", + Ok(data::list_actions()? + .iter() + .map(ToString::to_string) + .collect()) } - .to_string() } diff --git a/cmd/soroban-cli/src/commands/data/mod.rs b/cmd/soroban-cli/src/commands/data/mod.rs index 1ea63c501..f9d07d4c8 100644 --- a/cmd/soroban-cli/src/commands/data/mod.rs +++ b/cmd/soroban-cli/src/commands/data/mod.rs @@ -1 +1,23 @@ +use clap::Parser; pub mod ls; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// List identities + Ls(ls::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Ls(#[from] ls::Error), +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + match self { + Cmd::Ls(cmd) => cmd.run()?, + }; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 17d45d1af..1f5b002b2 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -101,6 +101,7 @@ impl Root { Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run().await?, Cmd::Config(c) => c.run().await?, + Cmd::Data(data) => data.run()?, }; Ok(()) } @@ -138,6 +139,9 @@ pub enum Cmd { Network(network::Cmd), /// Print version information Version(version::Cmd), + /// Access cached data + #[command(subcommand)] + Data(data::Cmd), } #[derive(thiserror::Error, Debug)] @@ -159,6 +163,8 @@ pub enum Error { Plugin(#[from] plugin::Error), #[error(transparent)] Network(#[from] network::Error), + #[error(transparent)] + Data(#[from] data::Error), } #[async_trait] From 976825e844a75724f344c2de06fe57e51982aa0c Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 28 Mar 2024 14:22:04 -0400 Subject: [PATCH 07/28] fix: docs --- cmd/soroban-cli/src/commands/data/ls.rs | 2 +- docs/soroban-cli-full-docs.md | 35 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/data/ls.rs b/cmd/soroban-cli/src/commands/data/ls.rs index c798c557b..a45311cb2 100644 --- a/cmd/soroban-cli/src/commands/data/ls.rs +++ b/cmd/soroban-cli/src/commands/data/ls.rs @@ -1,7 +1,7 @@ use clap::command; -use crate::commands::config::data; use super::super::config::locator; +use crate::commands::config::data; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 72abb00af..11b6d52b6 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -70,6 +70,8 @@ This document contains the help content for the `soroban` command-line program. * [`soroban network start`↴](#soroban-network-start) * [`soroban network stop`↴](#soroban-network-stop) * [`soroban version`↴](#soroban-version) +* [`soroban data`↴](#soroban-data) +* [`soroban data ls`↴](#soroban-data-ls) ## `soroban` @@ -110,6 +112,7 @@ Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soro * `lab` — Experiment with early features and expert tools * `network` — Start and configure networks * `version` — Print version information +* `data` — Access cached data ###### **Options:** @@ -1687,6 +1690,38 @@ Print version information +## `soroban data` + +Access cached data + +**Usage:** `soroban data ` + +###### **Subcommands:** + +* `ls` — List identities + + + +## `soroban data ls` + +List identities + +**Usage:** `soroban data ls [OPTIONS]` + +###### **Options:** + +* `--global` — Use global config + + Possible values: `true`, `false` + +* `--config-dir ` — Location of config directory, default is "." +* `-l`, `--long` + + Possible values: `true`, `false` + + + +
From f5ca6dd7c11a0535f82889a60dad2eb0b10f65ef Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 28 Mar 2024 15:19:19 -0400 Subject: [PATCH 08/28] feat: rename to cache command --- Cargo.lock | 1 + cmd/crates/soroban-test/Cargo.toml | 1 + cmd/crates/soroban-test/src/lib.rs | 3 +- .../tests/it/integration/hello_world.rs | 9 +++- cmd/soroban-cli/src/commands/cache/clean.rs | 34 +++++++++++++++ cmd/soroban-cli/src/commands/cache/info.rs | 23 ++++++++++ .../src/commands/{data => cache}/ls.rs | 2 +- cmd/soroban-cli/src/commands/cache/mod.rs | 42 +++++++++++++++++++ cmd/soroban-cli/src/commands/cache/read.rs | 31 ++++++++++++++ cmd/soroban-cli/src/commands/config/data.rs | 9 +++- cmd/soroban-cli/src/commands/data/mod.rs | 23 ---------- cmd/soroban-cli/src/commands/mod.rs | 10 ++--- 12 files changed, 156 insertions(+), 32 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/cache/clean.rs create mode 100644 cmd/soroban-cli/src/commands/cache/info.rs rename cmd/soroban-cli/src/commands/{data => cache}/ls.rs (96%) create mode 100644 cmd/soroban-cli/src/commands/cache/mod.rs create mode 100644 cmd/soroban-cli/src/commands/cache/read.rs delete mode 100644 cmd/soroban-cli/src/commands/data/mod.rs diff --git a/Cargo.lock b/Cargo.lock index d6ef884b5..592eb29b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3964,6 +3964,7 @@ dependencies = [ "thiserror", "tokio", "toml 0.8.10", + "ulid", "walkdir", "which", ] diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index cba4dae74..b6388b292 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -41,6 +41,7 @@ serde_json = "1.0.93" which = { workspace = true } tokio = "1.28.1" walkdir = "2.4.0" +ulid.workspace = true [features] it = [] diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index e4e24f568..e4d7410ce 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -126,7 +126,8 @@ impl TestEnv { .env("SOROBAN_ACCOUNT", TEST_ACCOUNT) .env("SOROBAN_RPC_URL", &self.rpc_url) .env("SOROBAN_NETWORK_PASSPHRASE", LOCAL_NETWORK_PASSPHRASE) - .env("XDG_CONFIG_HOME", self.temp_dir.as_os_str()) + .env("XDG_CONFIG_HOME", self.temp_dir.join("config").as_os_str()) + .env("XDG_DATA_HOME", self.temp_dir.join("data").as_os_str()) .current_dir(&self.temp_dir); cmd } diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 671d2edff..77fb9644c 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -10,9 +10,10 @@ use crate::integration::util::extend_contract; use super::util::{deploy_hello, extend, HELLO_WORLD}; +#[allow(clippy::too_many_lines)] #[tokio::test] async fn invoke() { - let sandbox = &TestEnv::new(); + let sandbox = &TestEnv::with_rpc_url("http://moss:8090/soroban/rpc"); let c = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); let GetLatestLedgerResponse { sequence, .. } = c.get_latest_ledger().await.unwrap(); sandbox @@ -63,6 +64,12 @@ async fn invoke() { }; let id = &deploy_hello(sandbox).await; extend_contract(sandbox, id).await; + let uid = sandbox + .new_assert_cmd("cache") + .arg("ls") + .assert() + .stdout_as_str(); + ulid::Ulid::from_string(&uid).expect("invalid ulid"); // Note that all functions tested here have no state invoke_hello_world(sandbox, id); diff --git a/cmd/soroban-cli/src/commands/cache/clean.rs b/cmd/soroban-cli/src/commands/cache/clean.rs new file mode 100644 index 000000000..ec908b151 --- /dev/null +++ b/cmd/soroban-cli/src/commands/cache/clean.rs @@ -0,0 +1,34 @@ +use std::fs; + +use super::super::config::locator; +use crate::commands::config::data; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Io(#[from] std::io::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// Actions only + #[arg(long, short = 'a')] + pub actions: bool, +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + let dir = if self.actions { + data::actions_dir()? + } else { + data::project_dir()?.data_dir().to_path_buf() + }; + fs::remove_dir_all(dir)?; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/cache/info.rs b/cmd/soroban-cli/src/commands/cache/info.rs new file mode 100644 index 000000000..723ae890e --- /dev/null +++ b/cmd/soroban-cli/src/commands/cache/info.rs @@ -0,0 +1,23 @@ +use super::super::config::locator; +use crate::commands::config::data; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), + #[error(transparent)] + Data(#[from] data::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd {} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + let binding = data::project_dir()?; + let dir = binding.data_dir(); + println!("{}", dir.to_string_lossy()); + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/data/ls.rs b/cmd/soroban-cli/src/commands/cache/ls.rs similarity index 96% rename from cmd/soroban-cli/src/commands/data/ls.rs rename to cmd/soroban-cli/src/commands/cache/ls.rs index a45311cb2..05d99bb7e 100644 --- a/cmd/soroban-cli/src/commands/data/ls.rs +++ b/cmd/soroban-cli/src/commands/cache/ls.rs @@ -29,7 +29,7 @@ impl Cmd { } pub fn ls(&self) -> Result, Error> { - Ok(data::list_actions()? + Ok(data::list_ulids()? .iter() .map(ToString::to_string) .collect()) diff --git a/cmd/soroban-cli/src/commands/cache/mod.rs b/cmd/soroban-cli/src/commands/cache/mod.rs new file mode 100644 index 000000000..db629db10 --- /dev/null +++ b/cmd/soroban-cli/src/commands/cache/mod.rs @@ -0,0 +1,42 @@ +use clap::Parser; + +pub mod clean; +pub mod info; +pub mod ls; +pub mod read; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// List cached actions (transactions, simulations) + Ls(ls::Cmd), + /// Show location of cache + Info(info::Cmd), + /// Delete all cached actions + Clean(clean::Cmd), + /// Read cached action + Read(read::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Info(#[from] info::Error), + #[error(transparent)] + Ls(#[from] ls::Error), + #[error(transparent)] + Clean(#[from] clean::Error), + #[error(transparent)] + Read(#[from] read::Error), +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + match self { + Cmd::Ls(cmd) => cmd.run()?, + Cmd::Info(cmd) => cmd.run()?, + Cmd::Clean(cmd) => cmd.run()?, + Cmd::Read(cmd) => cmd.run()?, + }; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/read.rs new file mode 100644 index 000000000..73b959a99 --- /dev/null +++ b/cmd/soroban-cli/src/commands/cache/read.rs @@ -0,0 +1,31 @@ +use std::fs; + +use super::super::config::locator; +use crate::commands::config::data; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error("failed to find cache entry {0}")] + NotFound(String), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// ULID of the cache entry + #[arg(long, visible_alias = "id")] + ulid: String, +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + let dir = data::actions_dir()?; + fs::read_to_string(dir.join(&self.ulid).with_extension(".json")) + .map_err(|_| Error::NotFound(self.ulid.clone()))?; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 2ca1c49d0..93bab46dd 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -112,7 +112,14 @@ impl std::fmt::Display for DatedAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (id, a, uri) = (&self.0, &self.1, &self.2); let datetime = to_datatime(id).format("%b %d %H:%M"); - write!(f, "{datetime} {uri} {}", a.type_str(),) + let status = match a { + Action::Simulation(sim) => sim + .error + .as_ref() + .map_or_else(|| "SUCCESS".to_string(), |_| "ERROR".to_string()), + Action::Transaction(txn) => txn.status.to_string(), + }; + write!(f, "{id} {} {status} {datetime} {uri} ", a.type_str(),) } } diff --git a/cmd/soroban-cli/src/commands/data/mod.rs b/cmd/soroban-cli/src/commands/data/mod.rs deleted file mode 100644 index f9d07d4c8..000000000 --- a/cmd/soroban-cli/src/commands/data/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -use clap::Parser; -pub mod ls; - -#[derive(Debug, Parser)] -pub enum Cmd { - /// List identities - Ls(ls::Cmd), -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Ls(#[from] ls::Error), -} - -impl Cmd { - pub fn run(&self) -> Result<(), Error> { - match self { - Cmd::Ls(cmd) => cmd.run()?, - }; - Ok(()) - } -} diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 1f5b002b2..3da8e3971 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -3,10 +3,10 @@ use std::str::FromStr; use async_trait::async_trait; use clap::{command, error::ErrorKind, CommandFactory, FromArgMatches, Parser}; +pub mod cache; pub mod completion; pub mod config; pub mod contract; -pub mod data; pub mod events; pub mod global; pub mod keys; @@ -101,7 +101,7 @@ impl Root { Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run().await?, Cmd::Config(c) => c.run().await?, - Cmd::Data(data) => data.run()?, + Cmd::Cache(data) => data.run()?, }; Ok(()) } @@ -139,9 +139,9 @@ pub enum Cmd { Network(network::Cmd), /// Print version information Version(version::Cmd), - /// Access cached data + /// Cache for tranasctions and contract specs #[command(subcommand)] - Data(data::Cmd), + Cache(cache::Cmd), } #[derive(thiserror::Error, Debug)] @@ -164,7 +164,7 @@ pub enum Error { #[error(transparent)] Network(#[from] network::Error), #[error(transparent)] - Data(#[from] data::Error), + Cache(#[from] cache::Error), } #[async_trait] From a1d8a507e92850fcab10d3437ec1f90ed4b9a1c5 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 8 Apr 2024 11:27:03 -0400 Subject: [PATCH 09/28] fix: revert change to invoke test --- cmd/crates/soroban-test/tests/it/integration/hello_world.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 77fb9644c..a6e8d863a 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -13,7 +13,7 @@ use super::util::{deploy_hello, extend, HELLO_WORLD}; #[allow(clippy::too_many_lines)] #[tokio::test] async fn invoke() { - let sandbox = &TestEnv::with_rpc_url("http://moss:8090/soroban/rpc"); + let sandbox = &TestEnv::new(); let c = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); let GetLatestLedgerResponse { sequence, .. } = c.get_latest_ledger().await.unwrap(); sandbox From 4ca157ca67b162f9bdc3b04ae7e76a63c9be2a16 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 8 Apr 2024 11:43:11 -0400 Subject: [PATCH 10/28] fix: md --- docs/soroban-cli-full-docs.md | 61 +++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 11b6d52b6..6dde899de 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -70,8 +70,11 @@ This document contains the help content for the `soroban` command-line program. * [`soroban network start`↴](#soroban-network-start) * [`soroban network stop`↴](#soroban-network-stop) * [`soroban version`↴](#soroban-version) -* [`soroban data`↴](#soroban-data) -* [`soroban data ls`↴](#soroban-data-ls) +* [`soroban cache`↴](#soroban-cache) +* [`soroban cache ls`↴](#soroban-cache-ls) +* [`soroban cache info`↴](#soroban-cache-info) +* [`soroban cache clean`↴](#soroban-cache-clean) +* [`soroban cache read`↴](#soroban-cache-read) ## `soroban` @@ -112,7 +115,7 @@ Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soro * `lab` — Experiment with early features and expert tools * `network` — Start and configure networks * `version` — Print version information -* `data` — Access cached data +* `cache` — Cache for tranasctions and contract specs ###### **Options:** @@ -1690,23 +1693,26 @@ Print version information -## `soroban data` +## `soroban cache` -Access cached data +Cache for tranasctions and contract specs -**Usage:** `soroban data ` +**Usage:** `soroban cache ` ###### **Subcommands:** -* `ls` — List identities +* `ls` — List cached actions (transactions, simulations) +* `info` — Show location of cache +* `clean` — Delete all cached actions +* `read` — Read cached action -## `soroban data ls` +## `soroban cache ls` -List identities +List cached actions (transactions, simulations) -**Usage:** `soroban data ls [OPTIONS]` +**Usage:** `soroban cache ls [OPTIONS]` ###### **Options:** @@ -1722,6 +1728,41 @@ List identities +## `soroban cache info` + +Show location of cache + +**Usage:** `soroban cache info` + + + +## `soroban cache clean` + +Delete all cached actions + +**Usage:** `soroban cache clean [OPTIONS]` + +###### **Options:** + +* `-a`, `--actions` — Actions only + + Possible values: `true`, `false` + + + + +## `soroban cache read` + +Read cached action + +**Usage:** `soroban cache read --ulid ` + +###### **Options:** + +* `--ulid ` — ULID of the cache entry + + +
From 445a15db96aafb468b5c5e2679401e0d25909ff8 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 15 Apr 2024 15:45:54 -0400 Subject: [PATCH 11/28] feat: cache read --- cmd/soroban-cli/src/commands/cache/read.rs | 50 ++++++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/read.rs index 73b959a99..520e14284 100644 --- a/cmd/soroban-cli/src/commands/cache/read.rs +++ b/cmd/soroban-cli/src/commands/cache/read.rs @@ -1,4 +1,10 @@ -use std::fs; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use clap::ValueEnum; +use ulid::Ulid; use super::super::config::locator; use crate::commands::config::data; @@ -9,8 +15,12 @@ pub enum Error { Config(#[from] locator::Error), #[error(transparent)] Data(#[from] data::Error), + #[error(transparent)] + Ulid(#[from] ulid::DecodeError), #[error("failed to find cache entry {0}")] NotFound(String), + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), } #[derive(Debug, clap::Parser, Clone)] @@ -19,13 +29,45 @@ pub struct Cmd { /// ULID of the cache entry #[arg(long, visible_alias = "id")] ulid: String, + #[arg(long)] + output: Option, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum, Default)] +pub enum OutputType { + // Status, + #[default] + Envelope, + // ResultMeta, + // Result, } impl Cmd { pub fn run(&self) -> Result<(), Error> { - let dir = data::actions_dir()?; - fs::read_to_string(dir.join(&self.ulid).with_extension(".json")) - .map_err(|_| Error::NotFound(self.ulid.clone()))?; + let file = self.file()?; + tracing::debug!("reading file {}", file.display()); + let (action, _) = data::read(&self.ulid()?)?; + let output = if let Some(_) = self.output { + match action { + data::Action::Transaction(sim) => sim.envelope_xdr.expect("missing envelope"), + _ => todo!(), + } + } else { + serde_json::to_string_pretty(&action)? + }; + println!("{output}"); Ok(()) } + + pub fn file(&self) -> Result { + Ok(data::actions_dir()?.join(&self.ulid).with_extension("json")) + } + + pub fn read_file(&self, file: &Path) -> Result { + fs::read_to_string(file).map_err(|_| Error::NotFound(self.ulid.clone())) + } + + pub fn ulid(&self) -> Result { + Ok(Ulid::from_string(&self.ulid)?) + } } From 9210164523dd5e8178ddfb34384f3259304e9157 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 16 Apr 2024 11:49:23 -0400 Subject: [PATCH 12/28] fix: clippy --- cmd/soroban-cli/src/commands/cache/read.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/read.rs index 520e14284..9ac328436 100644 --- a/cmd/soroban-cli/src/commands/cache/read.rs +++ b/cmd/soroban-cli/src/commands/cache/read.rs @@ -47,10 +47,10 @@ impl Cmd { let file = self.file()?; tracing::debug!("reading file {}", file.display()); let (action, _) = data::read(&self.ulid()?)?; - let output = if let Some(_) = self.output { + let output = if self.output.is_some() { match action { data::Action::Transaction(sim) => sim.envelope_xdr.expect("missing envelope"), - _ => todo!(), + data::Action::Simulation(_) => todo!("Only read transactions"), } } else { serde_json::to_string_pretty(&action)? From 38a290ead5498d144217dc62b485a6cc1b7d254b Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 16 Apr 2024 12:22:20 -0400 Subject: [PATCH 13/28] fix: docs --- docs/soroban-cli-full-docs.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 6dde899de..8562c1aa5 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -1755,11 +1755,15 @@ Delete all cached actions Read cached action -**Usage:** `soroban cache read --ulid ` +**Usage:** `soroban cache read [OPTIONS] --ulid ` ###### **Options:** * `--ulid ` — ULID of the cache entry +* `--output ` + + Possible values: `envelope` + From f4179249844f42b2d60b8c944266d73c01e7c71f Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 16 Apr 2024 12:59:28 -0400 Subject: [PATCH 14/28] fix: fetch SAC contract spec --- .../src/commands/contract/invoke.rs | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 2e8414285..d40f9c46f 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -331,28 +331,32 @@ impl NetworkRunnable for Cmd { let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); + let r = client.get_contract_data(&contract_id).await?; + tracing::trace!("{r:?}"); let ContractDataEntry { - val: - xdr::ScVal::ContractInstance(xdr::ScContractInstance { - executable: xdr::ContractExecutable::Wasm(hash), - .. - }), + val: xdr::ScVal::ContractInstance(xdr::ScContractInstance { executable, .. }), .. - } = client.get_contract_data(&contract_id).await? + } = r else { return Err(Error::MissingResult); }; - let hash = hash.to_string(); - // Get the contract - let spec_entries = if let Ok(entries) = data::read_spec(&hash) { - entries - } else { - let res = client.get_remote_contract_spec(&contract_id).await?; - if global_args.map_or(true, |a| !a.no_cache) { - data::write_spec(&hash, &res)?; + let spec_entries = match executable { + xdr::ContractExecutable::Wasm(hash) => { + let hash = hash.to_string(); + if let Ok(entries) = data::read_spec(&hash) { + entries + } else { + let res = client.get_remote_contract_spec(&contract_id).await?; + if global_args.map_or(true, |a| !a.no_cache) { + data::write_spec(&hash, &res)?; + } + res + } + } + xdr::ContractExecutable::StellarAsset => { + soroban_spec::read::parse_raw(&soroban_sdk::token::StellarAssetSpec::spec_xdr())? } - res }; // Get the ledger footprint From 26ddb5a69491f5afb5d83f6eb7cf96a39af5e5e1 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 23 Apr 2024 10:05:22 -0400 Subject: [PATCH 15/28] fix: PR suggestions Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- cmd/soroban-cli/Cargo.toml | 5 +---- cmd/soroban-cli/src/commands/cache/read.rs | 4 ++-- cmd/soroban-cli/src/commands/config/data.rs | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index df2570d07..8985193fd 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -96,10 +96,7 @@ cargo_metadata = "0.15.4" pathdiff = "0.2.1" dotenvy = "0.15.7" directories = { workspace = true } -# For unique identifiers -ulid.workspace = true -# For unique identifiers -ulid.features = ["serde"] +ulid = { workspace = true, features = ["serde"] } strum = "0.17.1" strum_macros = "0.17.1" gix = { version = "0.58.0", default-features = false, features = [ diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/read.rs index 9ac328436..21340e12e 100644 --- a/cmd/soroban-cli/src/commands/cache/read.rs +++ b/cmd/soroban-cli/src/commands/cache/read.rs @@ -27,8 +27,8 @@ pub enum Error { #[group(skip)] pub struct Cmd { /// ULID of the cache entry - #[arg(long, visible_alias = "id")] - ulid: String, + #[arg(long)] + id: String, #[arg(long)] output: Option, } diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 93bab46dd..451065434 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -9,7 +9,7 @@ use crate::xdr::{self, WriteXdr}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Failed to find project directories")] - FiledToFindProjectDirs, + FailedToFindProjectDirs, #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] @@ -27,7 +27,7 @@ pub const XDG_DATA_HOME: &str = "XDG_DATA_HOME"; pub fn project_dir() -> Result { std::env::var(XDG_DATA_HOME) .map_or_else( - |_| ProjectDirs::from("com", "stellar", "soroban-cli"), + |_| ProjectDirs::from("com", "stellar", "stellar-cli"), |data_home| ProjectDirs::from_path(std::path::PathBuf::from(data_home)), ) .ok_or(Error::FiledToFindProjectDirs) From ead02f23d0467f154acb680ea48c60d513c57eae Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 23 Apr 2024 15:48:39 -0400 Subject: [PATCH 16/28] fix: remove option when cleaning and fix up refactor --- cmd/soroban-cli/src/commands/cache/clean.rs | 13 +++---------- cmd/soroban-cli/src/commands/cache/read.rs | 10 +++++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/clean.rs b/cmd/soroban-cli/src/commands/cache/clean.rs index ec908b151..2ec1fdc95 100644 --- a/cmd/soroban-cli/src/commands/cache/clean.rs +++ b/cmd/soroban-cli/src/commands/cache/clean.rs @@ -15,19 +15,12 @@ pub enum Error { #[derive(Debug, clap::Parser, Clone)] #[group(skip)] -pub struct Cmd { - /// Actions only - #[arg(long, short = 'a')] - pub actions: bool, -} +pub struct Cmd {} impl Cmd { pub fn run(&self) -> Result<(), Error> { - let dir = if self.actions { - data::actions_dir()? - } else { - data::project_dir()?.data_dir().to_path_buf() - }; + let binding = data::project_dir()?; + let dir = binding.data_dir(); fs::remove_dir_all(dir)?; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/read.rs index 21340e12e..88be5b7b0 100644 --- a/cmd/soroban-cli/src/commands/cache/read.rs +++ b/cmd/soroban-cli/src/commands/cache/read.rs @@ -28,9 +28,9 @@ pub enum Error { pub struct Cmd { /// ULID of the cache entry #[arg(long)] - id: String, + pub id: String, #[arg(long)] - output: Option, + pub output: Option, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum, Default)] @@ -60,14 +60,14 @@ impl Cmd { } pub fn file(&self) -> Result { - Ok(data::actions_dir()?.join(&self.ulid).with_extension("json")) + Ok(data::actions_dir()?.join(&self.id).with_extension("json")) } pub fn read_file(&self, file: &Path) -> Result { - fs::read_to_string(file).map_err(|_| Error::NotFound(self.ulid.clone())) + fs::read_to_string(file).map_err(|_| Error::NotFound(self.id.clone())) } pub fn ulid(&self) -> Result { - Ok(Ulid::from_string(&self.ulid)?) + Ok(Ulid::from_string(&self.id)?) } } From a9e311db5e75517743cd047a7b209c2f79b16a86 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 23 Apr 2024 15:49:00 -0400 Subject: [PATCH 17/28] feat: create data_local_dir function to ensure consistent location --- cmd/soroban-cli/src/commands/cache/info.rs | 4 +--- cmd/soroban-cli/src/commands/config/data.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/info.rs b/cmd/soroban-cli/src/commands/cache/info.rs index 723ae890e..beb2cafe3 100644 --- a/cmd/soroban-cli/src/commands/cache/info.rs +++ b/cmd/soroban-cli/src/commands/cache/info.rs @@ -15,9 +15,7 @@ pub struct Cmd {} impl Cmd { pub fn run(&self) -> Result<(), Error> { - let binding = data::project_dir()?; - let dir = binding.data_dir(); - println!("{}", dir.to_string_lossy()); + println!("{:?}", data::data_local_dir()?); Ok(()) } } diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 451065434..5f717c7e3 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -30,17 +30,22 @@ pub fn project_dir() -> Result { |_| ProjectDirs::from("com", "stellar", "stellar-cli"), |data_home| ProjectDirs::from_path(std::path::PathBuf::from(data_home)), ) - .ok_or(Error::FiledToFindProjectDirs) + .ok_or(Error::FailedToFindProjectDirs) +} + +#[allow(clippy::module_name_repetitions)] +pub fn data_local_dir() -> Result { + Ok(project_dir()?.data_local_dir().to_path_buf()) } pub fn actions_dir() -> Result { - let dir = project_dir()?.data_local_dir().join("actions"); + let dir = data_local_dir()?.join("actions"); std::fs::create_dir_all(&dir)?; Ok(dir) } pub fn spec_dir() -> Result { - let dir = project_dir()?.data_local_dir().join("spec"); + let dir = data_local_dir()?.join("spec"); std::fs::create_dir_all(&dir)?; Ok(dir) } From ed0278074f2b3412441354006ec8b6f831630b29 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 23 Apr 2024 15:59:26 -0400 Subject: [PATCH 18/28] fix: md --- docs/soroban-cli-full-docs.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 8562c1aa5..42e9ec132 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -1740,14 +1740,7 @@ Show location of cache Delete all cached actions -**Usage:** `soroban cache clean [OPTIONS]` - -###### **Options:** - -* `-a`, `--actions` — Actions only - - Possible values: `true`, `false` - +**Usage:** `soroban cache clean` @@ -1755,11 +1748,11 @@ Delete all cached actions Read cached action -**Usage:** `soroban cache read [OPTIONS] --ulid ` +**Usage:** `soroban cache read [OPTIONS] --id ` ###### **Options:** -* `--ulid ` — ULID of the cache entry +* `--id ` — ULID of the cache entry * `--output ` Possible values: `envelope` From c745de7894384c49c8704616d623a7d4e8596ace Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 1 May 2024 19:07:31 -0400 Subject: [PATCH 19/28] fix: add actionlog subcommand --- .../tests/it/integration/hello_world.rs | 1 + .../src/commands/cache/{ => actionlog}/ls.rs | 2 +- .../src/commands/cache/actionlog/mod.rs | 30 +++++++++++++++++++ .../commands/cache/{ => actionlog}/read.rs | 2 +- cmd/soroban-cli/src/commands/cache/mod.rs | 23 ++++++-------- 5 files changed, 42 insertions(+), 16 deletions(-) rename cmd/soroban-cli/src/commands/cache/{ => actionlog}/ls.rs (95%) create mode 100644 cmd/soroban-cli/src/commands/cache/actionlog/mod.rs rename cmd/soroban-cli/src/commands/cache/{ => actionlog}/read.rs (97%) diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index a6e8d863a..be03afa8d 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -66,6 +66,7 @@ async fn invoke() { extend_contract(sandbox, id).await; let uid = sandbox .new_assert_cmd("cache") + .arg("actionlog") .arg("ls") .assert() .stdout_as_str(); diff --git a/cmd/soroban-cli/src/commands/cache/ls.rs b/cmd/soroban-cli/src/commands/cache/actionlog/ls.rs similarity index 95% rename from cmd/soroban-cli/src/commands/cache/ls.rs rename to cmd/soroban-cli/src/commands/cache/actionlog/ls.rs index 05d99bb7e..cb7a958c6 100644 --- a/cmd/soroban-cli/src/commands/cache/ls.rs +++ b/cmd/soroban-cli/src/commands/cache/actionlog/ls.rs @@ -1,6 +1,6 @@ use clap::command; -use super::super::config::locator; +use super::super::super::config::locator; use crate::commands::config::data; #[derive(thiserror::Error, Debug)] diff --git a/cmd/soroban-cli/src/commands/cache/actionlog/mod.rs b/cmd/soroban-cli/src/commands/cache/actionlog/mod.rs new file mode 100644 index 000000000..cb7549609 --- /dev/null +++ b/cmd/soroban-cli/src/commands/cache/actionlog/mod.rs @@ -0,0 +1,30 @@ +use clap::Parser; + +pub mod ls; +pub mod read; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// List cached actions (transactions, simulations) + Ls(ls::Cmd), + /// Read cached action + Read(read::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Ls(#[from] ls::Error), + #[error(transparent)] + Read(#[from] read::Error), +} + +impl Cmd { + pub fn run(&self) -> Result<(), Error> { + match self { + Cmd::Ls(cmd) => cmd.run()?, + Cmd::Read(cmd) => cmd.run()?, + }; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/cache/read.rs b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs similarity index 97% rename from cmd/soroban-cli/src/commands/cache/read.rs rename to cmd/soroban-cli/src/commands/cache/actionlog/read.rs index 88be5b7b0..b3f6bc57f 100644 --- a/cmd/soroban-cli/src/commands/cache/read.rs +++ b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs @@ -6,7 +6,7 @@ use std::{ use clap::ValueEnum; use ulid::Ulid; -use super::super::config::locator; +use super::super::super::config::locator; use crate::commands::config::data; #[derive(thiserror::Error, Debug)] diff --git a/cmd/soroban-cli/src/commands/cache/mod.rs b/cmd/soroban-cli/src/commands/cache/mod.rs index db629db10..32302e9d3 100644 --- a/cmd/soroban-cli/src/commands/cache/mod.rs +++ b/cmd/soroban-cli/src/commands/cache/mod.rs @@ -1,41 +1,36 @@ use clap::Parser; +pub mod actionlog; pub mod clean; pub mod info; -pub mod ls; -pub mod read; #[derive(Debug, Parser)] pub enum Cmd { - /// List cached actions (transactions, simulations) - Ls(ls::Cmd), - /// Show location of cache - Info(info::Cmd), + /// Access details about (transactions, simulations) + #[command(subcommand)] + Actionlog(actionlog::Cmd), /// Delete all cached actions Clean(clean::Cmd), - /// Read cached action - Read(read::Cmd), + /// Show location of cache + Info(info::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - Info(#[from] info::Error), - #[error(transparent)] - Ls(#[from] ls::Error), + Actionlog(#[from] actionlog::Error), #[error(transparent)] Clean(#[from] clean::Error), #[error(transparent)] - Read(#[from] read::Error), + Info(#[from] info::Error), } impl Cmd { pub fn run(&self) -> Result<(), Error> { match self { - Cmd::Ls(cmd) => cmd.run()?, + Cmd::Actionlog(cmd) => cmd.run()?, Cmd::Info(cmd) => cmd.run()?, Cmd::Clean(cmd) => cmd.run()?, - Cmd::Read(cmd) => cmd.run()?, }; Ok(()) } From a99b828a03b2a6e27ecaa2286d56c2c1b28b3315 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 13:49:58 +1000 Subject: [PATCH 20/28] com -> org --- cmd/soroban-cli/src/commands/config/data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 5f717c7e3..156b0a531 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -27,7 +27,7 @@ pub const XDG_DATA_HOME: &str = "XDG_DATA_HOME"; pub fn project_dir() -> Result { std::env::var(XDG_DATA_HOME) .map_or_else( - |_| ProjectDirs::from("com", "stellar", "stellar-cli"), + |_| ProjectDirs::from("org", "stellar", "stellar-cli"), |data_home| ProjectDirs::from_path(std::path::PathBuf::from(data_home)), ) .ok_or(Error::FailedToFindProjectDirs) From e4f03c1e0ecad5184574eb97516e73de8251335c Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 15:04:57 +1000 Subject: [PATCH 21/28] fix clean erroring on not found --- cmd/soroban-cli/src/commands/cache/clean.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/clean.rs b/cmd/soroban-cli/src/commands/cache/clean.rs index 2ec1fdc95..bea0a43d4 100644 --- a/cmd/soroban-cli/src/commands/cache/clean.rs +++ b/cmd/soroban-cli/src/commands/cache/clean.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::{fs, io::ErrorKind}; use super::super::config::locator; use crate::commands::config::data; @@ -21,7 +21,10 @@ impl Cmd { pub fn run(&self) -> Result<(), Error> { let binding = data::project_dir()?; let dir = binding.data_dir(); - fs::remove_dir_all(dir)?; + match fs::remove_dir_all(dir) { + Err(err) if err.kind() == ErrorKind::NotFound => (), + r => r?, + } Ok(()) } } From a0e11045c21bb08a85da3a07a0ead172354ffb80 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 15:16:56 +1000 Subject: [PATCH 22/28] rename info to path and add experimental doc --- cmd/soroban-cli/src/commands/cache/mod.rs | 23 ++++++++++--------- .../src/commands/cache/{info.rs => path.rs} | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) rename cmd/soroban-cli/src/commands/cache/{info.rs => path.rs} (85%) diff --git a/cmd/soroban-cli/src/commands/cache/mod.rs b/cmd/soroban-cli/src/commands/cache/mod.rs index 32302e9d3..87ee351ae 100644 --- a/cmd/soroban-cli/src/commands/cache/mod.rs +++ b/cmd/soroban-cli/src/commands/cache/mod.rs @@ -2,35 +2,36 @@ use clap::Parser; pub mod actionlog; pub mod clean; -pub mod info; +pub mod path; #[derive(Debug, Parser)] pub enum Cmd { - /// Access details about (transactions, simulations) + /// Delete the cache + Clean(clean::Cmd), + /// Show the location of the cache + Path(path::Cmd), + /// Access details about cached actions like transactions, and simulations. + /// (Experimental. May see breaking changes at any time.) #[command(subcommand)] Actionlog(actionlog::Cmd), - /// Delete all cached actions - Clean(clean::Cmd), - /// Show location of cache - Info(info::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - Actionlog(#[from] actionlog::Error), #[error(transparent)] Clean(#[from] clean::Error), #[error(transparent)] - Info(#[from] info::Error), + Path(#[from] path::Error), + #[error(transparent)] + Actionlog(#[from] actionlog::Error), } impl Cmd { pub fn run(&self) -> Result<(), Error> { match self { - Cmd::Actionlog(cmd) => cmd.run()?, - Cmd::Info(cmd) => cmd.run()?, Cmd::Clean(cmd) => cmd.run()?, + Cmd::Path(cmd) => cmd.run()?, + Cmd::Actionlog(cmd) => cmd.run()?, }; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/cache/info.rs b/cmd/soroban-cli/src/commands/cache/path.rs similarity index 85% rename from cmd/soroban-cli/src/commands/cache/info.rs rename to cmd/soroban-cli/src/commands/cache/path.rs index beb2cafe3..337608735 100644 --- a/cmd/soroban-cli/src/commands/cache/info.rs +++ b/cmd/soroban-cli/src/commands/cache/path.rs @@ -15,7 +15,7 @@ pub struct Cmd {} impl Cmd { pub fn run(&self) -> Result<(), Error> { - println!("{:?}", data::data_local_dir()?); + println!("{}", data::data_local_dir()?.to_string_lossy()); Ok(()) } } From 6d5a5e320272081c582aa010e583d93787ad9c5c Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 15:36:36 +1000 Subject: [PATCH 23/28] use terms simulate and send for consistency with actions a user performs --- .../src/commands/cache/actionlog/read.rs | 4 ++-- cmd/soroban-cli/src/commands/config/data.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/actionlog/read.rs b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs index b3f6bc57f..d5115bd88 100644 --- a/cmd/soroban-cli/src/commands/cache/actionlog/read.rs +++ b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs @@ -49,8 +49,8 @@ impl Cmd { let (action, _) = data::read(&self.ulid()?)?; let output = if self.output.is_some() { match action { - data::Action::Transaction(sim) => sim.envelope_xdr.expect("missing envelope"), - data::Action::Simulation(_) => todo!("Only read transactions"), + data::Action::Send(sim) => sim.envelope_xdr.expect("missing envelope"), + data::Action::Simulate(_) => todo!("Only read transactions"), } } else { serde_json::to_string_pretty(&action)? diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 156b0a531..84400d823 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -118,11 +118,11 @@ impl std::fmt::Display for DatedAction { let (id, a, uri) = (&self.0, &self.1, &self.2); let datetime = to_datatime(id).format("%b %d %H:%M"); let status = match a { - Action::Simulation(sim) => sim + Action::Simulate(sim) => sim .error .as_ref() .map_or_else(|| "SUCCESS".to_string(), |_| "ERROR".to_string()), - Action::Transaction(txn) => txn.status.to_string(), + Action::Send(txn) => txn.status.to_string(), }; write!(f, "{id} {} {status} {datetime} {uri} ", a.type_str(),) } @@ -142,15 +142,15 @@ struct Data { #[derive(Serialize, Deserialize, Clone)] pub enum Action { - Simulation(SimulateTransactionResponse), - Transaction(GetTransactionResponseRaw), + Simulate(SimulateTransactionResponse), + Send(GetTransactionResponseRaw), } impl Action { pub fn type_str(&self) -> String { match self { - Action::Simulation(_) => "Sim", - Action::Transaction(_) => "Txn", + Action::Simulate(_) => "Sim", + Action::Send(_) => "Txn", } .to_string() } @@ -158,14 +158,14 @@ impl Action { impl From for Action { fn from(res: SimulateTransactionResponse) -> Self { - Self::Simulation(res) + Self::Simulate(res) } } impl TryFrom for Action { type Error = xdr::Error; fn try_from(res: GetTransactionResponse) -> Result { - Ok(Self::Transaction(GetTransactionResponseRaw { + Ok(Self::Send(GetTransactionResponseRaw { status: res.status, envelope_xdr: res.envelope.as_ref().map(to_xdr).transpose()?, result_xdr: res.result.as_ref().map(to_xdr).transpose()?, @@ -194,7 +194,7 @@ mod test { let (action, new_rpc_uri) = read(&id).unwrap(); assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { - (Action::Simulation(a), Action::Simulation(b)) => { + (Action::Simulate(a), Action::Simulate(b)) => { assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); } _ => panic!("Action mismatch"), From c0bf97714b9bb129e6c55f85abaf9c8d7c4c7b28 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 15:57:25 +1000 Subject: [PATCH 24/28] simply the actionlog cache output --- .../src/commands/cache/actionlog/read.rs | 44 +++---------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/cmd/soroban-cli/src/commands/cache/actionlog/read.rs b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs index d5115bd88..67b4358f1 100644 --- a/cmd/soroban-cli/src/commands/cache/actionlog/read.rs +++ b/cmd/soroban-cli/src/commands/cache/actionlog/read.rs @@ -1,10 +1,4 @@ -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use clap::ValueEnum; -use ulid::Ulid; +use std::{fs, io, path::PathBuf}; use super::super::super::config::locator; use crate::commands::config::data; @@ -15,8 +9,6 @@ pub enum Error { Config(#[from] locator::Error), #[error(transparent)] Data(#[from] data::Error), - #[error(transparent)] - Ulid(#[from] ulid::DecodeError), #[error("failed to find cache entry {0}")] NotFound(String), #[error(transparent)] @@ -26,48 +18,22 @@ pub enum Error { #[derive(Debug, clap::Parser, Clone)] #[group(skip)] pub struct Cmd { - /// ULID of the cache entry + /// ID of the cache entry #[arg(long)] pub id: String, - #[arg(long)] - pub output: Option, -} - -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum, Default)] -pub enum OutputType { - // Status, - #[default] - Envelope, - // ResultMeta, - // Result, } impl Cmd { pub fn run(&self) -> Result<(), Error> { let file = self.file()?; tracing::debug!("reading file {}", file.display()); - let (action, _) = data::read(&self.ulid()?)?; - let output = if self.output.is_some() { - match action { - data::Action::Send(sim) => sim.envelope_xdr.expect("missing envelope"), - data::Action::Simulate(_) => todo!("Only read transactions"), - } - } else { - serde_json::to_string_pretty(&action)? - }; - println!("{output}"); + let mut file = fs::File::open(file).map_err(|_| Error::NotFound(self.id.clone()))?; + let mut stdout = io::stdout(); + let _ = io::copy(&mut file, &mut stdout); Ok(()) } pub fn file(&self) -> Result { Ok(data::actions_dir()?.join(&self.id).with_extension("json")) } - - pub fn read_file(&self, file: &Path) -> Result { - fs::read_to_string(file).map_err(|_| Error::NotFound(self.id.clone())) - } - - pub fn ulid(&self) -> Result { - Ok(Ulid::from_string(&self.id)?) - } } From def7d296c69646e231929945f2e49ed92214fc92 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 16:01:40 +1000 Subject: [PATCH 25/28] use snake_case for json to match other json the cli outputs --- cmd/soroban-cli/src/commands/config/data.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 84400d823..5b5e62e98 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -135,12 +135,14 @@ fn to_datatime(id: &ulid::Ulid) -> chrono::DateTime { } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] struct Data { action: Action, rpc_url: String, } #[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] pub enum Action { Simulate(SimulateTransactionResponse), Send(GetTransactionResponseRaw), From d15db7c66cd88633fb85fe7529563d8d99a25fee Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 16:04:30 +1000 Subject: [PATCH 26/28] match names in output --- cmd/soroban-cli/src/commands/config/data.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 5b5e62e98..717e189fa 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -151,8 +151,8 @@ pub enum Action { impl Action { pub fn type_str(&self) -> String { match self { - Action::Simulate(_) => "Sim", - Action::Send(_) => "Txn", + Action::Simulate(_) => "Simulate", + Action::Send(_) => "Send ", } .to_string() } From 72ab2b7052f8b3db3128b707589319ac83a23652 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 1 May 2024 23:38:22 -0700 Subject: [PATCH 27/28] Update soroban-cli-full-docs.md --- docs/soroban-cli-full-docs.md | 67 ++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 43adda9f6..766a7798e 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -52,10 +52,11 @@ This document contains the help content for the `soroban` command-line program. * [`soroban network stop`↴](#soroban-network-stop) * [`soroban version`↴](#soroban-version) * [`soroban cache`↴](#soroban-cache) -* [`soroban cache ls`↴](#soroban-cache-ls) -* [`soroban cache info`↴](#soroban-cache-info) * [`soroban cache clean`↴](#soroban-cache-clean) -* [`soroban cache read`↴](#soroban-cache-read) +* [`soroban cache path`↴](#soroban-cache-path) +* [`soroban cache actionlog`↴](#soroban-cache-actionlog) +* [`soroban cache actionlog ls`↴](#soroban-cache-actionlog-ls) +* [`soroban cache actionlog read`↴](#soroban-cache-actionlog-read) ## `soroban` @@ -1273,63 +1274,71 @@ Cache for tranasctions and contract specs ###### **Subcommands:** -* `ls` — List cached actions (transactions, simulations) -* `info` — Show location of cache -* `clean` — Delete all cached actions -* `read` — Read cached action +* `clean` — Delete the cache +* `path` — Show the location of the cache +* `actionlog` — Access details about cached actions like transactions, and simulations. (Experimental. May see breaking changes at any time.) -## `soroban cache ls` +## `soroban cache clean` -List cached actions (transactions, simulations) +Delete the cache -**Usage:** `soroban cache ls [OPTIONS]` +**Usage:** `soroban cache clean` -###### **Options:** -* `--global` — Use global config - Possible values: `true`, `false` +## `soroban cache path` -* `--config-dir ` — Location of config directory, default is "." -* `-l`, `--long` +Show the location of the cache - Possible values: `true`, `false` +**Usage:** `soroban cache path` +## `soroban cache actionlog` -## `soroban cache info` +Access details about cached actions like transactions, and simulations. (Experimental. May see breaking changes at any time.) -Show location of cache +**Usage:** `soroban cache actionlog ` -**Usage:** `soroban cache info` +###### **Subcommands:** +* `ls` — List cached actions (transactions, simulations) +* `read` — Read cached action -## `soroban cache clean` -Delete all cached actions +## `soroban cache actionlog ls` -**Usage:** `soroban cache clean` +List cached actions (transactions, simulations) +**Usage:** `soroban cache actionlog ls [OPTIONS]` +###### **Options:** -## `soroban cache read` +* `--global` — Use global config -Read cached action + Possible values: `true`, `false` -**Usage:** `soroban cache read [OPTIONS] --id ` +* `--config-dir ` — Location of config directory, default is "." +* `-l`, `--long` -###### **Options:** + Possible values: `true`, `false` -* `--id ` — ULID of the cache entry -* `--output ` - Possible values: `envelope` +## `soroban cache actionlog read` + +Read cached action + +**Usage:** `soroban cache actionlog read --id ` + +###### **Options:** + +* `--id ` — ID of the cache entry +
From e996366ee818c7c7ac14a86cb6344d616ef91415 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 2 May 2024 16:48:16 +1000 Subject: [PATCH 28/28] add room for more data stored with actions --- cmd/soroban-cli/src/commands/config/data.rs | 36 ++++++++++++--------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-cli/src/commands/config/data.rs b/cmd/soroban-cli/src/commands/config/data.rs index 717e189fa..409ef2227 100644 --- a/cmd/soroban-cli/src/commands/config/data.rs +++ b/cmd/soroban-cli/src/commands/config/data.rs @@ -118,11 +118,11 @@ impl std::fmt::Display for DatedAction { let (id, a, uri) = (&self.0, &self.1, &self.2); let datetime = to_datatime(id).format("%b %d %H:%M"); let status = match a { - Action::Simulate(sim) => sim + Action::Simulate { response } => response .error .as_ref() .map_or_else(|| "SUCCESS".to_string(), |_| "ERROR".to_string()), - Action::Send(txn) => txn.status.to_string(), + Action::Send { response } => response.status.to_string(), }; write!(f, "{id} {} {status} {datetime} {uri} ", a.type_str(),) } @@ -144,35 +144,41 @@ struct Data { #[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "snake_case")] pub enum Action { - Simulate(SimulateTransactionResponse), - Send(GetTransactionResponseRaw), + Simulate { + response: SimulateTransactionResponse, + }, + Send { + response: GetTransactionResponseRaw, + }, } impl Action { pub fn type_str(&self) -> String { match self { - Action::Simulate(_) => "Simulate", - Action::Send(_) => "Send ", + Action::Simulate { .. } => "Simulate", + Action::Send { .. } => "Send ", } .to_string() } } impl From for Action { - fn from(res: SimulateTransactionResponse) -> Self { - Self::Simulate(res) + fn from(response: SimulateTransactionResponse) -> Self { + Self::Simulate { response } } } impl TryFrom for Action { type Error = xdr::Error; fn try_from(res: GetTransactionResponse) -> Result { - Ok(Self::Send(GetTransactionResponseRaw { - status: res.status, - envelope_xdr: res.envelope.as_ref().map(to_xdr).transpose()?, - result_xdr: res.result.as_ref().map(to_xdr).transpose()?, - result_meta_xdr: res.result_meta.as_ref().map(to_xdr).transpose()?, - })) + Ok(Self::Send { + response: GetTransactionResponseRaw { + status: res.status, + envelope_xdr: res.envelope.as_ref().map(to_xdr).transpose()?, + result_xdr: res.result.as_ref().map(to_xdr).transpose()?, + result_meta_xdr: res.result_meta.as_ref().map(to_xdr).transpose()?, + }, + }) } } @@ -196,7 +202,7 @@ mod test { let (action, new_rpc_uri) = read(&id).unwrap(); assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { - (Action::Simulate(a), Action::Simulate(b)) => { + (Action::Simulate { response: a }, Action::Simulate { response: b }) => { assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); } _ => panic!("Action mismatch"),