Skip to content

Commit

Permalink
Merge pull request #1507 from multiversx/uerror
Browse files Browse the repository at this point in the history
Result handlers - error handling and "expect"
  • Loading branch information
andrei-marinica authored Mar 26, 2024
2 parents 125a765 + b3f1e9e commit b409f1f
Show file tree
Hide file tree
Showing 30 changed files with 465 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[settings]
proxy-paths = ["tests/pmf_proxy.rs"]
main = "main"

[contracts.main]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ pub trait PanicMessageFeatures {

#[event("before-panic")]
fn before_panic(&self);

#[view]
fn sc_panic(&self) {
sc_panic!("sc_panic! test");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
mod pmf_proxy;
use multiversx_sc::types::{AddressExpr, ScExpr};
use multiversx_sc_scenario::{scenario_model::*, *};

const OWNER: AddressExpr = AddressExpr("owner");
const SC_PMF: ScExpr = ScExpr("pmf");
const CODE_EXPR: &str = "mxsc:output/panic-message-features.mxsc.json";

fn world() -> ScenarioWorld {
let mut blockchain = ScenarioWorld::new();
blockchain.set_current_dir_from_workspace("contracts/examples/adder");

blockchain.register_contract(CODE_EXPR, panic_message_features::ContractBuilder);
blockchain
}

fn setup() -> ScenarioWorld {
let mut world = world();
let code = world.code_expression(CODE_EXPR);

world.set_state_step(
SetStateStep::new()
.put_account(OWNER, Account::new().nonce(1))
.put_account(SC_PMF, Account::new().code(code)),
);

world
}

// TODO: move to basic-features a testing framework tester
#[test]
fn tx_returns_error_test() {
let mut world = setup();

let (status, message) = world
.tx()
.from(OWNER)
.to(SC_PMF)
.typed(pmf_proxy::PanicMessageFeaturesProxy)
.sc_panic()
.returns(ReturnsStatus)
.returns(ReturnsMessage)
.run();

assert_eq!(status, 4);
assert_eq!(message, "sc_panic! test");
}

#[test]
fn query_returns_error_test() {
let mut world = setup();

let (status, message) = world
.query()
.to(SC_PMF)
.typed(pmf_proxy::PanicMessageFeaturesProxy)
.sc_panic()
.returns(ReturnsStatus)
.returns(ReturnsMessage)
.run();

assert_eq!(status, 4);
assert_eq!(message, "sc_panic! test");
}

#[test]
fn tx_expect_error_test() {
let mut world = setup();

world
.tx()
.from(OWNER)
.to(SC_PMF)
.typed(pmf_proxy::PanicMessageFeaturesProxy)
.sc_panic()
.returns(ExpectStatus(4))
.returns(ExpectMessage("sc_panic! test"))
.run();
}

#[test]
fn query_expect_error_test() {
let mut world = setup();

world
.query()
.to(SC_PMF)
.typed(pmf_proxy::PanicMessageFeaturesProxy)
.sc_panic()
.returns(ExpectStatus(4))
.returns(ExpectMessage("sc_panic! test"))
.run();
}
95 changes: 95 additions & 0 deletions contracts/feature-tests/panic-message-features/tests/pmf_proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Code generated by the multiversx-sc proxy generator. DO NOT EDIT.

////////////////////////////////////////////////////
////////////////// AUTO-GENERATED //////////////////
////////////////////////////////////////////////////

#![allow(dead_code)]
#![allow(clippy::all)]

use multiversx_sc::proxy_imports::*;

pub struct PanicMessageFeaturesProxy;

impl<Env, From, To, Gas> TxProxyTrait<Env, From, To, Gas> for PanicMessageFeaturesProxy
where
Env: TxEnv,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
type TxProxyMethods = PanicMessageFeaturesProxyMethods<Env, From, To, Gas>;

fn proxy_methods(self, tx: Tx<Env, From, To, (), Gas, (), ()>) -> Self::TxProxyMethods {
PanicMessageFeaturesProxyMethods { wrapped_tx: tx }
}
}

pub struct PanicMessageFeaturesProxyMethods<Env, From, To, Gas>
where
Env: TxEnv,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
wrapped_tx: Tx<Env, From, To, (), Gas, (), ()>,
}

