From 0443ef2ccf88f9f065e0cb9199eca22cbed59d06 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 15 Apr 2024 15:11:52 -0400 Subject: [PATCH] feat: txn commands to sign, simulate, and send txns --- cmd/soroban-cli/src/commands/mod.rs | 7 +++ cmd/soroban-cli/src/commands/txn/mod.rs | 48 +++++++++++++++++ cmd/soroban-cli/src/commands/txn/send.rs | 19 +++++++ cmd/soroban-cli/src/commands/txn/sign.rs | 19 +++++++ cmd/soroban-cli/src/commands/txn/simulate.rs | 19 +++++++ cmd/soroban-cli/src/commands/txn/xdr.rs | 55 ++++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 cmd/soroban-cli/src/commands/txn/mod.rs create mode 100644 cmd/soroban-cli/src/commands/txn/send.rs create mode 100644 cmd/soroban-cli/src/commands/txn/sign.rs create mode 100644 cmd/soroban-cli/src/commands/txn/simulate.rs create mode 100644 cmd/soroban-cli/src/commands/txn/xdr.rs diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index f4f1cc315f..74e4ff053d 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -13,6 +13,7 @@ pub mod keys; pub mod lab; pub mod network; pub mod plugin; +pub mod txn; pub mod version; pub mod txn_result; @@ -103,6 +104,7 @@ impl Root { Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run().await?, Cmd::Config(c) => c.run().await?, + Cmd::Txn(tx) => tx.run().await?, Cmd::Cache(data) => data.run()?, }; Ok(()) @@ -141,6 +143,9 @@ pub enum Cmd { Network(network::Cmd), /// Print version information Version(version::Cmd), + /// Sign, Simulate, and Send transactions + #[command(subcommand)] + Txn(txn::Cmd), /// Cache for tranasctions and contract specs #[command(subcommand)] Cache(cache::Cmd), @@ -166,6 +171,8 @@ pub enum Error { #[error(transparent)] Network(#[from] network::Error), #[error(transparent)] + Txn(#[from] txn::Error), + #[error(transparent)] Cache(#[from] cache::Error), } diff --git a/cmd/soroban-cli/src/commands/txn/mod.rs b/cmd/soroban-cli/src/commands/txn/mod.rs new file mode 100644 index 0000000000..9de360fcc2 --- /dev/null +++ b/cmd/soroban-cli/src/commands/txn/mod.rs @@ -0,0 +1,48 @@ +use clap::Parser; + +pub mod send; +pub mod sign; +pub mod simulate; +pub mod xdr; + +use stellar_xdr::cli as xdr_cli; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// Add a new identity (keypair, ledger, macOS keychain) + Inspect(xdr_cli::Root), + /// Given an identity return its address (public key) + Sign(sign::Cmd), + /// Submit a transaction to the network + Send(send::Cmd), + /// Simulate a transaction + Simulate(simulate::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + /// An error during the simulation + #[error(transparent)] + Simulate(#[from] simulate::Error), + /// An error during the inspect + #[error(transparent)] + Inspect(#[from] xdr_cli::Error), + /// An error during the sign + #[error(transparent)] + Sign(#[from] sign::Error), + /// An error during the send + #[error(transparent)] + Send(#[from] send::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + match self { + Cmd::Inspect(cmd) => cmd.run()?, + Cmd::Sign(cmd) => cmd.run().await?, + Cmd::Send(cmd) => cmd.run().await?, + Cmd::Simulate(cmd) => cmd.run().await?, + }; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/txn/send.rs b/cmd/soroban-cli/src/commands/txn/send.rs new file mode 100644 index 0000000000..5eea6c0965 --- /dev/null +++ b/cmd/soroban-cli/src/commands/txn/send.rs @@ -0,0 +1,19 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[clap(flatten)] + pub xdr_args: super::xdr::Args, +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + println!("{:#?}", self.xdr_args.txn()?); + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/txn/sign.rs b/cmd/soroban-cli/src/commands/txn/sign.rs new file mode 100644 index 0000000000..5eea6c0965 --- /dev/null +++ b/cmd/soroban-cli/src/commands/txn/sign.rs @@ -0,0 +1,19 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[clap(flatten)] + pub xdr_args: super::xdr::Args, +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + println!("{:#?}", self.xdr_args.txn()?); + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/txn/simulate.rs b/cmd/soroban-cli/src/commands/txn/simulate.rs new file mode 100644 index 0000000000..5eea6c0965 --- /dev/null +++ b/cmd/soroban-cli/src/commands/txn/simulate.rs @@ -0,0 +1,19 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[clap(flatten)] + pub xdr_args: super::xdr::Args, +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + println!("{:#?}", self.xdr_args.txn()?); + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/txn/xdr.rs b/cmd/soroban-cli/src/commands/txn/xdr.rs new file mode 100644 index 0000000000..24630b166f --- /dev/null +++ b/cmd/soroban-cli/src/commands/txn/xdr.rs @@ -0,0 +1,55 @@ +use std::{io::stdin, path::PathBuf}; + +use soroban_env_host::xdr::ReadXdr; +use soroban_sdk::xdr::{Limited, Limits, Transaction}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("failed to decode XDR from base64")] + Base64Decode, + #[error("failed to decode XDR from file: {0}")] + FileDecode(PathBuf), + #[error("failed to decode XDR from stdin")] + StdinDecode, + #[error(transparent)] + Io(#[from] std::io::Error), +} + +/// XDR input, either base64 encoded or file path and stdin if neither is provided +#[derive(Debug, clap::Args, Clone)] +#[group(skip)] +pub struct Args { + /// Base64 encoded XDR transaction + #[arg( + long = "xdr-base64", + env = "STELLAR_TXN_XDR_BASE64", + conflicts_with = "xdr_file" + )] + pub xdr_base64: Option, + //// File containing Binary encoded data + #[arg( + long = "xdr-file", + env = "STELLAR_TXN_XDR_FILE", + conflicts_with = "xdr_base64" + )] + pub xdr_file: Option, +} + +impl Args { + pub fn xdr(&self) -> Result { + match (self.xdr_base64.as_ref(), self.xdr_file.as_ref()) { + (Some(xdr_base64), None) => { + T::from_xdr_base64(xdr_base64, Limits::none()).map_err(|_| Error::Base64Decode) + } + (_, Some(xdr_file)) => T::from_xdr(std::fs::read(xdr_file)?, Limits::none()) + .map_err(|_| Error::FileDecode(xdr_file.clone())), + + _ => T::read_xdr_base64_to_end(&mut Limited::new(stdin(), Limits::none())) + .map_err(|_| Error::StdinDecode), + } + } + + pub fn txn(&self) -> Result { + self.xdr::() + } +}