Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use TxStatus to map revert errors #1142

Merged
merged 6 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 18 additions & 23 deletions packages/fuels-accounts/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,32 +290,27 @@ impl Provider {
Ok(self.client.node_info().await?.into())
}

pub async fn checked_dry_run<T: Transaction>(&self, tx: T) -> Result<Vec<Receipt>> {
pub async fn checked_dry_run<T: Transaction>(&self, tx: T) -> Result<TxStatus> {
let receipts = self.dry_run(tx).await?;
Self::has_script_succeeded(&receipts)?;

Ok(receipts)
Ok(Self::tx_status_from_receipts(receipts))
}

fn has_script_succeeded(receipts: &[Receipt]) -> Result<()> {
receipts
.iter()
.find_map(|receipt| match receipt {
Receipt::ScriptResult { result, .. }
if *result != ScriptExecutionResult::Success =>
{
Some(format!("{result:?}"))
}
_ => None,
})
.map(|error_message| {
Err(Error::RevertTransactionError {
reason: error_message,
revert_id: 0,
receipts: receipts.to_owned(),
})
})
.unwrap_or(Ok(()))
fn tx_status_from_receipts(receipts: Vec<Receipt>) -> TxStatus {
let revert_reason = receipts.iter().find_map(|receipt| match receipt {
Receipt::ScriptResult { result, .. } if *result != ScriptExecutionResult::Success => {
Some(format!("{result:?}"))
}
_ => None,
});

match revert_reason {
Some(reason) => TxStatus::Revert {
receipts,
reason,
id: 0,
},
None => TxStatus::Success { receipts },
}
}

pub async fn dry_run<T: Transaction>(&self, tx: T) -> Result<Vec<Receipt>> {
Expand Down
36 changes: 0 additions & 36 deletions packages/fuels-core/src/codec/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ use std::{
iter::FilterMap,
};

use fuel_abi_types::error_codes::{
FAILED_ASSERT_EQ_SIGNAL, FAILED_ASSERT_SIGNAL, FAILED_REQUIRE_SIGNAL,
FAILED_SEND_MESSAGE_SIGNAL, FAILED_TRANSFER_TO_ADDRESS_SIGNAL,
};
use fuel_tx::{ContractId, Receipt};

use crate::{
Expand Down Expand Up @@ -208,38 +204,6 @@ impl<'a, I: Iterator<Item = &'a Receipt>> ExtractLogIdData for I {
}
}

/// Map the provided `RevertTransactionError` based on the `revert_id`.
/// If applicable, decode the logged types from the receipt.
pub fn map_revert_error(mut err: Error, log_decoder: &LogDecoder) -> Error {
if let Error::RevertTransactionError {
revert_id,
ref receipts,
ref mut reason,
} = err
{
match revert_id {
FAILED_REQUIRE_SIGNAL => {
*reason = log_decoder.decode_last_log(receipts).unwrap_or_else(|err| {
format!("failed to decode log from require revert: {err}")
})
}
FAILED_ASSERT_EQ_SIGNAL => {
*reason = match log_decoder.decode_last_two_logs(receipts) {
Ok((lhs, rhs)) => format!(
"assertion failed: `(left == right)`\n left: `{lhs:?}`\n right: `{rhs:?}`"
),
Err(err) => format!("failed to decode log from assert_eq revert: {err}"),
};
}
FAILED_ASSERT_SIGNAL => *reason = "assertion failed.".into(),
FAILED_SEND_MESSAGE_SIGNAL => *reason = "failed to send message.".into(),
FAILED_TRANSFER_TO_ADDRESS_SIGNAL => *reason = "failed transfer to address.".into(),
_ => {}
}
}
err
}

pub fn log_formatters_lookup(
log_id_log_formatter_pairs: Vec<(u64, LogFormatter)>,
contract_id: ContractId,
Expand Down
36 changes: 12 additions & 24 deletions packages/fuels-programs/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use fuel_tx::{
};
use fuels_accounts::{provider::TransactionCost, Account};
use fuels_core::{
codec::{map_revert_error, ABIEncoder, DecoderConfig, LogDecoder},
codec::{ABIEncoder, DecoderConfig, LogDecoder},
constants::{BASE_ASSET_ID, DEFAULT_CALL_PARAMS_AMOUNT},
traits::{Parameterize, Tokenizable},
types::{
Expand Down Expand Up @@ -579,9 +579,7 @@ where

/// Call a contract's method on the node, in a state-modifying manner.
pub async fn call(mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(false)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(false).await
}

pub async fn submit(mut self) -> Result<SubmitResponse<T, D>> {
Expand Down Expand Up @@ -610,9 +608,7 @@ where
/// blockchain is *not* modified but simulated.
///
pub async fn simulate(&mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(true)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(true).await
}

async fn call_or_simulate(&mut self, simulate: bool) -> Result<FuelCallResponse<D>> {
Expand All @@ -621,15 +617,13 @@ where

self.cached_tx_id = Some(tx.id(provider.chain_id()));

let receipts = if simulate {
let tx_status = if simulate {
provider.checked_dry_run(tx).await?
} else {
let tx_id = provider.send_transaction_and_await_commit(tx).await?;
provider
.tx_status(&tx_id)
.await?
.take_receipts_checked(Some(&self.log_decoder))?
provider.tx_status(&tx_id).await?
};
let receipts = tx_status.take_receipts_checked(Some(&self.log_decoder))?;

self.get_response(receipts)
}
Expand Down Expand Up @@ -867,9 +861,7 @@ impl<T: Account> MultiContractCallHandler<T> {

/// Call contract methods on the node, in a state-modifying manner.
pub async fn call<D: Tokenizable + Debug>(&mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(false)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(false).await
}

pub async fn submit(mut self) -> Result<SubmitResponseMultiple<T>> {
Expand Down Expand Up @@ -900,9 +892,7 @@ impl<T: Account> MultiContractCallHandler<T> {
///
/// [call]: Self::call
pub async fn simulate<D: Tokenizable + Debug>(&mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(true)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(true).await
}

async fn call_or_simulate<D: Tokenizable + Debug>(
Expand All @@ -914,15 +904,13 @@ impl<T: Account> MultiContractCallHandler<T> {

self.cached_tx_id = Some(tx.id(provider.chain_id()));

let receipts = if simulate {
let tx_status = if simulate {
provider.checked_dry_run(tx).await?
} else {
let tx_id = provider.send_transaction_and_await_commit(tx).await?;
provider
.tx_status(&tx_id)
.await?
.take_receipts_checked(Some(&self.log_decoder))?
provider.tx_status(&tx_id).await?
};
let receipts = tx_status.take_receipts_checked(Some(&self.log_decoder))?;

self.get_response(receipts)
}
Expand All @@ -932,7 +920,7 @@ impl<T: Account> MultiContractCallHandler<T> {
let provider = self.account.try_provider()?;
let tx = self.build_tx().await?;

provider.checked_dry_run(tx).await?;
provider.checked_dry_run(tx).await?.check(None)?;

Ok(())
}
Expand Down
18 changes: 6 additions & 12 deletions packages/fuels-programs/src/script_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use fuels_accounts::{
Account,
};
use fuels_core::{
codec::{map_revert_error, DecoderConfig, LogDecoder},
codec::{DecoderConfig, LogDecoder},
constants::BASE_ASSET_ID,
offsets::base_offset_script,
traits::{Parameterize, Tokenizable},
Expand Down Expand Up @@ -240,24 +240,20 @@ where
let tx = self.build_tx().await?;
self.cached_tx_id = Some(tx.id(self.provider.chain_id()));

let receipts = if simulate {
let tx_status = if simulate {
self.provider.checked_dry_run(tx).await?
} else {
let tx_id = self.provider.send_transaction_and_await_commit(tx).await?;
self.provider
.tx_status(&tx_id)
.await?
.take_receipts_checked(Some(&self.log_decoder))?
self.provider.tx_status(&tx_id).await?
};
let receipts = tx_status.take_receipts_checked(Some(&self.log_decoder))?;

self.get_response(receipts)
}

/// Call a script on the node, in a state-modifying manner.
pub async fn call(mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(false)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(false).await
}

pub async fn submit(mut self) -> Result<SubmitResponse<T, D>> {
Expand Down Expand Up @@ -286,9 +282,7 @@ where
///
/// [`call`]: Self::call
pub async fn simulate(&mut self) -> Result<FuelCallResponse<D>> {
self.call_or_simulate(true)
.await
.map_err(|err| map_revert_error(err, &self.log_decoder))
self.call_or_simulate(true).await
}

/// Get a scripts's estimated cost
Expand Down
2 changes: 2 additions & 0 deletions packages/fuels/tests/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ async fn test_provider_launch_and_connect() -> Result<()> {
Ok(())
}

// Ignored until https://github.com/FuelLabs/fuel-core/issues/1384 is resolved
#[tokio::test]
#[ignore]
async fn test_network_error() -> Result<()> {
abigen!(Contract(
name = "MyContract",
Expand Down
Loading