#[rustfmt::skip]
impl<Env, From, Gas> PanicMessageFeaturesProxyMethods<Env, From, (), Gas>
where
Env: TxEnv,
Env::Api: VMApi,
From: TxFrom<Env>,
Gas: TxGas<Env>,
{
pub fn init(
self,
) -> TxProxyDeploy<Env, From, Gas, ()> {
self.wrapped_tx
.raw_deploy()
.original_result()
}
}

#[rustfmt::skip]
impl<Env, From, To, Gas> PanicMessageFeaturesProxyMethods<Env, From, To, Gas>
where
Env: TxEnv,
Env::Api: VMApi,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
pub fn panic_with_message<
Arg0: CodecInto<u32>,
>(
self,
some_value: Arg0,
) -> TxProxyCall<Env, From, To, Gas, ()> {
self.wrapped_tx
.raw_call()
.function_name("panicWithMessage")
.argument(&some_value)
.original_result()
}

/// Logs do not get recorded in case of panic.
pub fn panic_after_log(
self,
) -> TxProxyCall<Env, From, To, Gas, ()> {
self.wrapped_tx
.raw_call()
.function_name("panicAfterLog")
.original_result()
}

pub fn sc_panic(
self,
) -> TxProxyCall<Env, From, To, Gas, ()> {
self.wrapped_tx
.raw_call()
.function_name("sc_panic")
.original_result()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
////////////////////////////////////////////////////

// Init: 1
// Endpoints: 2
// Endpoints: 3
// Async Callback (empty): 1
// Total number of exported functions: 4
// Total number of exported functions: 5

#![no_std]
#![allow(internal_features)]
Expand All @@ -22,6 +22,7 @@ multiversx_sc_wasm_adapter::endpoints! {
init => init
panicWithMessage => panic_with_message
panicAfterLog => panic_after_log
sc_panic => sc_panic
)
}

Expand Down
5 changes: 5 additions & 0 deletions framework/base/src/types/interaction/expr/address_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ impl AddressExpr {
}
result
}

#[cfg(feature = "alloc")]
pub fn eval_to_expr(&self) -> alloc::string::String {
alloc::format!("{ADDRESS_PREFIX}{}", self.0)
}
}

#[cfg(test)]
Expand Down
5 changes: 5 additions & 0 deletions framework/base/src/types/interaction/expr/sc_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ impl<'a> ScExpr<'a> {
}
result
}

#[cfg(feature = "alloc")]
pub fn eval_to_expr(&self) -> alloc::string::String {
alloc::format!("{SC_PREFIX}{}", self.0)
}
}

