Skip to content

Commit

Permalink
Merge pull request #1158 from multiversx/interactor-eager-response
Browse files Browse the repository at this point in the history
Interactor eager response
  • Loading branch information
andrei-marinica committed Jul 12, 2023
2 parents 5d878bb + e893b0f commit b403baf
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 116 deletions.
6 changes: 3 additions & 3 deletions framework/scenario/src/scenario/model/step/typed_sc_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::multiversx_sc::codec::{CodecFrom, TopEncodeMulti};

use crate::{
scenario::model::{AddressValue, U64Value},
scenario_model::{BigUintValue, BytesValue, TxError, TxExpect, TxResponse},
scenario_model::{BigUintValue, BytesValue, TxResponseStatus, TxExpect, TxResponse},
};

use super::ScCallStep;
Expand All @@ -19,12 +19,12 @@ pub struct TypedScCall<OriginalResult> {
}

impl<OriginalResult> TypedScCall<OriginalResult> {
pub fn result<RequestedResult>(&self) -> Result<RequestedResult, TxError>
pub fn result<RequestedResult>(&self) -> Result<RequestedResult, TxResponseStatus>
where
OriginalResult: TopEncodeMulti,
RequestedResult: CodecFrom<OriginalResult>,
{
let mut raw_result = self.response().raw_result()?;
let mut raw_result = self.response().out.clone();
Ok(
RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler)
.unwrap(),
Expand Down
6 changes: 3 additions & 3 deletions framework/scenario/src/scenario/model/step/typed_sc_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
types::{Address, CodeMetadata},
},
scenario_format::interpret_trait::InterpreterContext,
scenario_model::{TxError, TxResponse},
scenario_model::{TxResponse, TxResponseStatus},
};

use crate::scenario::model::{AddressValue, BigUintValue, TxExpect, U64Value};
Expand All @@ -23,12 +23,12 @@ pub struct TypedScDeploy<OriginalResult> {
}

impl<OriginalResult> TypedScDeploy<OriginalResult> {
pub fn result<RequestedResult>(&self) -> Result<RequestedResult, TxError>
pub fn result<RequestedResult>(&self) -> Result<RequestedResult, TxResponseStatus>
where
OriginalResult: TopEncodeMulti,
RequestedResult: CodecFrom<OriginalResult>,
{
let mut raw_result = self.response().raw_result()?;
let mut raw_result = self.response().out.clone();
Ok(
RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler)
.unwrap(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod tx_expect;
mod tx_interpret_util;
mod tx_query;
mod tx_response;
mod tx_response_status;
mod tx_transfer;
mod tx_validator_reward;

Expand All @@ -19,6 +20,7 @@ pub use tx_deploy::*;
pub use tx_esdt::*;
pub use tx_expect::*;
pub use tx_query::*;
pub use tx_response::*;
pub use tx_response::TxResponse;
pub use tx_response_status::TxResponseStatus;
pub use tx_transfer::*;
pub use tx_validator_reward::*;
211 changes: 106 additions & 105 deletions framework/scenario/src/scenario/model/transaction/tx_response.rs
Original file line number Diff line number Diff line change
@@ -1,105 +1,96 @@
use std::error::Error;
use core::panic;

use crate::{
bech32,
multiversx_sc::types::Address,
scenario_model::{BytesValue, U64Value},
};
use log::info;
use crate::multiversx_sc::types::Address;
use multiversx_chain_vm::tx_mock::TxResult;
use multiversx_sdk::data::transaction::{
ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork,
};

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

const LOG_IDENTIFIER_SC_DEPLOY: &str = "SCDeploy";
const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError";

const SYSTEM_SC_BECH32: &str = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u";

#[derive(Debug)]
pub struct TxError {
pub message: String,
}

impl std::fmt::Display for TxError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "transaction error: {}", self.message)
}
}

impl Error for TxError {}

#[derive(Debug, Default, Clone)]
pub struct TxResponse {
pub out: Vec<BytesValue>,
pub status: U64Value,
pub message: BytesValue,
pub out: Vec<Vec<u8>>,
pub new_deployed_address: Option<Address>,
pub tx_error: TxResponseStatus,
pub logs: Vec<Log>,
pub gas: U64Value,
pub refund: U64Value,
pub gas: u64,
pub refund: u64,
pub api_scrs: Vec<ApiSmartContractResult>,
pub api_logs: Option<ApiLogs>,
}

