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

VM refactor: builtin functions #1149

Merged
merged 4 commits into from
Jul 11, 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
8 changes: 4 additions & 4 deletions framework/scenario/src/facade/scenario_world_whitebox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl ScenarioWorld {
) -> &mut Self
where
ContractObj: ContractBase<Api = DebugApi> + CallableContract + 'static,
F: FnOnce(ContractObj) + 'static,
F: FnOnce(ContractObj),
{
self.whitebox_call_check(whitebox_contract, sc_call_step, f, |tx_result| {
tx_result.assert_ok();
Expand All @@ -71,7 +71,7 @@ impl ScenarioWorld {
) -> &mut Self
where
ContractObj: ContractBase<Api = DebugApi> + CallableContract + 'static,
F: FnOnce(ContractObj) + 'static,
F: FnOnce(ContractObj),
C: FnOnce(TxResult),
{
// the recipient can be deduced from the contract object, it is redundant to provide it in the step
Expand Down Expand Up @@ -102,7 +102,7 @@ impl ScenarioWorld {
) -> &mut Self
where
ContractObj: ContractBase<Api = DebugApi> + CallableContract + 'static,
F: FnOnce(ContractObj) + 'static,
F: FnOnce(ContractObj),
{
self.whitebox_deploy_check(whitebox_contract, sc_deploy_step, f, |tx_result| {
tx_result.assert_ok();
Expand All @@ -118,7 +118,7 @@ impl ScenarioWorld {
) -> &mut Self
where
ContractObj: ContractBase<Api = DebugApi> + CallableContract + 'static,
F: FnOnce(ContractObj) + 'static,
F: FnOnce(ContractObj),
C: FnOnce(TxResult),
{
let contract_obj = (whitebox_contract.contract_obj_builder)();
Expand Down
4 changes: 2 additions & 2 deletions framework/scenario/src/scenario/run_vm/sc_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl ScenarioVMRunner {

pub fn perform_sc_call_lambda<F>(&mut self, sc_call_step: &ScCallStep, f: F) -> TxResult
where
F: FnOnce() + 'static,
F: FnOnce(),
{
let tx_input = tx_input_from_call(sc_call_step);

Expand All @@ -62,7 +62,7 @@ impl ScenarioVMRunner {
f: F,
) -> TxResult
where
F: FnOnce() + 'static,
F: FnOnce(),
{
let tx_result = self.perform_sc_call_lambda(sc_call_step, f);
if let Some(tx_expect) = &sc_call_step.expect {
Expand Down
6 changes: 3 additions & 3 deletions vm/src/tx_execution/blockchain_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::{ops::Deref, rc::Rc};

use multiversx_chain_vm_executor::Executor;

use crate::tx_execution::{init_builtin_functions, BuiltinFunctionMap};
use super::BuiltinFunctionContainer;

pub struct BlockchainVM {
pub builtin_functions: Rc<BuiltinFunctionMap>,
pub builtin_functions: BuiltinFunctionContainer,
pub executor: Box<dyn Executor>,
}

Expand All @@ -15,7 +15,7 @@ pub struct BlockchainVMRef(Rc<BlockchainVM>);
impl BlockchainVM {
pub fn new(executor: Box<dyn Executor>) -> Self {
BlockchainVM {
builtin_functions: Rc::new(init_builtin_functions()),
builtin_functions: BuiltinFunctionContainer,
executor,
}
}
Expand Down
7 changes: 2 additions & 5 deletions vm/src/tx_execution/builtin_function_mocks.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
mod builtin_func_exec;
mod builtin_func_map;
mod builtin_func_role_check_wrapper;
mod builtin_func_container;
mod builtin_func_trait;
mod esdt_nft;
mod general;
mod transfer;
pub mod vm_builtin_function_names;

pub use builtin_func_exec::init_builtin_functions;
pub use builtin_func_map::BuiltinFunctionMap;
pub use builtin_func_container::BuiltinFunctionContainer;
pub use builtin_func_trait::{BuiltinFunction, BuiltinFunctionEsdtTransferInfo};
pub use vm_builtin_function_names as builtin_function_names;
158 changes: 158 additions & 0 deletions vm/src/tx_execution/builtin_function_mocks/builtin_func_container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use super::{
builtin_func_trait::BuiltinFunction,
builtin_function_names::*,
esdt_nft::{
ESDTLocalBurn, ESDTLocalMint, ESDTNftAddQuantity, ESDTNftAddUri, ESDTNftBurn,
ESDTNftCreate, ESDTNftUpdateAttributes,
},
general::{ChangeOwner, ClaimDeveloperRewards, SetUsername, UpgradeContract},
transfer::{ESDTMultiTransfer, ESDTNftTransfer, ESDTTransfer},
BuiltinFunctionEsdtTransferInfo,
};
use crate::{
tx_execution::BlockchainVMRef,
tx_mock::{BlockchainUpdate, TxCache, TxInput, TxResult},
types::EsdtLocalRole,
};

/// Container for builtin function logic.
///
/// Currently has no data, but could conceivably be configurable in the future.
pub struct BuiltinFunctionContainer;

impl BuiltinFunctionContainer {
/// If the call points to a builtin function, it executes it, otherwise calls the `or_else` closure.
///
/// It also checks that the appropriate roles are set, where applicable.
pub fn execute_builtin_function_or_else<F, Else>(
&self,
vm: &BlockchainVMRef,
tx_input: TxInput,
tx_cache: TxCache,
f: F,
or_else: Else,
) -> (TxResult, BlockchainUpdate)
where
F: FnOnce(),
Else: FnOnce(TxInput, TxCache, F) -> (TxResult, BlockchainUpdate),
{
BuiltinFunctionCall::new(vm, tx_input, tx_cache).execute_or_else(f, or_else)
}

/// Provides data on the builtin functions that perform ESDT token transfers.
pub fn extract_token_transfers(&self, tx_input: &TxInput) -> BuiltinFunctionEsdtTransferInfo {
match tx_input.func_name.as_str() {
ESDT_MULTI_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTMultiTransfer, tx_input),
ESDT_NFT_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTNftTransfer, tx_input),
ESDT_TRANSFER_FUNC_NAME => bf_extract_transfers(ESDTTransfer, tx_input),
_ => BuiltinFunctionEsdtTransferInfo::empty(tx_input),
}
}
}

fn bf_extract_transfers<B>(builtin_func: B, tx_input: &TxInput) -> BuiltinFunctionEsdtTransferInfo
where
B: BuiltinFunction,
{
builtin_func.extract_esdt_transfers(tx_input)
}

/// Syntax helper for the big builtin function match in `execute_or_else`.
/// Thanks to it we do not need to write out the arguments for each match arm.
struct BuiltinFunctionCall<'a> {
vm: &'a BlockchainVMRef,
tx_input: TxInput,
tx_cache: TxCache,
}

impl<'a> BuiltinFunctionCall<'a> {
pub fn new(vm: &'a BlockchainVMRef, tx_input: TxInput, tx_cache: TxCache) -> Self {
BuiltinFunctionCall {
vm,
tx_input,
tx_cache,
}
}

pub fn execute_or_else<F, Else>(self, f: F, or_else: Else) -> (TxResult, BlockchainUpdate)
where
F: FnOnce(),
Else: FnOnce(TxInput, TxCache, F) -> (TxResult, BlockchainUpdate),
{
match self.tx_input.func_name.as_str() {
ESDT_LOCAL_MINT_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::Mint, ESDTLocalMint, f)
},
ESDT_LOCAL_BURN_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::Burn, ESDTLocalBurn, f)
},
ESDT_NFT_CREATE_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::NftCreate, ESDTNftCreate, f)
},
ESDT_NFT_BURN_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::NftBurn, ESDTNftBurn, f)
},
ESDT_NFT_ADD_QUANTITY_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::NftAddQuantity, ESDTNftAddQuantity, f)
},
ESDT_NFT_ADD_URI_FUNC_NAME => {
self.check_role_and_execute(EsdtLocalRole::NftAddUri, ESDTNftAddUri, f)
},
ESDT_NFT_UPDATE_ATTRIBUTES_FUNC_NAME => self.check_role_and_execute(
EsdtLocalRole::NftUpdateAttributes,
ESDTNftUpdateAttributes,
f,
),

ESDT_MULTI_TRANSFER_FUNC_NAME => self.execute_bf(ESDTMultiTransfer, f),
ESDT_NFT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTNftTransfer, f),
ESDT_TRANSFER_FUNC_NAME => self.execute_bf(ESDTTransfer, f),
CHANGE_OWNER_BUILTIN_FUNC_NAME => self.execute_bf(ChangeOwner, f),
CLAIM_DEVELOPER_REWARDS_FUNC_NAME => self.execute_bf(ClaimDeveloperRewards, f),
SET_USERNAME_FUNC_NAME => self.execute_bf(SetUsername, f),
UPGRADE_CONTRACT_FUNC_NAME => self.execute_bf(UpgradeContract, f),
MIGRATE_USERNAME_FUNC_NAME => {
panic!("builtin function {MIGRATE_USERNAME_FUNC_NAME} not implemented")
},
_ => or_else(self.tx_input, self.tx_cache, f),
}
}