#[cfg(test)]
Expand Down
3 changes: 3 additions & 0 deletions framework/base/src/types/interaction/tx_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use crate::{api::CallTypeApi, types::ManagedAddress};
pub trait TxEnv: Sized {
type Api: CallTypeApi;

/// Type built by result handlers that translates into the "expect" section in scenarios.
type RHExpect: Default;

fn resolve_sender_address(&self) -> ManagedAddress<Self::Api>;

fn default_gas(&self) -> u64;
Expand Down
2 changes: 2 additions & 0 deletions framework/base/src/types/interaction/tx_env_sc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ where
{
type Api = Api;

type RHExpect = ();

fn resolve_sender_address(&self) -> ManagedAddress<Api> {
BlockchainWrapper::<Api>::new().get_sc_address()
}
Expand Down
50 changes: 42 additions & 8 deletions framework/base/src/types/interaction/tx_rh_list/tx_rh_list_exec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{proxy_imports::OriginalResultMarker, types::TxEnv};
use crate::{
api::CallTypeApi,
proxy_imports::{ManagedBuffer, OriginalResultMarker},
types::TxEnv,
};

use super::{ConsNoRet, ConsRet, RHList, RHListItem};

Expand All @@ -10,35 +14,57 @@ pub trait RHListItemExec<RawResult, Env, Original>: RHListItem<Env, Original>
where
Env: TxEnv,
{
fn item_process_result(self, raw_result: &RawResult) -> Self::Returns;
}
/// Part of the execution pre-processing, each result handler needs to produce an "expect" field,
/// as defined in the environment.
///
/// The operation is chained, so all result handlers can contribute, hence the `prev` argument,
/// which represents the "expect" field produces by the other result handlers.
///
/// The default behavior is to leave it unchanged.
fn item_tx_expect(&self, prev: Env::RHExpect) -> Env::RHExpect {
prev
}

impl<RawResult, Env, Original> RHListItemExec<RawResult, Env, Original> for ()
where
Env: TxEnv,
{
fn item_process_result(self, _raw_result: &RawResult) -> Self::Returns {}
/// The main functionality of a result handler, it either does some computation internally
/// (e.g. execution of a lambda function), or produces a result, or both.
fn item_process_result(self, raw_result: &RawResult) -> Self::Returns;
}

/// Indicates how result processing will undergo for an ensemble of result handlers.
pub trait RHListExec<RawResult, Env>: RHList<Env>
where
Env: TxEnv,
{
/// Provides the execution pre-processing, in which result handlers collectively produce an "expect" field.
///
/// The operation starts with the default "expect" field, which normally has all fields unspecified, except
/// for the "status", which is by default set to "0". This means that failing transactions will cause a panic
/// unless explicitly stated in one of the result handlers.
fn list_tx_expect(&self) -> Env::RHExpect;

/// Aggregates the executions of all result handlers, as configured for a transaction.
fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns;
}

impl<RawResult, Env> RHListExec<RawResult, Env> for ()
where
Env: TxEnv,
{
fn list_tx_expect(&self) -> Env::RHExpect {
Env::RHExpect::default()
}

fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {}
}

impl<RawResult, Env, O> RHListExec<RawResult, Env> for OriginalResultMarker<O>
where
Env: TxEnv,
{
fn list_tx_expect(&self) -> Env::RHExpect {
Env::RHExpect::default()
}

fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {}
}

Expand All @@ -48,6 +74,10 @@ where
Head: RHListItemExec<RawResult, Env, Tail::OriginalResult>,
Tail: RHListExec<RawResult, Env>,
{
fn list_tx_expect(&self) -> Env::RHExpect {
self.head.item_tx_expect(self.tail.list_tx_expect())
}

fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns {
let head_result = self.head.item_process_result(raw_result);
let tail_result = self.tail.list_process_result(raw_result);
Expand All @@ -61,6 +91,10 @@ where
Head: RHListItemExec<RawResult, Env, Tail::OriginalResult, Returns = ()>,
Tail: RHListExec<RawResult, Env>,
{
fn list_tx_expect(&self) -> Env::RHExpect {
self.head.item_tx_expect(self.tail.list_tx_expect())
}

fn list_process_result(self, raw_result: &RawResult) -> Self::ListReturns {
self.head.item_process_result(raw_result);
self.tail.list_process_result(raw_result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ use std::{fs::File, io::Write};

use super::proxy_naming::{proxy_methods_type_name, proxy_type_name};

const PREFIX_AUTO_GENERATED: &str = "////////////////////////////////////////////////////
const PRELUDE: &str = "// Code generated by the multiversx-sc proxy generator. DO NOT EDIT.
////////////////////////////////////////////////////
////////////////// AUTO-GENERATED //////////////////
////////////////////////////////////////////////////
";
const PRELUDE: &str = "#![allow(clippy::all)]
#![allow(dead_code)]
#![allow(clippy::all)]
use multiversx_sc::proxy_imports::*;";

pub(crate) fn write_header(file: &mut File) {
writeln!(file, "{PREFIX_AUTO_GENERATED}").unwrap();
writeln!(file, r#"{PRELUDE}"#).unwrap();
}

Expand Down
1 change: 1 addition & 0 deletions framework/scenario/src/facade.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod contract_info;
mod debugger_backend;
pub(crate) mod result_handlers;
mod scenario_world;
mod scenario_world_runner;
mod scenario_world_steps;
Expand Down
Loading

0 comments on commit b409f1f

Please sign in to comment.