Skip to content

Commit

Permalink
TxResponse refactor part2
Browse files Browse the repository at this point in the history
  • Loading branch information
ovstinga committed Jul 12, 2023
1 parent b403baf commit 87bc98e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 80 deletions.
2 changes: 2 additions & 0 deletions framework/scenario/src/scenario/model/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod tx_interpret_util;
mod tx_query;
mod tx_response;
mod tx_response_status;
mod tx_response_utils;
mod tx_transfer;
mod tx_validator_reward;

Expand All @@ -22,5 +23,6 @@ pub use tx_expect::*;
pub use tx_query::*;
pub use tx_response::TxResponse;
pub use tx_response_status::TxResponseStatus;
pub use tx_response_utils::*;
pub use tx_transfer::*;
pub use tx_validator_reward::*;
146 changes: 68 additions & 78 deletions framework/scenario/src/scenario/model/transaction/tx_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use multiversx_sdk::data::transaction::{
ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork,
};

use super::{Log, TxResponseStatus};
use super::{decode_scr_data_or_panic, process_topics_error, Log, TxResponseStatus};

const LOG_IDENTIFIER_SC_DEPLOY: &str = "SCDeploy";
const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError";
Expand All @@ -17,6 +17,7 @@ const SYSTEM_SC_BECH32: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqq
pub struct TxResponse {
pub out: Vec<Vec<u8>>,
pub new_deployed_address: Option<Address>,
pub new_issued_token_identifier: Option<String>,
pub tx_error: TxResponseStatus,
pub logs: Vec<Log>,
pub gas: u64,
Expand Down Expand Up @@ -52,81 +53,87 @@ impl TxResponse {
response.process()
}

fn process_signal_error(&self) -> TxResponseStatus {
if let Some(event) = self.find_log(LOG_IDENTIFIER_SIGNAL_ERROR) {
let topics = event.topics.as_ref();
if let Some(error) = process_topics_error(topics) {
return TxResponseStatus::signal_error(&error);
}

let error_raw = base64::decode(topics.unwrap().get(1).unwrap()).unwrap();
let error = String::from_utf8(error_raw).unwrap();
return TxResponseStatus::signal_error(&error);
}

TxResponseStatus::default()
}

// Finds api logs matching the given log identifier.
fn find_log(&self, log_identifier: &str) -> Option<&Events> {
if let Some(logs) = &self.api_logs {
logs.events
.iter()
.find(|event| event.identifier == log_identifier)
} else {
None
}
}

fn process(self) -> Self {
self.process_out().process_new_deployed_address()
self.process_out()
.process_new_deployed_address()
.process_new_issued_token_identifier()
}

fn process_out(mut self) -> Self {
if let Some(first_scr) = self.api_scrs.get(0) {
self.out = decode_scr_data_or_panic(first_scr.data.as_str());
} else {
panic!("no smart contract results obtained")
// self.tx_error.status = 0; // TODO: Add correct status
// self.tx_error.message = "no smart contract results obtained".to_string();
panic!("no smart contract results obtained");
}

self
}

fn process_new_deployed_address(mut self) -> Self {
if let Some(event) = self.find_log(LOG_IDENTIFIER_SC_DEPLOY).cloned() {
// handle topics
if let Some(topics) = event.topics.as_ref() {
if topics.len() != 2 {
self.tx_error.message.push_str(
format!("expected to have 2 topics, found {} instead", topics.len())
.as_str(),
);
}

let address_raw = base64::decode(topics.get(0).unwrap()).unwrap();
let address = Address::from_slice(address_raw.as_slice());
self.new_deployed_address = Some(address);
} else {
self.tx_error.message.push_str("missing topics");
let topics = event.topics.as_ref();
if let Some(error) = process_topics_error(topics) {
panic!("{error}");
}

let address_raw = base64::decode(topics.unwrap().get(0).unwrap()).unwrap();
let address: Address = Address::from_slice(address_raw.as_slice());
self.new_deployed_address = Some(address);
}

self
}

// Finds api logs matching the given log identifier.
fn find_log(&self, log_identifier: &str) -> Option<&Events> {
if let Some(logs) = &self.api_logs {
logs.events
.iter()
.find(|event| event.identifier == log_identifier)
} else {
None
}
}
fn process_new_issued_token_identifier(mut self) -> Self {
let token_identifier_issue_scr: Option<&ApiSmartContractResult> = self
.api_scrs
.iter()
.find(|scr| scr.sender.to_string() == SYSTEM_SC_BECH32 && scr.data.starts_with("@00@"));

fn process_signal_error(&self) -> TxResponseStatus {
let mut tx_error = TxResponseStatus::default();
if token_identifier_issue_scr.is_none() {
return self;
}

if let Some(event) = self.find_log(LOG_IDENTIFIER_SIGNAL_ERROR) {
tx_error.status = 4;
tx_error.message = "signal error: ".to_string();

if let Some(topics) = event.topics.as_ref() {
if topics.len() != 2 {
tx_error.message.push_str(
format!(" expected to have 2 topics, found {} instead", topics.len())
.as_str(),
);
}

let error_raw = base64::decode(topics.get(1).unwrap()).unwrap();
let error = String::from_utf8(error_raw).unwrap();

tx_error.message.push_str(&error);
} else {
tx_error.message.push_str("missing topics");
}
let token_identifier_issue_scr = token_identifier_issue_scr.unwrap();
let encoded_tid = token_identifier_issue_scr.data.split('@').nth(2);
if encoded_tid.is_none() {
panic!("no token identifier found in SCR");
}

tx_error
self.new_issued_token_identifier =
Some(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap());
self
}

#[deprecated(
note = "used for consistency, will be removed soon"
)]
pub fn handle_signal_error_event(&self) -> Result<(), TxResponseStatus> {
if !self.tx_error.is_success() {
Err(self.tx_error.clone())
Expand All @@ -135,6 +142,9 @@ impl TxResponse {
}
}

#[deprecated(
note = "used for consistency, will be removed soon"
)]
pub fn new_deployed_address(&self) -> Result<Address, TxResponseStatus> {
if !self.tx_error.is_success() {
Err(self.tx_error.clone())
Expand All @@ -143,34 +153,14 @@ impl TxResponse {
}
}

// Returns the token identifier of the newly issued non-fungible token.
#[deprecated(
note = "used for consistency, will be removed soon"
)]
pub fn issue_non_fungible_new_token_identifier(&self) -> Result<String, TxResponseStatus> {
let token_identifier_issue_scr: Option<&ApiSmartContractResult> = self
.api_scrs
.iter()
.find(|scr| scr.sender.to_string() == SYSTEM_SC_BECH32 && scr.data.starts_with("@00@"));

if token_identifier_issue_scr.is_none() {
panic!("no token identifier issue SCR found");
}

let token_identifier_issue_scr = token_identifier_issue_scr.unwrap();
let encoded_tid = token_identifier_issue_scr.data.split('@').nth(2);
if encoded_tid.is_none() {
panic!("no token identifier found in SCR");
if !self.tx_error.is_success() {
Err(self.tx_error.clone())
} else {
Ok(self.new_issued_token_identifier.clone().unwrap())
}

Ok(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap())
}
}

