Skip to content

Commit

Permalink
unified tx syntax - blackbox prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-marinica committed Nov 5, 2023
1 parent 669ff3a commit ae054e1
Show file tree
Hide file tree
Showing 18 changed files with 623 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use adder::*;
use multiversx_sc::storage::mappers::SingleValue;
use multiversx_sc::{
storage::mappers::SingleValue,
types::{AddressExpr, ScExpr},
};
use multiversx_sc_scenario::{api::StaticApi, num_bigint::BigUint, scenario_model::*, *};

const ADDER_PATH_EXPR: &str = "file:output/adder.wasm";
Expand Down Expand Up @@ -41,12 +44,11 @@ fn adder_blackbox_with_values() {
.call(adder_contract.sum())
.expect_value(SingleValue::from(BigUint::from(5u32))),
)
.sc_call(
ScCallStep::new()
.from(owner_address)
.to(&adder_contract)
.call(adder_contract.add(3u32)),
)
.tx(|tx| {
tx.from(AddressExpr("owner"))
.to(ScExpr("adder"))
.call(adder_contract.add(3u32))
})
.check_state_step(
CheckStateStep::new()
.put_account(owner_address, CheckAccount::new())
Expand Down
49 changes: 49 additions & 0 deletions framework/base/src/types/interaction/annotated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::{
api::ManagedTypeApi,
types::{ManagedAddress, ManagedBuffer},
};

pub trait AnnotatedValue<Api, T>
where
Api: ManagedTypeApi,
{
fn annotation(&self) -> ManagedBuffer<Api>;

fn into_value(self) -> T;

fn with_value_ref<F: FnOnce(&T)>(&self, f: F);
}

impl<Api> AnnotatedValue<Api, ManagedAddress<Api>> for ManagedAddress<Api>
where
Api: ManagedTypeApi,
{
fn annotation(&self) -> ManagedBuffer<Api> {
self.hex_expr()
}

fn into_value(self) -> ManagedAddress<Api> {
self
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Api>)>(&self, f: F) {
f(self)
}
}

impl<Api> AnnotatedValue<Api, ManagedAddress<Api>> for &ManagedAddress<Api>
where
Api: ManagedTypeApi,
{
fn annotation(&self) -> crate::types::ManagedBuffer<Api> {
self.hex_expr()
}

fn into_value(self) -> ManagedAddress<Api> {
self.clone()
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Api>)>(&self, f: F) {
f(self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ where
self.function_call
}

pub fn tx(self) -> Tx<SA, (), (), (), (), FunctionCall<SA>> {
pub fn tx(self) -> Tx<SA, (), (), (), (), (), FunctionCall<SA>> {
Tx::new().call(self.function_call)
}
}
85 changes: 85 additions & 0 deletions framework/base/src/types/interaction/expr_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use core::ptr;

use crate::{
api::CallTypeApi,
types::{ManagedAddress, ManagedBuffer},
};

use super::{AnnotatedValue, TxFrom, TxFromSpecified};

const ADDRESS_PREFIX: &str = "address:";

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AddressExpr(pub &'static str);

impl<Api> AnnotatedValue<Api, ManagedAddress<Api>> for AddressExpr
where
Api: CallTypeApi,
{
fn annotation(&self) -> ManagedBuffer<Api> {
let mut result = ManagedBuffer::new_from_bytes(ADDRESS_PREFIX.as_bytes());
result.append_bytes(self.0.as_bytes());
result
}

fn into_value(self) -> ManagedAddress<Api> {
let expr: [u8; 32] = self.eval_to_array();
expr.into()
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Api>)>(&self, f: F) {
let expr: [u8; 32] = self.eval_to_array();
let ma = expr.into();
f(&ma);
}
}
impl<Api> TxFrom<Api> for AddressExpr
where
Api: CallTypeApi,
{
fn resolve_address(&self) -> ManagedAddress<Api> {
let expr: [u8; 32] = self.eval_to_array();
expr.into()
}
}
impl<Api> TxFromSpecified<Api> for AddressExpr where Api: CallTypeApi {}

impl AddressExpr {
pub const fn eval_to_array(&self) -> [u8; 32] {
let result = [b'_'; 32];
let expr_bytes = self.0.as_bytes();
let mut len = expr_bytes.len();
if len > 32 {
len = 32;
}
unsafe {
ptr::copy_nonoverlapping(expr_bytes.as_ptr(), result.as_ptr() as *mut u8, len);
}
result
}
}

#[cfg(test)]
pub mod tests {
use super::*;

fn assert_eq_eval(expr: &'static str, expected: &[u8; 32]) {
assert_eq!(&AddressExpr(expr).eval_to_array(), expected);
}

#[test]
fn test_address_value() {
assert_eq_eval("", b"________________________________");
assert_eq_eval("a", b"a_______________________________");
assert_eq_eval("a\x05", b"a\x05______________________________");
assert_eq_eval("an_address", b"an_address______________________");
assert_eq_eval(
"12345678901234567890123456789012",
b"12345678901234567890123456789012",
);
assert_eq_eval(
"123456789012345678901234567890123",
b"12345678901234567890123456789012",
);
}
}
109 changes: 109 additions & 0 deletions framework/base/src/types/interaction/expr_sc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use core::ptr;

use crate::{
api::CallTypeApi,
types::{ManagedAddress, ManagedBuffer},
};

use super::{AnnotatedValue, TxFrom, TxFromSpecified, TxTo, TxToSpecified};

const SC_PREFIX: &str = "sc:";

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ScExpr<'a>(pub &'a str);

impl<'a, Api> AnnotatedValue<Api, ManagedAddress<Api>> for ScExpr<'a>
where
Api: CallTypeApi,
{
fn annotation(&self) -> ManagedBuffer<Api> {
let mut result = ManagedBuffer::new_from_bytes(SC_PREFIX.as_bytes());
result.append_bytes(self.0.as_bytes());
result
}

fn into_value(self) -> ManagedAddress<Api> {
let expr: [u8; 32] = self.eval_to_array();
expr.into()
}

fn with_value_ref<F: FnOnce(&ManagedAddress<Api>)>(&self, f: F) {
let expr: [u8; 32] = self.eval_to_array();
let ma = expr.into();
f(&ma);
}
}
impl<'a, Api> TxFrom<Api> for ScExpr<'a>
where
Api: CallTypeApi,
{
fn resolve_address(&self) -> ManagedAddress<Api> {
let expr: [u8; 32] = self.eval_to_array();
expr.into()
}
}
impl<'a, Api> TxFromSpecified<Api> for ScExpr<'a> where Api: CallTypeApi {}
impl<'a, Api> TxTo<Api> for ScExpr<'a> where Api: CallTypeApi {}
impl<'a, Api> TxToSpecified<Api> for ScExpr<'a> where Api: CallTypeApi {}

impl<'a> ScExpr<'a> {
pub const fn eval_to_array(&self) -> [u8; 32] {
let result = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00______________________";
let expr_bytes = self.0.as_bytes();
let mut len = expr_bytes.len();
if len > 22 {
len = 22;
}
unsafe {
ptr::copy_nonoverlapping(
expr_bytes.as_ptr(),
result.as_ptr().offset(10) as *mut u8,
len,
);
}
result
}
}

#[cfg(test)]
pub mod tests {
use super::*;

fn assert_eq_eval(expr: &'static str, expected: &[u8; 32]) {
assert_eq!(&ScExpr(expr).eval_to_array(), expected);
}

#[test]
fn test_address_value() {
assert_eq_eval(
"",
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00______________________",
);
assert_eq_eval(
"a",
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a_____________________",
);
assert_eq_eval(
"12345678901234567890120s",
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012",
);
}

// #[test]
// fn test_sc_address() {
// let context = InterpreterContext::default();
// assert_eq!(
// b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00a_____________________".to_vec(),
// interpret_string("sc:a", &context)
// );
// assert_eq!(
// b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(),
// interpret_string("sc:12345678901234567890120s", &context)
// );
// // trims excess
// assert_eq!(
// b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001234567890123456789012".to_vec(),
// interpret_string("sc:12345678901234567890120sx", &context)
// );
// }
}
13 changes: 11 additions & 2 deletions framework/base/src/types/interaction/function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use multiversx_sc_codec::{
use crate::{
abi::{TypeAbi, TypeName},
api::{
ManagedTypeApi, ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME,
CallTypeApi, ManagedTypeApi, ESDT_MULTI_TRANSFER_FUNC_NAME, ESDT_NFT_TRANSFER_FUNC_NAME,
ESDT_TRANSFER_FUNC_NAME,
},
types::{EsdtTokenPayment, ManagedAddress, ManagedBuffer, ManagedVec, MultiValueEncoded},
};

use super::ManagedArgBuffer;
use super::{ContractCallNoPayment, ManagedArgBuffer};

/// Encodes a function call on the blockchain, composed of a function name and its encoded arguments.
///
Expand Down Expand Up @@ -69,6 +69,15 @@ where
}
}

impl<Api, R> From<ContractCallNoPayment<Api, R>> for FunctionCall<Api>
where
Api: CallTypeApi,
{
fn from(ccnp: ContractCallNoPayment<Api, R>) -> Self {
ccnp.function_call
}
}

impl<Api> TopEncodeMulti for FunctionCall<Api>
where
Api: ManagedTypeApi,
Expand Down
4 changes: 4 additions & 0 deletions framework/base/src/types/interaction/managed_arg_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ where
pub fn into_vec_of_buffers(self) -> ManagedVec<M, ManagedBuffer<M>> {
self.data
}

pub fn iter_buffers(&self) -> ManagedVecRefIterator<M, ManagedBuffer<M>> {
ManagedVecRefIterator::new(&self.data)
}
}

impl<M> ManagedArgBuffer<M>
Expand Down
8 changes: 8 additions & 0 deletions framework/base/src/types/interaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod annotated;
mod async_call;
mod async_call_promises;
mod back_transfers;
Expand All @@ -12,15 +13,19 @@ mod contract_call_with_egld;
mod contract_call_with_egld_or_single_esdt;
mod contract_call_with_multi_esdt;
mod contract_deploy;
mod expr_address;
mod expr_sc;
mod function_call;
mod managed_arg_buffer;
mod tx;
mod tx_data;
mod tx_environment;
mod tx_from;
mod tx_gas;
mod tx_payment;
mod tx_to;

pub use annotated::*;
pub use async_call::AsyncCall;
pub use async_call_promises::AsyncCallPromises;
pub use back_transfers::BackTransfers;
Expand All @@ -35,10 +40,13 @@ pub use contract_call_with_egld::ContractCallWithEgld;
pub use contract_call_with_egld_or_single_esdt::ContractCallWithEgldOrSingleEsdt;
pub use contract_call_with_multi_esdt::ContractCallWithMultiEsdt;
pub use contract_deploy::{new_contract_deploy, ContractDeploy};
pub use expr_address::AddressExpr;
pub use expr_sc::ScExpr;
pub use function_call::FunctionCall;
pub use managed_arg_buffer::ManagedArgBuffer;
pub use tx::*;
pub use tx_data::*;
pub use tx_environment::*;
pub use tx_from::*;
pub use tx_gas::*;
pub use tx_payment::*;
Expand Down
Loading

0 comments on commit ae054e1

Please sign in to comment.