Skip to content

Commit

Permalink
feat(devx): Add filter flag to Iota CLI (#2574)
Browse files Browse the repository at this point in the history
* cleanup

Signed-off-by: salaheldinsoliman <[email protected]>

* use strum to remove custom value parser

Signed-off-by: salaheldinsoliman <[email protected]>

* appease dprint

Signed-off-by: salaheldinsoliman <[email protected]>

* remove unneeded type annotation

Signed-off-by: salaheldinsoliman <[email protected]>

* appease dprint

Signed-off-by: salaheldinsoliman <[email protected]>

* remove strum_macros from Cargo.toml

Signed-off-by: salaheldinsoliman <[email protected]>

* use HashSet instead of Vec

Signed-off-by: salaheldinsoliman <[email protected]>

* refactor opts_from_cli

Signed-off-by: salaheldinsoliman <[email protected]>

* apply fmt

Signed-off-by: salaheldinsoliman <[email protected]>

* appease clippy

Signed-off-by: salaheldinsoliman <[email protected]>

* appease clippy

Signed-off-by: salaheldinsoliman <[email protected]>

* Apply suggestions from code review

Co-authored-by: Thoralf-M <[email protected]>

---------

Signed-off-by: salaheldinsoliman <[email protected]>
Co-authored-by: Thoralf-M <[email protected]>
  • Loading branch information
salaheldinsoliman and Thoralf-M authored Nov 8, 2024
1 parent 549b554 commit c445090
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions crates/iota/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ serde_json.workspace = true
serde_yaml.workspace = true
shell-words = "1.1.0"
signature.workspace = true
strum.workspace = true
tabled.workspace = true
tap.workspace = true
tempfile.workspace = true
Expand Down
106 changes: 91 additions & 15 deletions crates/iota/src/client_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0

use std::{
collections::{BTreeMap, btree_map::Entry},
cmp::Eq,
collections::{BTreeMap, HashSet, btree_map::Entry},
fmt::{Debug, Display, Formatter, Write},
fs,
path::{Path, PathBuf},
Expand Down Expand Up @@ -57,6 +58,7 @@ use iota_types::{
move_package::UpgradeCap,
object::Owner,
parse_iota_type_tag,
quorum_driver_types::ExecuteTransactionRequestType,
signature::GenericSignature,
transaction::{
SenderSignedData, Transaction, TransactionData, TransactionDataAPI, TransactionKind,
Expand All @@ -72,6 +74,7 @@ use reqwest::StatusCode;
use serde::Serialize;
use serde_json::{Value, json};
use shared_crypto::intent::Intent;
use strum::EnumString;
use tabled::{
builder::Builder as TableBuilder,
settings::{
Expand Down Expand Up @@ -649,6 +652,13 @@ pub struct Opts {
/// --signed-tx-bytes <SIGNED_TX_BYTES>`.
#[arg(long, required = false)]
pub serialize_signed_transaction: bool,

/// Select which fields of the response to display.
/// If not provided, all fields are displayed.
/// The fields are: effects, input, events, object_changes,
/// balance_changes.
#[clap(long, required = false, value_delimiter = ',', num_args = 0.., value_parser = parse_emit_option)]
pub emit: HashSet<EmitOption>,
}

/// Global options with gas
Expand All @@ -665,23 +675,40 @@ pub struct OptsWithGas {

impl Opts {
/// Uses the passed gas_budget for the gas budget variable and sets all
/// other flags to false.
/// other flags to false, and emit to an empty vector(defaulting to all emit
/// options).
pub fn for_testing(gas_budget: u64) -> Self {
Self {
gas_budget: Some(gas_budget),
dry_run: false,
serialize_unsigned_transaction: false,
serialize_signed_transaction: false,
emit: HashSet::new(),
}
}
/// Uses the passed gas_budget for the gas budget variable, sets dry run to
/// true, and sets all other flags to false.
/// true, and sets all other flags to false, and emit to an empty
/// vector(defaulting to all emit options).
pub fn for_testing_dry_run(gas_budget: u64) -> Self {
Self {
gas_budget: Some(gas_budget),
dry_run: true,
serialize_unsigned_transaction: false,
serialize_signed_transaction: false,
emit: HashSet::new(),
}
}

/// Uses the passed gas_budget for the gas budget variable, sets dry run to
/// false, and sets all other flags to false, and emit to the passed emit
/// vector.
pub fn for_testing_emit_options(gas_budget: u64, emit: HashSet<EmitOption>) -> Self {
Self {
gas_budget: Some(gas_budget),
dry_run: false,
serialize_unsigned_transaction: false,
serialize_signed_transaction: false,
emit,
}
}
}
Expand All @@ -703,6 +730,30 @@ impl OptsWithGas {
rest: Opts::for_testing_dry_run(gas_budget),
}
}

/// Sets the gas object to gas, and uses the passed gas_budget for the gas
/// budget variable. Dry run is set to false, and emit to the passed emit
/// vector. All other flags are set to false.
pub fn for_testing_emit_options(
gas: Option<ObjectID>,
gas_budget: u64,
emit: HashSet<EmitOption>,
) -> Self {
Self {
gas,
rest: Opts::for_testing_emit_options(gas_budget, emit),
}
}
}

#[derive(Clone, Debug, EnumString, Hash, Eq, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum EmitOption {
Effects,
Input,
Events,
ObjectChanges,
BalanceChanges,
}

#[derive(serde::Deserialize)]
Expand Down Expand Up @@ -2930,18 +2981,18 @@ pub(crate) async fn dry_run_or_execute_or_serialize(
))
} else {
let transaction = Transaction::new(sender_signed_data);
let mut response = context.execute_transaction_may_fail(transaction).await?;
if let Some(effects) = response.effects.as_mut() {
prerender_clever_errors(effects, client.read_api()).await;
}
let effects = response.effects.as_ref().ok_or_else(|| {
anyhow!("Effects from IotaTransactionBlockResult should not be empty")
})?;
if let IotaExecutionStatus::Failure { error } = effects.status() {
return Err(anyhow!(
"Error executing transaction '{}': {error}",
response.digest
));
let response = client
.quorum_driver_api()
.execute_transaction_block(
transaction,
opts_from_cli(opts.emit),
Some(ExecuteTransactionRequestType::WaitForLocalExecution),
)
.await?;

let errors: &Vec<String> = response.errors.as_ref();
if !errors.is_empty() {
return Err(anyhow!("Error executing transaction: {errors:#?}"));
}
Ok(IotaClientCommandResult::TransactionBlock(response))
}
Expand All @@ -2959,3 +3010,28 @@ pub(crate) async fn prerender_clever_errors(
}
}
}

fn opts_from_cli(opts: HashSet<EmitOption>) -> IotaTransactionBlockResponseOptions {
if opts.is_empty() {
IotaTransactionBlockResponseOptions::new()
.with_effects()
.with_input()
.with_events()
.with_object_changes()
.with_balance_changes()
} else {
IotaTransactionBlockResponseOptions {
show_input: opts.contains(&EmitOption::Input),
show_events: opts.contains(&EmitOption::Events),
show_object_changes: opts.contains(&EmitOption::ObjectChanges),
show_balance_changes: opts.contains(&EmitOption::BalanceChanges),
show_effects: opts.contains(&EmitOption::Effects),
show_raw_effects: false,
show_raw_input: false,
}
}
}

fn parse_emit_option(s: &str) -> Result<EmitOption, String> {
EmitOption::from_str(s).map_err(|_| format!("Invalid emit option: {}", s))
}
3 changes: 3 additions & 0 deletions crates/iota/src/client_ptb/ptb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashSet;

use anyhow::{Error, anyhow, ensure};
use clap::{Args, ValueHint, arg};
use iota_json_rpc_types::{IotaExecutionStatus, IotaTransactionBlockEffectsAPI};
Expand Down Expand Up @@ -154,6 +156,7 @@ impl PTB {
gas_budget: program_metadata.gas_budget.map(|x| x.value),
serialize_unsigned_transaction: program_metadata.serialize_unsigned_set,
serialize_signed_transaction: program_metadata.serialize_signed_set,
emit: HashSet::new(),
},
};

Expand Down
Loading

0 comments on commit c445090

Please sign in to comment.