Skip to content

Commit

Permalink
feat: use data directory and store simulations and transaction responses
Browse files Browse the repository at this point in the history
  • Loading branch information
willemneal committed Mar 7, 2024
1 parent cb3b0f8 commit e0fdd4c
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 29 deletions.
80 changes: 55 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ path = "cmd/soroban-cli"

[workspace.dependencies.soroban-rpc]
version = "=20.3.3"
# git = "https://github.com/stellar/soroban-rpc"
git = "https://github.com/stellar/soroban-rpc"
rev = "e84d0154d8ad416d64ac3a2c16d81f962595b5ea"

[workspace.dependencies.stellar-xdr]
version = "=20.1.0"
Expand Down Expand Up @@ -103,6 +104,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"
Expand Down
5 changes: 5 additions & 0 deletions cmd/soroban-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
138 changes: 138 additions & 0 deletions cmd/soroban-cli/src/commands/config/data.rs
Original file line number Diff line number Diff line change
@@ -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<directories::ProjectDirs, Error> {
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<std::path::PathBuf, Error> {
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<ulid::Ulid, Error> {
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<Vec<ulid::Ulid>, 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::<Result<Vec<String>, Error>>()?;
list.sort();
Ok(list
.iter()
.map(|s|ulid::Ulid::from_str(s))
.collect::<Result<Vec<_>, _>>()?)
}

pub fn list_actions() -> Result<Vec<(ulid::Ulid, Action, Uri)>, Error> {
list_ulids()?.into_iter()
.map(|id| {
let (action, uri) = read(&id)?;
Ok((id, action, uri))
})
.collect::<Result<Vec<_>,Error>>()
}

#[derive(Serialize, Deserialize)]
struct Data {
action: Action,
rpc_url: String,
}

#[derive(Serialize, Deserialize, Clone)]
pub enum Action {
Simulation(SimulateTransactionResponse),
Transaction(GetTransactionResponseRaw),
}

impl From<SimulateTransactionResponse> for Action {
fn from(res: SimulateTransactionResponse) -> Self {
Self::Simulation(res)
}
}

impl TryFrom<GetTransactionResponse> for Action {
type Error = xdr::Error;
fn try_from(res: GetTransactionResponse) -> Result<Self, Self::Error> {
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<String, xdr::Error> {
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"),
}
}
}
1 change: 1 addition & 0 deletions cmd/soroban-cli/src/commands/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use self::{network::Network, secret::Secret};

use super::{keys, network};

pub mod data;
pub mod locator;
pub mod secret;

Expand Down
16 changes: 13 additions & 3 deletions cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Infallible> for Error {
Expand Down Expand Up @@ -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 {
Expand All @@ -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()?)
};

Expand Down
Loading

0 comments on commit e0fdd4c

Please sign in to comment.