fn execute_bf<B, F>(self, builtin_func: B, f: F) -> (TxResult, BlockchainUpdate)
where
B: BuiltinFunction,
F: FnOnce(),
{
builtin_func.execute_lambda(self.vm, self.tx_input, self.tx_cache, f)
}

fn check_role_and_execute<B, F>(
self,
role: EsdtLocalRole,
builtin_func: B,
f: F,
) -> (TxResult, BlockchainUpdate)
where
B: BuiltinFunction,
F: FnOnce(),
{
if check_allowed_to_execute(role, &self.tx_input, &self.tx_cache) {
self.execute_bf(builtin_func, f)
} else {
(
TxResult::from_vm_error("action is not allowed"),
BlockchainUpdate::empty(),
)
}
}
}

fn check_allowed_to_execute(role: EsdtLocalRole, tx_input: &TxInput, tx_cache: &TxCache) -> bool {
let token_identifier = tx_input.args[0].clone();
let available_roles = tx_cache.with_account_mut(&tx_input.to, |account| {
account.esdt.get_roles(&token_identifier)
});
available_roles
.iter()
.any(|available_role| available_role.as_slice() == role.name().as_bytes())
}
63 changes: 0 additions & 63 deletions vm/src/tx_execution/builtin_function_mocks/builtin_func_exec.rs

This file was deleted.

44 changes: 0 additions & 44 deletions vm/src/tx_execution/builtin_function_mocks/builtin_func_map.rs

This file was deleted.

Loading
Loading