Skip to content

Commit

Permalink
feat: add no-build option for fee::Args (#1267)
Browse files Browse the repository at this point in the history
* feat: --no-build command allows returning built transaction

--sim-only for assembling transactions

* fix: clippy and fmt

* fix: address PR comments and skip install step for build only deploy
  • Loading branch information
willemneal authored May 9, 2024
1 parent fc1d631 commit 199a88a
Show file tree
Hide file tree
Showing 21 changed files with 344 additions and 120 deletions.
3 changes: 1 addition & 2 deletions cmd/crates/soroban-spec-tools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,8 +1137,7 @@ impl Spec {
ScSpecEntry::UdtStructV0(ScSpecUdtStructV0 { fields, .. })
if fields
.first()
.map(|f| f.name.to_utf8_string_lossy() == "0")
.unwrap_or_default() =>
.is_some_and(|f| f.name.to_utf8_string_lossy() == "0") =>
{
let fields = fields
.iter()
Expand Down
4 changes: 3 additions & 1 deletion cmd/crates/soroban-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ impl TestEnv {
source: &str,
) -> Result<String, invoke::Error> {
let cmd = self.cmd_with_config::<I, invoke::Cmd>(command_str);
self.run_cmd_with(cmd, source).await
self.run_cmd_with(cmd, source)
.await
.map(|r| r.into_result().unwrap())
}

/// A convenience method for using the invoke command.
Expand Down
27 changes: 22 additions & 5 deletions cmd/crates/soroban-test/tests/it/integration/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use predicates::boolean::PredicateBooleanExt;
use soroban_cli::commands::{
config::{locator, secret},
contract::{self, fetch},
txn_result::TxnResult,
};
use soroban_rpc::GetLatestLedgerResponse;
use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE};
Expand All @@ -11,6 +12,18 @@ use crate::integration::util::extend_contract;
use super::util::{deploy_hello, extend, HELLO_WORLD};

#[allow(clippy::too_many_lines)]
#[tokio::test]
async fn invoke_view_with_non_existent_source_account() {
let sandbox = &TestEnv::new();
let id = deploy_hello(sandbox).await;
let world = "world";
let mut cmd = hello_world_cmd(&id, world);
cmd.config.source_account = String::new();
cmd.is_view = true;
let res = sandbox.run_cmd_with(cmd, "test").await.unwrap();
assert_eq!(res, TxnResult::Res(format!(r#"["Hello",{world:?}]"#)));
}

#[tokio::test]
async fn invoke() {
let sandbox = &TestEnv::new();
Expand Down Expand Up @@ -140,14 +153,18 @@ fn invoke_hello_world(sandbox: &TestEnv, id: &str) {
.success();
}

async fn invoke_hello_world_with_lib(e: &TestEnv, id: &str) {
let cmd = contract::invoke::Cmd {
fn hello_world_cmd(id: &str, arg: &str) -> contract::invoke::Cmd {
contract::invoke::Cmd {
contract_id: id.to_string(),
slop: vec!["hello".into(), "--world=world".into()],
slop: vec!["hello".into(), format!("--world={arg}").into()],
..Default::default()
};
}
}

async fn invoke_hello_world_with_lib(e: &TestEnv, id: &str) {
let cmd = hello_world_cmd(id, "world");
let res = e.run_cmd_with(cmd, "test").await.unwrap();
assert_eq!(res, r#"["Hello","world"]"#);
assert_eq!(res, TxnResult::Res(r#"["Hello","world"]"#.to_string()));
}

fn invoke_auth(sandbox: &TestEnv, id: &str, addr: &str) {
Expand Down
7 changes: 6 additions & 1 deletion cmd/crates/soroban-test/tests/it/integration/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ pub async fn deploy_contract(sandbox: &TestEnv, wasm: &Wasm<'static>) -> String
TEST_SALT,
"--ignore-checks",
]);
sandbox.run_cmd_with(cmd, "test").await.unwrap()
sandbox
.run_cmd_with(cmd, "test")
.await
.unwrap()
.into_result()
.unwrap()
}

pub async fn extend_contract(sandbox: &TestEnv, id: &str) {
Expand Down
5 changes: 4 additions & 1 deletion cmd/crates/soroban-test/tests/it/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ pub async fn invoke_custom(
) -> Result<String, contract::invoke::Error> {
let mut i: contract::invoke::Cmd = sandbox.cmd_with_config(&["--id", id, "--", func, arg]);
i.wasm = Some(wasm.to_path_buf());
sandbox.run_cmd_with(i, TEST_ACCOUNT).await
sandbox
.run_cmd_with(i, TEST_ACCOUNT)
.await
.map(|r| r.into_result().unwrap())
}

pub const DEFAULT_CONTRACT_ID: &str = "CDR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OO5Z";
16 changes: 12 additions & 4 deletions cmd/soroban-cli/src/commands/contract/deploy/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError};
use crate::{
commands::{
config::{self, data},
global, network, NetworkRunnable,
global, network,
txn_result::TxnResult,
NetworkRunnable,
},
rpc::{Client, Error as SorobanRpcError},
utils::{contract_id_hash_from_asset, parsing::parse_asset},
Expand Down Expand Up @@ -74,13 +76,13 @@ impl Cmd {
#[async_trait::async_trait]
impl NetworkRunnable for Cmd {
type Error = Error;
type Result = String;
type Result = TxnResult<stellar_strkey::Contract>;

async fn run_against_rpc_server(
&self,
args: Option<&global::Args>,
config: Option<&config::Args>,
) -> Result<String, Error> {
) -> Result<Self::Result, Error> {
let config = config.unwrap_or(&self.config);
// Parse asset
let asset = parse_asset(&self.asset)?;
Expand Down Expand Up @@ -108,8 +110,14 @@ impl NetworkRunnable for Cmd {
network_passphrase,
&key,
)?;
if self.fee.build_only {
return Ok(TxnResult::Txn(tx));
}
let txn = client.create_assembled_transaction(&tx).await?;
let txn = self.fee.apply_to_assembled_txn(txn);
if self.fee.sim_only {
return Ok(TxnResult::Txn(txn.transaction().clone()));
}
let get_txn_resp = client
.send_assembled_transaction(txn, &key, &[], network_passphrase, None, None)
.await?
Expand All @@ -118,7 +126,7 @@ impl NetworkRunnable for Cmd {
data::write(get_txn_resp, &network.rpc_uri()?)?;
}

Ok(stellar_strkey::Contract(contract_id.0).to_string())
Ok(TxnResult::Res(stellar_strkey::Contract(contract_id.0)))
}
}

Expand Down
43 changes: 31 additions & 12 deletions cmd/soroban-cli/src/commands/contract/deploy/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use soroban_env_host::{
use crate::commands::{
config::data,
contract::{self, id::wasm::get_contract_id},
global, network, NetworkRunnable,
global, network,
txn_result::TxnResult,
NetworkRunnable,
};
use crate::{
commands::{config, contract::install, HEADING_RPC},
Expand Down Expand Up @@ -96,6 +98,8 @@ pub enum Error {
Data(#[from] data::Error),
#[error(transparent)]
Network(#[from] network::Error),
#[error(transparent)]
Wasm(#[from] wasm::Error),
}

impl Cmd {
Expand All @@ -109,23 +113,29 @@ impl Cmd {
#[async_trait::async_trait]
impl NetworkRunnable for Cmd {
type Error = Error;
type Result = String;
type Result = TxnResult<String>;

async fn run_against_rpc_server(
&self,
global_args: Option<&global::Args>,
config: Option<&config::Args>,
) -> Result<String, Error> {
) -> Result<TxnResult<String>, Error> {
let config = config.unwrap_or(&self.config);
let wasm_hash = if let Some(wasm) = &self.wasm {
let hash = install::Cmd {
wasm: wasm::Args { wasm: wasm.clone() },
config: config.clone(),
fee: self.fee.clone(),
ignore_checks: self.ignore_checks,
}
.run_against_rpc_server(global_args, Some(config))
.await?;
let hash = if self.fee.build_only {
wasm::Args { wasm: wasm.clone() }.hash()?
} else {
install::Cmd {
wasm: wasm::Args { wasm: wasm.clone() },
config: config.clone(),
fee: self.fee.clone(),
ignore_checks: self.ignore_checks,
}
.run_against_rpc_server(global_args, Some(config))
.await?
.into_result()
.expect("the value (hash) is expected because it should always be available since build-only is a shared parameter")
};
hex::encode(hash)
} else {
self.wasm_hash
Expand Down Expand Up @@ -169,16 +179,25 @@ impl NetworkRunnable for Cmd {
salt,
&key,
)?;
if self.fee.build_only {
return Ok(TxnResult::Txn(txn));
}

let txn = client.create_assembled_transaction(&txn).await?;
let txn = self.fee.apply_to_assembled_txn(txn);
if self.fee.sim_only {
return Ok(TxnResult::Txn(txn.transaction().clone()));
}
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())
Ok(TxnResult::Res(
stellar_strkey::Contract(contract_id.0).to_string(),
))
}
}

Expand Down
37 changes: 23 additions & 14 deletions cmd/soroban-cli/src/commands/contract/extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ use std::{fmt::Debug, path::Path, str::FromStr};
use clap::{command, Parser};
use soroban_env_host::xdr::{
Error as XdrError, ExtendFootprintTtlOp, ExtensionPoint, LedgerEntry, LedgerEntryChange,
LedgerEntryData, LedgerFootprint, Memo, MuxedAccount, Operation, OperationBody, Preconditions,
SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt,
TransactionMeta, TransactionMetaV3, TtlEntry, Uint256,
LedgerEntryData, LedgerFootprint, Limits, Memo, MuxedAccount, Operation, OperationBody,
Preconditions, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction,
TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, Uint256, WriteXdr,
};

use crate::{
commands::{
config::{self, data},
global, network, NetworkRunnable,
global, network,
txn_result::TxnResult,
NetworkRunnable,
},
key,
rpc::{self, Client},
Expand Down Expand Up @@ -87,11 +89,16 @@ pub enum Error {
impl Cmd {
#[allow(clippy::too_many_lines)]
pub async fn run(&self) -> Result<(), Error> {
let ttl_ledger = self.run_against_rpc_server(None, None).await?;
if self.ttl_ledger_only {
println!("{ttl_ledger}");
} else {
println!("New ttl ledger: {ttl_ledger}");
let res = self.run_against_rpc_server(None, None).await?;
match res {
TxnResult::Txn(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?),
TxnResult::Res(ttl_ledger) => {
if self.ttl_ledger_only {
println!("{ttl_ledger}");
} else {
println!("New ttl ledger: {ttl_ledger}");
}
}
}

Ok(())
Expand All @@ -111,13 +118,13 @@ impl Cmd {
#[async_trait::async_trait]
impl NetworkRunnable for Cmd {
type Error = Error;
type Result = u32;
type Result = TxnResult<u32>;

async fn run_against_rpc_server(
&self,
args: Option<&global::Args>,
config: Option<&config::Args>,
) -> Result<u32, Self::Error> {
) -> Result<TxnResult<u32>, Self::Error> {
let config = config.unwrap_or(&self.config);
let network = config.get_network()?;
tracing::trace!(?network);
Expand Down Expand Up @@ -161,7 +168,9 @@ impl NetworkRunnable for Cmd {
resource_fee: 0,
}),
};

if self.fee.build_only {
return Ok(TxnResult::Txn(tx));
}
let res = client
.prepare_and_send_transaction(&tx, &key, &[], &network.network_passphrase, None, None)
.await?;
Expand Down Expand Up @@ -194,7 +203,7 @@ impl NetworkRunnable for Cmd {
let entry = client.get_full_ledger_entries(&keys).await?;
let extension = entry.entries[0].live_until_ledger_seq;
if entry.latest_ledger + i64::from(extend_to) < i64::from(extension) {
return Ok(extension);
return Ok(TxnResult::Res(extension));
}
}

Expand All @@ -209,7 +218,7 @@ impl NetworkRunnable for Cmd {
}),
..
}),
) => Ok(*live_until_ledger_seq),
) => Ok(TxnResult::Res(*live_until_ledger_seq)),
_ => Err(Error::LedgerEntryNotFound),
}
}
Expand Down
1 change: 0 additions & 1 deletion cmd/soroban-cli/src/commands/contract/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ impl NetworkRunnable for Cmd {
client
.verify_network_passphrase(Some(&network.network_passphrase))
.await?;
// async closures are not yet stable
Ok(client.get_remote_wasm(&contract_id).await?)
}
}
Expand Down
9 changes: 6 additions & 3 deletions cmd/soroban-cli/src/commands/contract/id/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ pub enum Error {
}
impl Cmd {
pub fn run(&self) -> Result<(), Error> {
println!("{}", self.contract_address()?);
Ok(())
}

pub fn contract_address(&self) -> Result<stellar_strkey::Contract, Error> {
let asset = parse_asset(&self.asset)?;
let network = self.config.get_network()?;
let contract_id = contract_id_hash_from_asset(&asset, &network.network_passphrase)?;
let strkey_contract_id = stellar_strkey::Contract(contract_id.0).to_string();
println!("{strkey_contract_id}");
Ok(())
Ok(stellar_strkey::Contract(contract_id.0))
}
}
Loading

0 comments on commit 199a88a

Please sign in to comment.