Skip to content

Commit

Permalink
result handlers for errors, v2
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-marinica committed Mar 26, 2024
1 parent d28adf0 commit b3f1e9e
Show file tree
Hide file tree
Showing 22 changed files with 371 additions and 41 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
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
43 changes: 31 additions & 12 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,10 +14,19 @@ pub trait RHListItemExec<RawResult, Env, Original>: RHListItem<Env, Original>
where
Env: TxEnv,
{
fn is_error_handled(&self) -> bool {
false
/// 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
}

/// 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;
}

Expand All @@ -22,17 +35,23 @@ pub trait RHListExec<RawResult, Env>: RHList<Env>
where
Env: TxEnv,
{
fn is_error_handled(&self) -> bool;
/// 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 is_error_handled(&self) -> bool {
false
fn list_tx_expect(&self) -> Env::RHExpect {
Env::RHExpect::default()
}

fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {}
Expand All @@ -42,8 +61,8 @@ impl<RawResult, Env, O> RHListExec<RawResult, Env> for OriginalResultMarker<O>
where
Env: TxEnv,
{
fn is_error_handled(&self) -> bool {
false
fn list_tx_expect(&self) -> Env::RHExpect {
Env::RHExpect::default()
}

fn list_process_result(self, _raw_result: &RawResult) -> Self::ListReturns {}
Expand All @@ -55,8 +74,8 @@ where
Head: RHListItemExec<RawResult, Env, Tail::OriginalResult>,
Tail: RHListExec<RawResult, Env>,
{
fn is_error_handled(&self) -> bool {
self.head.is_error_handled() || self.tail.is_error_handled()
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 {
Expand All @@ -72,8 +91,8 @@ where
Head: RHListItemExec<RawResult, Env, Tail::OriginalResult, Returns = ()>,
Tail: RHListExec<RawResult, Env>,
{
fn is_error_handled(&self) -> bool {
self.head.is_error_handled() || self.tail.is_error_handled()
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 {
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
4 changes: 4 additions & 0 deletions framework/scenario/src/facade/result_handlers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
mod expect_message;
mod expect_status;
mod returns_message;
mod returns_status;
mod with_tx_raw_response;

pub use expect_message::ExpectMessage;
pub use expect_status::ExpectStatus;
pub use returns_message::ReturnsMessage;
pub use returns_status::ReturnsStatus;
pub use with_tx_raw_response::WithRawTxResponse;
Loading

0 comments on commit b3f1e9e

Please sign in to comment.