fn decode_scr_data_or_panic(data: &str) -> Vec<Vec<u8>> {
let mut split = data.split('@');
let _ = split.next().expect("SCR data should start with '@'");
let result_code = split.next().expect("missing result code");
assert_eq!(result_code, "6f6b", "result code is not 'ok'");

split
.map(|encoded_arg| hex::decode(encoded_arg).expect("error hex-decoding result"))
.collect()
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
#[derive(Debug, Default, Clone)]
pub struct TxResponseStatus {
pub status: u64,
pub(crate) status: u64,
pub message: String,
}

impl TxResponseStatus {
pub(crate) fn new(status: u64, message: &str) -> Self {
Self {
status,
message: message.to_string(),
}
}

pub(crate) fn signal_error(message: &str) -> Self {
Self::new(4, message)
}

pub fn is_success(&self) -> bool {
self.status == 0
}
Expand All @@ -15,7 +26,11 @@ impl std::fmt::Display for TxResponseStatus {
if self.is_success() {
write!(f, "transaction successful")
} else {
write!(f, "transaction error: {}", self.message)
write!(
f,
"transaction failed: (status: {}, message: {})",
self.status, self.message
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
pub fn process_topics_error(topics: Option<&Vec<String>>) -> Option<String> {
if topics.is_none() {
return Some("missing topics".to_string());
}

let topics = topics.unwrap();
return if topics.len() != 2 {

Check warning on line 7 in framework/scenario/src/scenario/model/transaction/tx_response_utils.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] framework/scenario/src/scenario/model/transaction/tx_response_utils.rs#L7

warning: unneeded `return` statement --> framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5 | 7 | / return if topics.len() != 2 { 8 | | Some(format!( 9 | | "expected to have 2 topics, found {} instead", 10 | | topics.len() ... | 13 | | None 14 | | }; | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default = help: remove `return`
Raw output
framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5:w:warning: unneeded `return` statement
  --> framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5
   |
7  | /     return if topics.len() != 2 {
8  | |         Some(format!(
9  | |             "expected to have 2 topics, found {} instead",
10 | |             topics.len()
...  |
13 | |         None
14 | |     };
   | |_____^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
   = note: `#[warn(clippy::needless_return)]` on by default
   = help: remove `return`


__END__

Check warning on line 7 in framework/scenario/src/scenario/model/transaction/tx_response_utils.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] framework/scenario/src/scenario/model/transaction/tx_response_utils.rs#L7

warning: unneeded `return` statement --> framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5 | 7 | / return if topics.len() != 2 { 8 | | Some(format!( 9 | | "expected to have 2 topics, found {} instead", 10 | | topics.len() ... | 13 | | None 14 | | }; | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default = help: remove `return`
Raw output
framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5:w:warning: unneeded `return` statement
  --> framework/scenario/src/scenario/model/transaction/tx_response_utils.rs:7:5
   |
7  | /     return if topics.len() != 2 {
8  | |         Some(format!(
9  | |             "expected to have 2 topics, found {} instead",
10 | |             topics.len()
...  |
13 | |         None
14 | |     };
   | |_____^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
   = note: `#[warn(clippy::needless_return)]` on by default
   = help: remove `return`


__END__
Some(format!(
"expected to have 2 topics, found {} instead",
topics.len()
))
} else {
None
};
}

pub fn decode_scr_data_or_panic(data: &str) -> Vec<Vec<u8>> {
let mut split = data.split('@');
let _ = split.next().expect("SCR data should start with '@'");
let result_code = split.next().expect("missing result code");
assert_eq!(result_code, "6f6b", "result code is not 'ok'");

split
.map(|encoded_arg| hex::decode(encoded_arg).expect("error hex-decoding result"))
.collect()
}

0 comments on commit 87bc98e

Please sign in to comment.