Skip to content

Commit

Permalink
feat: support all revert signals (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
hal3e authored Feb 9, 2023
1 parent a020519 commit 4cb767e
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 72 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ jobs:
toolchain: ${{ env.RUST_VERSION }}
# selecting a toolchain either by action or manual `rustup` calls should happen
# before the cache plugin, as it uses the current rustc version as its cache key
- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
with:
prefix-key: "v1-rust"

- name: Set git config
run: |
Expand Down
4 changes: 0 additions & 4 deletions packages/fuels-core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,4 @@ pub const BASE_ASSET_ID: AssetId = AssetId::BASE;
pub const BASE_MESSAGE_ID: MessageId = MessageId::zeroed();

pub const DEFAULT_GAS_ESTIMATION_TOLERANCE: f64 = 0.2;
pub const GAS_PRICE_FACTOR: u64 = 1_000_000_000;
pub const MAX_GAS_PER_TX: u64 = 100_000_000;

// Revert return value that indicates missing output variables
pub const FAILED_TRANSFER_TO_ADDRESS_SIGNAL: u64 = 0xffff_ffff_ffff_0001;
14 changes: 14 additions & 0 deletions packages/fuels-programs/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// Revert with this value for a failing call to `require`
pub const FAILED_REQUIRE_SIGNAL: u64 = 0xffff_ffff_ffff_0000;

/// Revert with this value for a failing call to `transfer_to_address`.
pub const FAILED_TRANSFER_TO_ADDRESS_SIGNAL: u64 = 0xffff_ffff_ffff_0001;

/// Revert with this value for a failing call to `send_message`.
pub const FAILED_SEND_MESSAGE_SIGNAL: u64 = 0xffff_ffff_ffff_0002;

/// Revert with this value for a failing call to `assert_eq`.
pub const FAILED_ASSERT_EQ_SIGNAL: u64 = 0xffff_ffff_ffff_0003;

/// Revert with this value for a failing call to `assert`.
pub const FAILED_ASSERT_SIGNAL: u64 = 0xffff_ffff_ffff_0004;
12 changes: 6 additions & 6 deletions packages/fuels-programs/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use fuel_vm::fuel_asm::PanicReason;
use fuels_core::{
abi_decoder::ABIDecoder,
abi_encoder::{ABIEncoder, UnresolvedBytes},
constants::FAILED_TRANSFER_TO_ADDRESS_SIGNAL,
parameters::{CallParameters, StorageConfiguration, TxParameters},
};
use fuels_signers::{
Expand All @@ -33,8 +32,9 @@ use fuels_types::{

use crate::{
call_response::FuelCallResponse,
constants::FAILED_TRANSFER_TO_ADDRESS_SIGNAL,
execution_script::ExecutableFuelCall,
logs::{decode_revert_error, LogDecoder},
logs::{map_revert_error, LogDecoder},
};

/// How many times to attempt to resolve missing tx dependencies.
Expand Down Expand Up @@ -682,7 +682,7 @@ where
pub async fn call(self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(&self, false)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

/// Call a contract's method on the node, in a simulated manner, meaning the state of the
Expand All @@ -693,7 +693,7 @@ where
pub async fn simulate(self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(&self, true)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

/// Simulates a call without needing to resolve the generic for the return type
Expand Down Expand Up @@ -827,7 +827,7 @@ impl MultiContractCallHandler {
pub async fn call<D: Tokenizable + Debug>(&self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(self, false)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

/// Call contract methods on the node, in a simulated manner, meaning the state of the
Expand All @@ -838,7 +838,7 @@ impl MultiContractCallHandler {
pub async fn simulate<D: Tokenizable + Debug>(&self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(self, true)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

async fn call_or_simulate<D: Tokenizable + Debug>(
Expand Down
1 change: 1 addition & 0 deletions packages/fuels-programs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod call_response;
pub mod call_utils;
pub mod constants;
pub mod contract;
pub mod execution_script;
pub mod logs;
Expand Down
50 changes: 22 additions & 28 deletions packages/fuels-programs/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ use fuels_types::{
traits::{Parameterize, Tokenizable},
};

const REQUIRE_ID: u64 = 0xffff_ffff_ffff_0000;
const ASSERT_EQ_ID: u64 = 0xffff_ffff_ffff_0003;
use crate::constants::{
FAILED_ASSERT_EQ_SIGNAL, FAILED_ASSERT_SIGNAL, FAILED_REQUIRE_SIGNAL,
FAILED_SEND_MESSAGE_SIGNAL, FAILED_TRANSFER_TO_ADDRESS_SIGNAL,
};

/// Holds a unique log ID
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -88,39 +90,37 @@ impl<'a, I: Iterator<Item = &'a Receipt>> ExtractLogIdData for I {
}
}

/// Decodes the logged type from the receipt of a `RevertTransactionError` if available
pub fn decode_revert_error(err: Error, log_decoder: &LogDecoder) -> Error {
/// 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,
receipts,
..
} = &err
ref receipts,
ref mut reason,
} = err
{
match *revert_id {
REQUIRE_ID => return decode_require_revert(log_decoder, receipts),
ASSERT_EQ_ID => return decode_assert_eq_revert(log_decoder, receipts),
match revert_id {
FAILED_REQUIRE_SIGNAL => *reason = decode_require_revert(log_decoder, receipts),
FAILED_ASSERT_EQ_SIGNAL => *reason = decode_assert_eq_revert(log_decoder, receipts),
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
}

fn decode_require_revert(log_decoder: &LogDecoder, receipts: &[Receipt]) -> Error {
let reason = log_decoder
fn decode_require_revert(log_decoder: &LogDecoder, receipts: &[Receipt]) -> String {
log_decoder
.get_logs(receipts)
.ok()
.and_then(|logs| logs.last().cloned())
.unwrap_or_else(|| "Failed to decode log from require revert".to_string());

Error::RevertTransactionError {
reason,
revert_id: REQUIRE_ID,
receipts: receipts.to_owned(),
}
.unwrap_or_else(|| "failed to decode log from require revert".to_string())
}

fn decode_assert_eq_revert(log_decoder: &LogDecoder, receipts: &[Receipt]) -> Error {
let reason = log_decoder
fn decode_assert_eq_revert(log_decoder: &LogDecoder, receipts: &[Receipt]) -> String {
log_decoder
.get_logs(receipts)
.ok()
.and_then(|logs| {
Expand All @@ -131,13 +131,7 @@ fn decode_assert_eq_revert(log_decoder: &LogDecoder, receipts: &[Receipt]) -> Er
}
None
})
.unwrap_or_else(|| "Failed to decode logs from assert_eq revert".to_string());

Error::RevertTransactionError {
reason,
revert_id: ASSERT_EQ_ID,
receipts: receipts.to_owned(),
}
.unwrap_or_else(|| "failed to decode logs from assert_eq revert".to_string())
}

pub fn log_type_lookup(
Expand Down
6 changes: 3 additions & 3 deletions packages/fuels-programs/src/script_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
call_utils::{generate_contract_inputs, generate_contract_outputs},
contract::{get_decoded_output, SettableContract},
execution_script::ExecutableFuelCall,
logs::{decode_revert_error, LogDecoder},
logs::{map_revert_error, LogDecoder},
};

#[derive(Debug)]
Expand Down Expand Up @@ -197,7 +197,7 @@ where
pub async fn call(self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(&self, false)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

/// Call a script on the node, in a simulated manner, meaning the state of the
Expand All @@ -208,7 +208,7 @@ where
pub async fn simulate(self) -> Result<FuelCallResponse<D>> {
Self::call_or_simulate(&self, true)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
.map_err(|err| map_revert_error(err, &self.log_decoder))
}

/// Create a [`FuelCallResponse`] from call receipts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "assert_eq"
name = "asserts"

[dependencies]
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,17 @@ impl Eq for TestEnum {
}

abi TestContract {
fn assert_primitive(a: u64, b: u64);
fn assert_eq_primitive(a: u64, b: u64);
fn assert_eq_struct(test_struct: TestStruct, test_struct2: TestStruct);
fn assert_eq_enum(test_enum: TestEnum, test_enum2: TestEnum);
}

impl TestContract for Contract {
fn assert_primitive(a: u64, b: u64) {
assert(a == b);
}

fn assert_eq_primitive(a: u64, b: u64) {
assert_eq(a, b);
}
Expand Down
Loading

0 comments on commit 4cb767e

Please sign in to comment.