impl TxResponse {
pub fn new(tx: TransactionOnNetwork) -> Self {
Self {
api_scrs: tx.smart_contract_results.unwrap_or_default(),
api_logs: tx.logs,
pub fn from_tx_result(tx_result: TxResult) -> Self {
TxResponse {
out: tx_result.result_values,
tx_error: TxResponseStatus {
status: tx_result.result_status,
message: tx_result.result_message,
},
..Default::default()
}
}

pub fn raw_result(&self) -> Result<Vec<Vec<u8>>, TxError> {
self.handle_signal_error_event()?;
pub fn from_network_tx(tx: TransactionOnNetwork) -> Self {
let mut response = Self {
api_scrs: tx.smart_contract_results.unwrap_or_default(),
api_logs: tx.logs,
..Default::default()
};

let first_scr = self.api_scrs.get(0);
if first_scr.is_none() {
return Err(TxError {
message: "no smart contract results obtained".to_string(),
});
response.tx_error = response.process_signal_error();
if !response.tx_error.is_success() {
return response;
}

Ok(decode_scr_data_or_panic(first_scr.unwrap().data.as_str()))
response.process()
}

pub fn new_deployed_address(&self) -> Result<Address, TxError> {
self.handle_signal_error_event()?;
self.handle_sc_deploy_event()
fn process(self) -> Self {
self.process_out().process_new_deployed_address()
}

// Returns the token identifier of the newly issued non-fungible token.
pub fn issue_non_fungible_new_token_identifier(&self) -> Result<String, TxError> {
self.handle_signal_error_event()?;

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() {
return Err(TxError {
message: "no token identifier SCR found".to_string(),
});
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();
}
self
}

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() {
return Err(TxError {
message: format!(
"bad issue token SCR data: {}",
token_identifier_issue_scr.data
),
});
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");
}
}

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

// Finds api logs matching the given log identifier.
pub fn find_log(&self, log_identifier: &str) -> Option<&Events> {
fn find_log(&self, log_identifier: &str) -> Option<&Events> {
if let Some(logs) = &self.api_logs {
logs.events
.iter()
Expand All @@ -109,57 +100,67 @@ impl TxResponse {
}
}

// Handles a signalError event
pub fn handle_signal_error_event(&self) -> Result<(), TxError> {
if let Some(event) = self.find_log(LOG_IDENTIFIER_SIGNAL_ERROR) {
let topics = self.handle_event_topics(event, LOG_IDENTIFIER_SIGNAL_ERROR)?;
let error_raw = base64::decode(topics.get(1).unwrap()).unwrap();
let error = String::from_utf8(error_raw).unwrap();
fn process_signal_error(&self) -> TxResponseStatus {
let mut tx_error = TxResponseStatus::default();

return Err(TxError { message: error });
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");
}
}
Ok(())

tx_error
}

// Handles a scDeploy event
fn handle_sc_deploy_event(&self) -> Result<Address, TxError> {
let event = self.find_log(LOG_IDENTIFIER_SC_DEPLOY);
if event.is_none() {
return Err(TxError {
message: format!("`{LOG_IDENTIFIER_SC_DEPLOY}` event not found"),
});
pub fn handle_signal_error_event(&self) -> Result<(), TxResponseStatus> {
if !self.tx_error.is_success() {
Err(self.tx_error.clone())
} else {
Ok(())
}
let topics = self.handle_event_topics(event.unwrap(), LOG_IDENTIFIER_SC_DEPLOY)?;
let address_raw = base64::decode(topics.get(0).unwrap()).unwrap();
let address = Address::from_slice(address_raw.as_slice());
}

info!("new address: {}", bech32::encode(&address));
Ok(address)
pub fn new_deployed_address(&self) -> Result<Address, TxResponseStatus> {
if !self.tx_error.is_success() {
Err(self.tx_error.clone())
} else {
Ok(self.new_deployed_address.clone().unwrap())
}
}

// Handles the topics of an event and returns them.
fn handle_event_topics<'a, 'b: 'a>(
&'a self,
event: &'b Events,
log_identifier: &str,
) -> Result<&Vec<String>, TxError> {
let option = event.topics.as_ref();
if option.is_none() {
return Err(TxError {
message: "missing topics".to_string(),
});
// Returns the token identifier of the newly issued non-fungible token.
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 topics = option.unwrap();
if topics.len() != 2 {
return Err(TxError {
message: format!(
"`{log_identifier}` is expected to have 2 topics, found {}",
topics.len()
),
});
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");
}
Ok(topics)

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[derive(Debug, Default, Clone)]
pub struct TxResponseStatus {
pub status: u64,
pub message: String,
}

impl TxResponseStatus {
pub fn is_success(&self) -> bool {
self.status == 0
}
}

impl std::fmt::Display for TxResponseStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_success() {
write!(f, "transaction successful")
} else {
write!(f, "transaction error: {}", self.message)
}
}
}
2 changes: 1 addition & 1 deletion framework/snippets/src/interactor_multi_sc_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl Interactor {
let results = self.process_txs(txs).await;

for (i, sc_call_step) in buffer.refs.iter_mut().enumerate() {
sc_call_step.set_response(TxResponse::new(results.get(i).unwrap().clone()));
sc_call_step.set_response(TxResponse::from_network_tx(results.get(i).unwrap().clone()));
}

for step in buffer.refs.iter() {
Expand Down
2 changes: 1 addition & 1 deletion framework/snippets/src/interactor_sc_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Interactor {
let tx_hash = self.launch_sc_call(sc_call_step).await;
let tx = self.retrieve_tx_on_network(tx_hash.clone()).await;

sc_call_step.response = Some(TxResponse::new(tx));
sc_call_step.response = Some(TxResponse::from_network_tx(tx));

if let Ok(token_identifier) = sc_call_step
.response()
Expand Down
4 changes: 2 additions & 2 deletions framework/snippets/src/interactor_sc_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ impl Interactor {

let addr = sc_deploy_step.tx.from.clone();
let nonce = tx.nonce;
sc_deploy_step.response = Some(TxResponse::new(tx));
sc_deploy_step.response = Some(TxResponse::from_network_tx(tx));

let deploy_address = sc_deploy_step.response().new_deployed_address().unwrap();
let deploy_address = sc_deploy_step.response().new_deployed_address.clone().unwrap();

let set_state_step = SetStateStep::new().new_address(
addr,
Expand Down

0 comments on commit b403baf

Please sign in to comment.