Skip to content

Commit

Permalink
Merge pull request #1760 from multiversx/process-status-fixes
Browse files Browse the repository at this point in the history
process status fixes, error and response handling
  • Loading branch information
andrei-marinica authored Sep 3, 2024
2 parents 84e6f05 + c409864 commit b26f964
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 26 deletions.
14 changes: 14 additions & 0 deletions sdk/core/src/data/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ pub struct TransactionStatus {
pub data: Option<TransactionStatusData>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionProcessStatusData {
pub reason: String,
pub status: String,
}

// TransactionProcessStatus holds a transaction's status response from the network obtained through the process-status API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionProcessStatus {
pub error: String,
pub code: String,
pub data: Option<TransactionProcessStatusData>,
}

// ArgCreateTransaction will hold the transaction fields
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArgCreateTransaction {
Expand Down
22 changes: 21 additions & 1 deletion sdk/core/src/gateway/gateway_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use crate::data::{
network_config::NetworkConfig,
transaction::{
ArgCreateTransaction, ResponseTxCost, SendTransactionResponse, SendTransactionsResponse,
Transaction, TransactionInfo, TransactionOnNetwork, TransactionStatus, TxCostResponseData,
Transaction, TransactionInfo, TransactionOnNetwork, TransactionProcessStatus,
TransactionStatus, TxCostResponseData,
},
vm::{ResponseVmValue, VmValueRequest, VmValuesResponseData},
};
Expand Down Expand Up @@ -96,6 +97,25 @@ impl GatewayProxy {
}
}

// get_transaction_process_status retrieves a transaction's status from the network using process-status API
pub async fn get_transaction_process_status(&self, hash: &str) -> Result<(String, String)> {
let endpoint = format!("transaction/{hash}/process-status");
let endpoint = self.get_endpoint(endpoint.as_str());

let resp = self
.client
.get(endpoint)
.send()
.await?
.json::<TransactionProcessStatus>()
.await?;

match resp.data {
None => Err(anyhow!("{}", resp.error)),
Some(b) => Ok((b.status, b.reason)),
}
}

// get_default_transaction_arguments will prepare the transaction creation argument by querying the account's info
pub async fn get_default_transaction_arguments(
&self,
Expand Down
66 changes: 41 additions & 25 deletions sdk/core/src/gateway/gateway_tx_retrieve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,45 @@ impl GatewayProxy {
let mut retries = 0;
let mut backoff_delay = Duration::from_secs_f32(INITIAL_BACKOFF_DELAY);
let start_time = Instant::now();
let mut issue_found = false;

loop {
match self.get_transaction_status(&tx_hash).await {
Ok(status) => {
match self.get_transaction_process_status(&tx_hash).await {
Ok((status, reason)) => {
// checks if transaction status is final
match status.as_str() {
"success" | "fail" => {
"success" => {
// retrieve transaction info with results
let transaction_info_with_results = self
.get_transaction_info_with_results(&tx_hash)
.await
.unwrap();

if !transaction_info_with_results
.smart_contract_results
.is_empty()
&& !issue_found
{
let first_scr =
&transaction_info_with_results.smart_contract_results[0];

if GatewayProxy::is_issue_tx(&first_scr.data) {
issue_found = true;
tokio::time::sleep(Duration::from_secs(30)).await;
continue;
}
}

info!(
"Transaction retrieved successfully, with status {}: {:#?}",
status, transaction_info_with_results
);
return transaction_info_with_results;
},
"fail" => {
// status failed and no reason means invalid transaction
if reason.is_empty() {
info!("Transaction failed. Invalid transaction: {tx_hash}");
panic!("Transaction failed. Invalid transaction: {tx_hash}");
}

let result = parse_reason(&reason);

match result {
Ok((code, err)) => {
info!("Transaction failed. Code: {code}, message: {err}");
panic!("Transaction failed. Code: {code}, message: {err}")
},
Err(err) => {
info!("Reason parsing error for failed transaction: {err}");
panic!("Reason parsing error for failed transaction: {err}")
},
}
},
_ => {
continue;
},
Expand Down Expand Up @@ -77,12 +81,24 @@ impl GatewayProxy {
);
TransactionOnNetwork::default()
}
}

fn is_issue_tx(data: &str) -> bool {
data.starts_with("issue@")
|| data.starts_with("issueSemiFungible@")
|| data.starts_with("issueNonFungible@")
|| data.starts_with("registerMetaESDT@")
|| data.starts_with("registerAndSetAllRoles@")
pub fn parse_reason(reason: &str) -> Result<(u64, String), String> {
let parts: Vec<&str> = reason.split('@').collect();

if parts.len() < 2 {
return Err("Invalid reason format".to_string());
}

let error_code_hex = parts[1];
let error_message_hex = parts[2];

let error_code =
u64::from_str_radix(error_code_hex, 16).expect("Failed to decode error code as u64");

let error_message =
String::from_utf8(hex::decode(error_message_hex).expect("Failed to decode error message"))
.expect("Failed to decode error message as UTF-8");

Ok((error_code, error_message))
}

0 comments on commit b26f964

Please sign in to comment.