diff --git a/cli/src/base_cli/commands/balance.rs b/cli/src/base_cli/commands/balance.rs index 752190d425..cea86ae48b 100644 --- a/cli/src/base_cli/commands/balance.rs +++ b/cli/src/base_cli/commands/balance.rs @@ -17,7 +17,7 @@ use crate::{ command_utils::{get_accountid_from_str, get_chain_api}, - Cli, + Cli, CliResult, CliResultOk, }; use substrate_api_client::GetAccountInformation; @@ -28,11 +28,12 @@ pub struct BalanceCommand { } impl BalanceCommand { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { let api = get_chain_api(cli); let accountid = get_accountid_from_str(&self.account); let balance = if let Some(data) = api.get_account_data(&accountid).unwrap() { data.free } else { 0 }; println!("{}", balance); + Ok(CliResultOk::Balance { balance }) } } diff --git a/cli/src/base_cli/commands/faucet.rs b/cli/src/base_cli/commands/faucet.rs index a034baf0fb..3c13eb18ae 100644 --- a/cli/src/base_cli/commands/faucet.rs +++ b/cli/src/base_cli/commands/faucet.rs @@ -17,7 +17,7 @@ use crate::{ command_utils::{get_accountid_from_str, get_chain_api}, - Cli, + Cli, CliResult, CliResultOk, }; use itp_node_api::api_client::ParentchainExtrinsicSigner; use my_node_runtime::{BalancesCall, RuntimeCall}; @@ -36,7 +36,7 @@ pub struct FaucetCommand { } impl FaucetCommand { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { let mut api = get_chain_api(cli); api.set_signer(ParentchainExtrinsicSigner::new(AccountKeyring::Alice.pair())); let mut nonce = api.get_nonce().unwrap(); @@ -56,5 +56,7 @@ impl FaucetCommand { let _blockh = api.submit_extrinsic(xt).unwrap(); nonce += 1; } + + Ok(CliResultOk::None) } } diff --git a/cli/src/base_cli/commands/listen.rs b/cli/src/base_cli/commands/listen.rs index ab303a2d73..f50b640b78 100644 --- a/cli/src/base_cli/commands/listen.rs +++ b/cli/src/base_cli/commands/listen.rs @@ -15,7 +15,7 @@ */ -use crate::{command_utils::get_chain_api, Cli}; +use crate::{command_utils::get_chain_api, Cli, CliResult, CliResultOk}; use base58::ToBase58; use codec::Encode; use log::*; @@ -34,7 +34,7 @@ pub struct ListenCommand { } impl ListenCommand { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { println!("{:?} {:?}", self.events, self.blocks); let api = get_chain_api(cli); info!("Subscribing to events"); @@ -44,12 +44,12 @@ impl ListenCommand { loop { if let Some(e) = self.events { if count >= e { - return + return Ok(CliResultOk::None) } }; if let Some(b) = self.blocks { if blocks >= b { - return + return Ok(CliResultOk::None) } }; diff --git a/cli/src/base_cli/commands/shield_funds.rs b/cli/src/base_cli/commands/shield_funds.rs index 05ca24d29f..dbad28d071 100644 --- a/cli/src/base_cli/commands/shield_funds.rs +++ b/cli/src/base_cli/commands/shield_funds.rs @@ -17,7 +17,7 @@ use crate::{ command_utils::{get_accountid_from_str, get_chain_api, *}, - Cli, + Cli, CliResult, CliResultOk, }; use base58::FromBase58; use codec::{Decode, Encode}; @@ -42,7 +42,7 @@ pub struct ShieldFundsCommand { } impl ShieldFundsCommand { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { let mut chain_api = get_chain_api(cli); let shard_opt = match self.shard.from_base58() { @@ -77,5 +77,7 @@ impl ShieldFundsCommand { let tx_hash = chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).unwrap(); println!("[+] TrustedOperation got finalized. Hash: {:?}\n", tx_hash); + + Ok(CliResultOk::None) } } diff --git a/cli/src/base_cli/commands/transfer.rs b/cli/src/base_cli/commands/transfer.rs index 34e221b74f..04c26a1d34 100644 --- a/cli/src/base_cli/commands/transfer.rs +++ b/cli/src/base_cli/commands/transfer.rs @@ -17,7 +17,7 @@ use crate::{ command_utils::{get_accountid_from_str, get_chain_api, *}, - Cli, + Cli, CliResult, CliResultOk, }; use itp_node_api::api_client::{Address, ParentchainExtrinsicSigner}; use log::*; @@ -40,7 +40,7 @@ pub struct TransferCommand { } impl TransferCommand { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { let from_account = get_pair_from_str(&self.from); let to_account = get_accountid_from_str(&self.to); info!("from ss58 is {}", from_account.public().to_ss58check()); @@ -54,6 +54,9 @@ impl TransferCommand { .extrinsic_hash; println!("[+] TrustedOperation got finalized. Hash: {:?}\n", tx_hash); let result = api.get_account_data(&to_account).unwrap().unwrap(); - println!("balance for {} is now {}", to_account, result.free); + let balance = result.free; + println!("balance for {} is now {}", to_account, balance); + + Ok(CliResultOk::Balance { balance }) } } diff --git a/cli/src/base_cli/mod.rs b/cli/src/base_cli/mod.rs index 525ee32eed..2279bceb23 100644 --- a/cli/src/base_cli/mod.rs +++ b/cli/src/base_cli/mod.rs @@ -21,7 +21,7 @@ use crate::{ shield_funds::ShieldFundsCommand, transfer::TransferCommand, }, command_utils::*, - Cli, + Cli, CliResult, CliResultOk, }; use base58::ToBase58; use chrono::{DateTime, Utc}; @@ -73,7 +73,7 @@ pub enum BaseCommand { } impl BaseCommand { - pub fn run(&self, cli: &Cli) { + pub fn run(&self, cli: &Cli) -> CliResult { match self { BaseCommand::Balance(cmd) => cmd.run(cli), BaseCommand::NewAccount => new_account(), @@ -89,42 +89,63 @@ impl BaseCommand { } } -fn new_account() { +fn new_account() -> CliResult { let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); let key: sr25519::AppPair = store.generate().unwrap(); + let key_base58 = key.public().to_ss58check(); drop(store); - println!("{}", key.public().to_ss58check()); + println!("{}", key_base58); + Ok(CliResultOk::PubKeysBase58 { + pubkeys_sr25519: Some(vec![key_base58]), + pubkeys_ed25519: None, + }) } -fn list_accounts() { +fn list_accounts() -> CliResult { let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); println!("sr25519 keys:"); + let mut keys_sr25519 = vec![]; for pubkey in store.public_keys::().unwrap().into_iter() { - println!("{}", pubkey.to_ss58check()); + let key_ss58 = pubkey.to_ss58check(); + println!("{}", key_ss58); + keys_sr25519.push(key_ss58); } println!("ed25519 keys:"); + let mut keys_ed25519 = vec![]; for pubkey in store.public_keys::().unwrap().into_iter() { - println!("{}", pubkey.to_ss58check()); + let key_ss58 = pubkey.to_ss58check(); + println!("{}", key_ss58); + keys_ed25519.push(key_ss58); } drop(store); + + Ok(CliResultOk::PubKeysBase58 { + pubkeys_sr25519: Some(keys_sr25519), + pubkeys_ed25519: Some(keys_ed25519), + }) } -fn print_metadata(cli: &Cli) { +fn print_metadata(cli: &Cli) -> CliResult { let api = get_chain_api(cli); let meta = api.metadata(); println!("Metadata:\n {}", Metadata::pretty_format(&meta.runtime_metadata()).unwrap()); + Ok(CliResultOk::Metadata { metadata: meta.clone() }) } -fn print_sgx_metadata(cli: &Cli) { +fn print_sgx_metadata(cli: &Cli) -> CliResult { let worker_api_direct = get_worker_api_direct(cli); let metadata = worker_api_direct.get_state_metadata().unwrap(); println!("Metadata:\n {}", Metadata::pretty_format(metadata.runtime_metadata()).unwrap()); + Ok(CliResultOk::Metadata { metadata }) } -fn list_workers(cli: &Cli) { +fn list_workers(cli: &Cli) -> CliResult { let api = get_chain_api(cli); let wcount = api.enclave_count(None).unwrap(); println!("number of workers registered: {}", wcount); + + let mut mr_enclaves = Vec::with_capacity(wcount as usize); + for w in 1..=wcount { let enclave = api.enclave(w, None).unwrap(); if enclave.is_none() { @@ -134,10 +155,15 @@ fn list_workers(cli: &Cli) { let enclave = enclave.unwrap(); let timestamp = DateTime::::from(UNIX_EPOCH + Duration::from_millis(enclave.timestamp)); + let mr_enclave = enclave.mr_enclave.to_base58(); println!("Enclave {}", w); println!(" AccountId: {}", enclave.pubkey.to_ss58check()); - println!(" MRENCLAVE: {}", enclave.mr_enclave.to_base58()); + println!(" MRENCLAVE: {}", mr_enclave); println!(" RA timestamp: {}", timestamp); println!(" URL: {}", enclave.url); + + mr_enclaves.push(mr_enclave); } + + Ok(CliResultOk::MrEnclaveBase58 { mr_enclaves }) } diff --git a/cli/src/benchmark/mod.rs b/cli/src/benchmark/mod.rs index 67c1faf7bd..bae12f9082 100644 --- a/cli/src/benchmark/mod.rs +++ b/cli/src/benchmark/mod.rs @@ -23,7 +23,7 @@ use crate::{ decode_balance, get_identifiers, get_keystore_path, get_pair_from_str, }, trusted_operation::{get_json_request, get_state, perform_trusted_operation, wait_until}, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use hdrhistogram::Histogram; @@ -113,7 +113,7 @@ struct BenchmarkTransaction { } impl BenchmarkCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let random_wait_before_transaction_ms: (u32, u32) = ( self.random_wait_before_transaction_min_ms, self.random_wait_before_transaction_max_ms, @@ -247,7 +247,9 @@ impl BenchmarkCommand { overall_start.elapsed().as_millis() ); - print_benchmark_statistic(outputs, self.wait_for_confirmation) + print_benchmark_statistic(outputs, self.wait_for_confirmation); + + Ok(CliResultOk::None) } } @@ -262,7 +264,7 @@ fn get_balance( ); let getter_start_timer = Instant::now(); - let getter_result = get_state(direct_client, shard, &getter); + let getter_result = get_state(direct_client, shard, &getter).unwrap_or_default(); let getter_execution_time = getter_start_timer.elapsed().as_millis(); let balance = decode_balance(getter_result); @@ -282,7 +284,7 @@ fn get_nonce( ); let getter_start_timer = Instant::now(); - let getter_result = get_state(direct_client, shard, &getter); + let getter_result = get_state(direct_client, shard, &getter).unwrap_or_default(); let getter_execution_time = getter_start_timer.elapsed().as_millis(); let nonce = match getter_result { diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 5fdd6f0383..e01a79d930 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -16,7 +16,7 @@ */ extern crate chrono; -use crate::{base_cli::BaseCommand, trusted_cli::TrustedCli, Cli}; +use crate::{base_cli::BaseCommand, trusted_cli::TrustedCli, Cli, CliResult, CliResultOk}; use clap::Subcommand; #[cfg(feature = "teeracle")] @@ -43,12 +43,18 @@ pub enum Commands { Attesteer(AttesteerCommand), } -pub fn match_command(cli: &Cli) { +pub fn match_command(cli: &Cli) -> CliResult { match &cli.command { Commands::Base(cmd) => cmd.run(cli), Commands::Trusted(trusted_cli) => trusted_cli.run(cli), #[cfg(feature = "teeracle")] - Commands::Oracle(cmd) => cmd.run(cli), - Commands::Attesteer(cmd) => cmd.run(cli), - }; + Commands::Oracle(cmd) => { + cmd.run(cli); + Ok(CliResultOk::None) + }, + Commands::Attesteer(cmd) => { + cmd.run(cli); + Ok(CliResultOk::None) + }, + } } diff --git a/cli/src/evm/commands/evm_call.rs b/cli/src/evm/commands/evm_call.rs index a84d05156e..7e2469f5da 100644 --- a/cli/src/evm/commands/evm_call.rs +++ b/cli/src/evm/commands/evm_call.rs @@ -20,7 +20,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::{get_identifiers, get_pair_from_str}, trusted_operation::perform_trusted_operation, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{Index, TrustedCall, TrustedGetter, TrustedOperation}; @@ -43,7 +43,7 @@ pub struct EvmCallCommands { } impl EvmCallCommands { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let sender = get_pair_from_str(trusted_args, &self.from); let sender_acc: AccountId = sender.public().into(); @@ -80,6 +80,7 @@ impl EvmCallCommands { ) .sign(&KeyPair::Sr25519(Box::new(sender)), nonce, &mrenclave, &shard) .into_trusted_operation(trusted_args.direct); - let _ = perform_trusted_operation(cli, trusted_args, &function_call); + Ok(perform_trusted_operation(cli, trusted_args, &function_call) + .map(|_| CliResultOk::None)?) } } diff --git a/cli/src/evm/commands/evm_command_utils.rs b/cli/src/evm/commands/evm_command_utils.rs index fe46dcacde..b752891312 100644 --- a/cli/src/evm/commands/evm_command_utils.rs +++ b/cli/src/evm/commands/evm_command_utils.rs @@ -20,15 +20,10 @@ macro_rules! get_layer_two_evm_nonce { let top: TrustedOperation = TrustedGetter::evm_nonce($signer_pair.public().into()) .sign(&KeyPair::Sr25519(Box::new($signer_pair.clone()))) .into(); - let res = perform_trusted_operation($cli, $trusted_args, &top); - let nonce: Index = if let Some(n) = res { - if let Ok(nonce) = Index::decode(&mut n.as_slice()) { - nonce - } else { - 0 - } - } else { - 0 + let res = perform_trusted_operation($cli, $trusted_args, &top).unwrap_or_default(); + let nonce = match res { + Some(n) => Index::decode(&mut n.as_slice()).unwrap_or(0), + None => 0, }; debug!("got evm nonce: {:?}", nonce); nonce diff --git a/cli/src/evm/commands/evm_create.rs b/cli/src/evm/commands/evm_create.rs index 9b1e3d7de3..ce7ed0e418 100644 --- a/cli/src/evm/commands/evm_create.rs +++ b/cli/src/evm/commands/evm_create.rs @@ -20,7 +20,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::{get_identifiers, get_pair_from_str}, trusted_operation::perform_trusted_operation, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{ @@ -44,7 +44,7 @@ pub struct EvmCreateCommands { } impl EvmCreateCommands { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let from = get_pair_from_str(trusted_args, &self.from); let from_acc: AccountId = from.public().into(); println!("from ss58 is {}", from.public().to_ss58check()); @@ -80,10 +80,11 @@ impl EvmCreateCommands { .sign(&from.into(), nonce, &mrenclave, &shard) .into_trusted_operation(trusted_args.direct); - let _ = perform_trusted_operation(cli, trusted_args, &top); + let _ = perform_trusted_operation(cli, trusted_args, &top)?; let execution_address = evm_create_address(sender_evm_acc, evm_account_nonce); info!("trusted call evm_create executed"); println!("Created the smart contract with address {:?}", execution_address); + Ok(CliResultOk::H160 { hash: execution_address }) } } diff --git a/cli/src/evm/commands/evm_read.rs b/cli/src/evm/commands/evm_read.rs index 8de4309c6f..c35e12899f 100644 --- a/cli/src/evm/commands/evm_read.rs +++ b/cli/src/evm/commands/evm_read.rs @@ -17,7 +17,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::get_pair_from_str, - trusted_operation::perform_trusted_operation, Cli, + trusted_operation::perform_trusted_operation, Cli, CliError, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{TrustedGetter, TrustedOperation}; @@ -36,7 +36,7 @@ pub struct EvmReadCommands { } impl EvmReadCommands { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let sender = get_pair_from_str(trusted_args, &self.from); let sender_acc: AccountId = sender.public().into(); @@ -56,21 +56,20 @@ impl EvmReadCommands { TrustedGetter::evm_account_storages(sender_acc, execution_address, H256::zero()) .sign(&KeyPair::Sr25519(Box::new(sender))) .into(); - let res = perform_trusted_operation(cli, trusted_args, &top); + let res = perform_trusted_operation(cli, trusted_args, &top)?; debug!("received result for balance"); - let val = if let Some(v) = res { + if let Some(v) = res { if let Ok(vd) = H256::decode(&mut v.as_slice()) { - vd + println!("{:?}", vd); + Ok(CliResultOk::H256 { hash: vd }) } else { error!("could not decode value. {:x?}", v); - H256::zero() + Err(CliError::EvmRead { msg: format!("could not decode value. {:x?}", v) }) } } else { error!("Nothing in state!"); - H256::zero() - }; - - println!("{:?}", val); + Err(CliError::EvmRead { msg: "Nothing in state!".to_string() }) + } } } diff --git a/cli/src/evm/mod.rs b/cli/src/evm/mod.rs index fca55b7605..0b1ff31d47 100644 --- a/cli/src/evm/mod.rs +++ b/cli/src/evm/mod.rs @@ -20,7 +20,7 @@ use crate::{ evm_call::EvmCallCommands, evm_create::EvmCreateCommands, evm_read::EvmReadCommands, }, trusted_cli::TrustedCli, - Cli, + Cli, CliResult, }; mod commands; @@ -39,7 +39,7 @@ pub enum EvmCommand { } impl EvmCommand { - pub fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { match self { EvmCommand::EvmCreate(cmd) => cmd.run(cli, trusted_args), EvmCommand::EvmRead(cmd) => cmd.run(cli, trusted_args), diff --git a/cli/src/lib.rs b/cli/src/lib.rs new file mode 100644 index 0000000000..dd7d783d64 --- /dev/null +++ b/cli/src/lib.rs @@ -0,0 +1,125 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! an RPC client to Integritee using websockets +//! +//! examples +//! integritee_cli 127.0.0.1:9944 transfer //Alice 5G9RtsTbiYJYQYMHbWfyPoeuuxNaCbC16tZ2JGrZ4gRKwz14 1000 +//! +#![feature(rustc_private)] +#[macro_use] +extern crate clap; +extern crate chrono; +extern crate env_logger; +extern crate log; + +mod attesteer; +mod base_cli; +mod benchmark; +mod command_utils; +mod error; +#[cfg(feature = "evm")] +mod evm; +#[cfg(feature = "teeracle")] +mod oracle; +mod trusted_base_cli; +mod trusted_cli; +mod trusted_command_utils; +mod trusted_operation; + +pub mod commands; + +use crate::commands::Commands; +use clap::Parser; +use sp_core::{H160, H256}; +use substrate_api_client::Metadata; +use thiserror::Error; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[derive(Parser)] +#[clap(name = "integritee-cli")] +#[clap(version = VERSION)] +#[clap(author = "Integritee AG ")] +#[clap(about = "interact with integritee-node and workers", long_about = None)] +#[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] +pub struct Cli { + /// node url + #[clap(short = 'u', long, default_value_t = String::from("ws://127.0.0.1"))] + node_url: String, + + /// node port + #[clap(short = 'p', long, default_value_t = String::from("9944"))] + node_port: String, + + /// worker url + #[clap(short = 'U', long, default_value_t = String::from("wss://127.0.0.1"))] + worker_url: String, + + /// worker direct invocation port + #[clap(short = 'P', long, default_value_t = String::from("2000"))] + trusted_worker_port: String, + + #[clap(subcommand)] + command: Commands, +} + +pub enum CliResultOk { + PubKeysBase58 { + pubkeys_sr25519: Option>, + pubkeys_ed25519: Option>, + }, + Balance { + balance: u128, + }, + MrEnclaveBase58 { + mr_enclaves: Vec, + }, + Metadata { + metadata: Metadata, + }, + H256 { + hash: H256, + }, + /// Result of "EvmCreateCommands": execution_address + H160 { + hash: H160, + }, + // TODO should ideally be removed; or at least drastically less used + // We WANT all commands exposed by the cli to return something useful for the caller(ie instead of printing) + None, +} + +#[derive(Debug, Error)] +pub enum CliError { + #[error("trusted operation error: {:?}", msg)] + TrustedOp { msg: String }, + #[error("EvmReadCommands error: {:?}", msg)] + EvmRead { msg: String }, +} + +pub type CliResult = Result; + +/// This is used for the commands that directly call `perform_trusted_operation` +/// which typically return `CliResultOk::None` +/// +/// eg: `SetBalanceCommand`,`TransferCommand`,`UnshieldFundsCommand` +impl From for CliError { + fn from(value: trusted_operation::TrustedOperationError) -> Self { + CliError::TrustedOp { msg: value.to_string() } + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 6deb825ce2..6dca5b1e90 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -15,69 +15,13 @@ */ -//! an RPC client to Integritee using websockets -//! -//! examples -//! integritee_cli 127.0.0.1:9944 transfer //Alice 5G9RtsTbiYJYQYMHbWfyPoeuuxNaCbC16tZ2JGrZ4gRKwz14 1000 -//! -#![feature(rustc_private)] -#[macro_use] -extern crate clap; -extern crate chrono; -extern crate env_logger; -extern crate log; - -mod attesteer; -mod base_cli; -mod benchmark; -mod command_utils; -mod commands; -mod error; -#[cfg(feature = "evm")] -mod evm; -#[cfg(feature = "teeracle")] -mod oracle; -mod trusted_base_cli; -mod trusted_cli; -mod trusted_command_utils; -mod trusted_operation; - -use crate::commands::Commands; use clap::Parser; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[derive(Parser)] -#[clap(name = "integritee-cli")] -#[clap(version = VERSION)] -#[clap(author = "Integritee AG ")] -#[clap(about = "interact with integritee-node and workers", long_about = None)] -#[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] -pub struct Cli { - /// node url - #[clap(short = 'u', long, default_value_t = String::from("ws://127.0.0.1"))] - node_url: String, - - /// node port - #[clap(short = 'p', long, default_value_t = String::from("9944"))] - node_port: String, - - /// worker url - #[clap(short = 'U', long, default_value_t = String::from("wss://127.0.0.1"))] - worker_url: String, - - /// worker direct invocation port - #[clap(short = 'P', long, default_value_t = String::from("2000"))] - trusted_worker_port: String, - - #[clap(subcommand)] - command: Commands, -} +use integritee_cli::{commands, Cli}; fn main() { env_logger::init(); let cli = Cli::parse(); - commands::match_command(&cli); + commands::match_command(&cli).unwrap(); } diff --git a/cli/src/trusted_base_cli/commands/balance.rs b/cli/src/trusted_base_cli/commands/balance.rs index 09dfb53250..3b5b9f4f33 100644 --- a/cli/src/trusted_base_cli/commands/balance.rs +++ b/cli/src/trusted_base_cli/commands/balance.rs @@ -15,7 +15,9 @@ */ -use crate::{trusted_cli::TrustedCli, trusted_command_utils::get_balance, Cli}; +use crate::{ + trusted_cli::TrustedCli, trusted_command_utils::get_balance, Cli, CliResult, CliResultOk, +}; #[derive(Parser)] pub struct BalanceCommand { @@ -24,7 +26,9 @@ pub struct BalanceCommand { } impl BalanceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { - println!("{}", get_balance(cli, trusted_args, &self.account).unwrap_or_default()); + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let balance = get_balance(cli, trusted_args, &self.account).unwrap_or_default(); + println!("{}", balance); + Ok(CliResultOk::Balance { balance }) } } diff --git a/cli/src/trusted_base_cli/commands/nonce.rs b/cli/src/trusted_base_cli/commands/nonce.rs index fbdb326117..a152b471fd 100644 --- a/cli/src/trusted_base_cli/commands/nonce.rs +++ b/cli/src/trusted_base_cli/commands/nonce.rs @@ -17,7 +17,7 @@ use crate::{ get_layer_two_nonce, trusted_cli::TrustedCli, trusted_command_utils::get_pair_from_str, - trusted_operation::perform_trusted_operation, Cli, + trusted_operation::perform_trusted_operation, Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{Index, TrustedGetter, TrustedOperation}; @@ -32,8 +32,9 @@ pub struct NonceCommand { } impl NonceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let who = get_pair_from_str(trusted_args, &self.account); println!("{}", get_layer_two_nonce!(who, cli, trusted_args)); + Ok(CliResultOk::None) } } diff --git a/cli/src/trusted_base_cli/commands/set_balance.rs b/cli/src/trusted_base_cli/commands/set_balance.rs index 5a303523e4..d2ab5fd346 100644 --- a/cli/src/trusted_base_cli/commands/set_balance.rs +++ b/cli/src/trusted_base_cli/commands/set_balance.rs @@ -20,7 +20,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::{get_identifiers, get_pair_from_str}, trusted_operation::perform_trusted_operation, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{Index, TrustedCall, TrustedGetter, TrustedOperation}; @@ -40,7 +40,7 @@ pub struct SetBalanceCommand { } impl SetBalanceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let who = get_pair_from_str(trusted_args, &self.account); let signer = get_pair_from_str(trusted_args, "//Alice"); info!("account ss58 is {}", who.public().to_ss58check()); @@ -57,6 +57,6 @@ impl SetBalanceCommand { ) .sign(&KeyPair::Sr25519(Box::new(signer)), nonce, &mrenclave, &shard) .into_trusted_operation(trusted_args.direct); - let _ = perform_trusted_operation(cli, trusted_args, &top); + Ok(perform_trusted_operation(cli, trusted_args, &top).map(|_| CliResultOk::None)?) } } diff --git a/cli/src/trusted_base_cli/commands/transfer.rs b/cli/src/trusted_base_cli/commands/transfer.rs index cd5d31f73f..f141281d86 100644 --- a/cli/src/trusted_base_cli/commands/transfer.rs +++ b/cli/src/trusted_base_cli/commands/transfer.rs @@ -20,7 +20,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, trusted_operation::perform_trusted_operation, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{Index, TrustedCall, TrustedGetter, TrustedOperation}; @@ -43,7 +43,7 @@ pub struct TransferCommand { } impl TransferCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let from = get_pair_from_str(trusted_args, &self.from); let to = get_accountid_from_str(&self.to); info!("from ss58 is {}", from.public().to_ss58check()); @@ -62,7 +62,8 @@ impl TransferCommand { TrustedCall::balance_transfer(from.public().into(), to, self.amount) .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) .into_trusted_operation(trusted_args.direct); - let _ = perform_trusted_operation(cli, trusted_args, &top); + let res = perform_trusted_operation(cli, trusted_args, &top).map(|_| CliResultOk::None)?; info!("trusted call transfer executed"); + Ok(res) } } diff --git a/cli/src/trusted_base_cli/commands/unshield_funds.rs b/cli/src/trusted_base_cli/commands/unshield_funds.rs index 6f179b960a..bff554fadf 100644 --- a/cli/src/trusted_base_cli/commands/unshield_funds.rs +++ b/cli/src/trusted_base_cli/commands/unshield_funds.rs @@ -20,7 +20,7 @@ use crate::{ trusted_cli::TrustedCli, trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, trusted_operation::perform_trusted_operation, - Cli, + Cli, CliResult, CliResultOk, }; use codec::Decode; use ita_stf::{Index, TrustedCall, TrustedGetter, TrustedOperation}; @@ -43,7 +43,7 @@ pub struct UnshieldFundsCommand { } impl UnshieldFundsCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { let from = get_pair_from_str(trusted_args, &self.from); let to = get_accountid_from_str(&self.to); println!("from ss58 is {}", from.public().to_ss58check()); @@ -62,6 +62,6 @@ impl UnshieldFundsCommand { TrustedCall::balance_unshield(from.public().into(), to, self.amount, shard) .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) .into_trusted_operation(trusted_args.direct); - let _ = perform_trusted_operation(cli, trusted_args, &top); + Ok(perform_trusted_operation(cli, trusted_args, &top).map(|_| CliResultOk::None)?) } } diff --git a/cli/src/trusted_base_cli/mod.rs b/cli/src/trusted_base_cli/mod.rs index dc6bd0172e..e32c44177b 100644 --- a/cli/src/trusted_base_cli/mod.rs +++ b/cli/src/trusted_base_cli/mod.rs @@ -22,7 +22,7 @@ use crate::{ }, trusted_cli::TrustedCli, trusted_command_utils::get_keystore_path, - Cli, + Cli, CliResult, CliResultOk, }; use log::*; use sp_application_crypto::{ed25519, sr25519}; @@ -57,7 +57,7 @@ pub enum TrustedBaseCommand { } impl TrustedBaseCommand { - pub fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) { + pub fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) -> CliResult { match self { TrustedBaseCommand::NewAccount => new_account(trusted_cli), TrustedBaseCommand::ListAccounts => list_accounts(trusted_cli), @@ -70,23 +70,34 @@ impl TrustedBaseCommand { } } -fn new_account(trusted_args: &TrustedCli) { +fn new_account(trusted_args: &TrustedCli) -> CliResult { let store = LocalKeystore::open(get_keystore_path(trusted_args), None).unwrap(); let key: sr25519::AppPair = store.generate().unwrap(); drop(store); info!("new account {}", key.public().to_ss58check()); - println!("{}", key.public().to_ss58check()); + let key_str = key.public().to_ss58check(); + println!("{}", key_str); + + Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: Some(vec![key_str]), pubkeys_ed25519: None }) } -fn list_accounts(trusted_args: &TrustedCli) { +fn list_accounts(trusted_args: &TrustedCli) -> CliResult { let store = LocalKeystore::open(get_keystore_path(trusted_args), None).unwrap(); info!("sr25519 keys:"); for pubkey in store.public_keys::().unwrap().into_iter() { println!("{}", pubkey.to_ss58check()); } info!("ed25519 keys:"); - for pubkey in store.public_keys::().unwrap().into_iter() { - println!("{}", pubkey.to_ss58check()); + let pubkeys: Vec = store + .public_keys::() + .unwrap() + .into_iter() + .map(|pubkey| pubkey.to_ss58check()) + .collect(); + for pubkey in &pubkeys { + println!("{}", pubkey); } drop(store); + + Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: None, pubkeys_ed25519: Some(pubkeys) }) } diff --git a/cli/src/trusted_cli.rs b/cli/src/trusted_cli.rs index 09c3cb0952..359fa05350 100644 --- a/cli/src/trusted_cli.rs +++ b/cli/src/trusted_cli.rs @@ -15,7 +15,7 @@ */ -use crate::{benchmark::BenchmarkCommand, Cli}; +use crate::{benchmark::BenchmarkCommand, Cli, CliResult}; #[cfg(feature = "evm")] use crate::evm::EvmCommand; @@ -57,7 +57,7 @@ pub enum TrustedCommand { } impl TrustedCli { - pub(crate) fn run(&self, cli: &Cli) { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { match &self.command { TrustedCommand::BaseTrusted(cmd) => cmd.run(cli, self), TrustedCommand::Benchmark(cmd) => cmd.run(cli, self), diff --git a/cli/src/trusted_command_utils.rs b/cli/src/trusted_command_utils.rs index 8857c07b38..24a074e1df 100644 --- a/cli/src/trusted_command_utils.rs +++ b/cli/src/trusted_command_utils.rs @@ -45,9 +45,11 @@ macro_rules! get_layer_two_nonce { .sign(&KeyPair::Sr25519(Box::new($signer_pair.clone()))) .into(); // final nonce = current system nonce + pending tx count, panic early - let nonce = perform_trusted_operation($cli, $trusted_args, &top) - .and_then(|n| Index::decode(&mut n.as_slice()).ok()) - .unwrap(); + let res = perform_trusted_operation($cli, $trusted_args, &top).unwrap_or_default(); + let nonce = match res { + Some(n) => Index::decode(&mut n.as_slice()).unwrap_or(0), + None => 0, + }; debug!("got system nonce: {:?}", nonce); let pending_tx_count = get_pending_trusted_calls_for($cli, $trusted_args, &$signer_pair.public().into()).len(); @@ -65,7 +67,7 @@ pub(crate) fn get_balance(cli: &Cli, trusted_args: &TrustedCli, arg_who: &str) - let top: TrustedOperation = TrustedGetter::free_balance(who.public().into()) .sign(&KeyPair::Sr25519(Box::new(who))) .into(); - let res = perform_trusted_operation(cli, trusted_args, &top); + let res = perform_trusted_operation(cli, trusted_args, &top).unwrap_or(None); debug!("received result for balance"); decode_balance(res) } diff --git a/cli/src/trusted_operation.rs b/cli/src/trusted_operation.rs index 8f3b8cf6ba..1d4f577528 100644 --- a/cli/src/trusted_operation.rs +++ b/cli/src/trusted_operation.rs @@ -44,12 +44,21 @@ use substrate_api_client::{ compose_extrinsic, GetHeader, SubmitAndWatch, SubscribeEvents, XtStatus, }; use teerex_primitives::Request; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum TrustedOperationError { + #[error("default error: {msg:?}")] + Default { msg: String }, +} + +pub(crate) type TrustedOpResult = StdResult>, TrustedOperationError>; pub(crate) fn perform_trusted_operation( cli: &Cli, trusted_args: &TrustedCli, top: &TrustedOperation, -) -> Option> { +) -> TrustedOpResult { match top { TrustedOperation::indirect_call(_) => send_request(cli, trusted_args, top), TrustedOperation::direct_call(_) => send_direct_request(cli, trusted_args, top), @@ -61,7 +70,7 @@ fn execute_getter_from_cli_args( cli: &Cli, trusted_args: &TrustedCli, getter: &Getter, -) -> Option> { +) -> TrustedOpResult { let shard = read_shard(trusted_args).unwrap(); let direct_api = get_worker_api_direct(cli); get_state(&direct_api, shard, getter) @@ -71,7 +80,7 @@ pub(crate) fn get_state( direct_api: &DirectClient, shard: ShardIdentifier, getter: &Getter, -) -> Option> { +) -> TrustedOpResult { // Compose jsonrpc call. let data = Request { shard, cyphertext: getter.encode() }; let rpc_method = "state_executeGetter".to_owned(); @@ -81,36 +90,37 @@ pub(crate) fn get_state( let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); // Decode RPC response. - let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).ok()?; + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| TrustedOperationError::Default { msg: err.to_string() })?; let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) // Replace with `inspect_err` once it's stable. - .map_err(|e| { - error!("Failed to decode RpcReturnValue: {:?}", e); - e - }) - .ok()?; + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + TrustedOperationError::Default { msg: "RpcReturnValue::from_hex".to_string() } + })?; if rpc_return_value.status == DirectRequestStatus::Error { println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); - return None + return Err(TrustedOperationError::Default { + msg: "[Error] DirectRequestStatus::Error".to_string(), + }) } let maybe_state = Option::decode(&mut rpc_return_value.value.as_slice()) // Replace with `inspect_err` once it's stable. - .map_err(|e| { - error!("Failed to decode return value: {:?}", e); - e - }) - .ok()?; + .map_err(|err| { + error!("Failed to decode return value: {:?}", err); + TrustedOperationError::Default { msg: "Option::decode".to_string() } + })?; - maybe_state + Ok(maybe_state) } fn send_request( cli: &Cli, trusted_args: &TrustedCli, trusted_operation: &TrustedOperation, -) -> Option> { +) -> TrustedOpResult { let mut chain_api = get_chain_api(cli); let encryption_key = get_shielding_key(cli).unwrap(); let call_encrypted = encryption_key.encrypt(&trusted_operation.encode()).unwrap(); @@ -157,11 +167,13 @@ fn send_request( confirmed_block_number, ) { error!("ProcessedParentchainBlock event: {:?}", e); - return None + return Err(TrustedOperationError::Default { + msg: format!("ProcessedParentchainBlock event: {:?}", e), + }) }; if confirmed_block_hash == block_hash { - return Some(confirmed_block_hash.encode()) + return Ok(Some(block_hash.encode())) } } } @@ -207,7 +219,7 @@ fn send_direct_request( cli: &Cli, trusted_args: &TrustedCli, operation_call: &TrustedOperation, -) -> Option> { +) -> TrustedOpResult { let encryption_key = get_shielding_key(cli).unwrap(); let shard = read_shard(trusted_args).unwrap(); let jsonrpc_call: String = get_json_request(shard, operation_call, encryption_key); @@ -234,7 +246,9 @@ fn send_direct_request( println!("[Error] {}", value); } direct_api.close().unwrap(); - return None + return Err(TrustedOperationError::Default { + msg: "[Error] DirectRequestStatus::Error".to_string(), + }) }, DirectRequestStatus::TrustedOperationStatus(status) => { debug!("request status is: {:?}", status); @@ -245,23 +259,25 @@ fn send_direct_request( direct_api.close().unwrap(); } }, - _ => { + DirectRequestStatus::Ok => { debug!("request status is ignored"); direct_api.close().unwrap(); - return None + return Ok(None) }, } if !return_value.do_watch { debug!("do watch is false, closing connection"); direct_api.close().unwrap(); - return None + return Ok(None) } }; }, Err(e) => { error!("failed to receive rpc response: {:?}", e); direct_api.close().unwrap(); - return None + return Err(TrustedOperationError::Default { + msg: "failed to receive rpc response".to_string(), + }) }, }; } @@ -319,7 +335,7 @@ pub(crate) fn wait_until( } } }, - _ => { + DirectRequestStatus::Ok => { debug!("request status is ignored"); return None }, diff --git a/cli/tests/basic_tests.rs b/cli/tests/basic_tests.rs new file mode 100644 index 0000000000..408e0717eb --- /dev/null +++ b/cli/tests/basic_tests.rs @@ -0,0 +1,24 @@ +use clap::Parser; +use integritee_cli::Cli; + +fn init() { + env_logger::try_init(); +} + +#[test] +fn test_version() { + init(); + + let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--version"]); + + assert!(matches!(res, Err(clap::Error { kind: clap::ErrorKind::DisplayVersion, .. }))); +} + +#[test] +fn test_help() { + init(); + + let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--help"]); + + assert!(matches!(res, Err(clap::Error { kind: clap::ErrorKind::DisplayHelp, .. }))); +}