Skip to content

Commit

Permalink
Generate exec parts (#272)
Browse files Browse the repository at this point in the history
* odra reexports odra_macros
* Generate execute_* functions
* generate a module with execute_* functions
* add get_name_arg() function `ContractContext`
* update odra-vm
* update odra-casper
* add try-from-macro crate
* add TryFromRef derive macro that generate TryFrom<&'_ SourceStruct> implementation
* add UnwrapOrRevert
  • Loading branch information
kpob authored Dec 4, 2023
1 parent 9cd1cf3 commit 426076c
Show file tree
Hide file tree
Showing 30 changed files with 530 additions and 224 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"odra-macros",
"odra-casper/wasm-env",
"odra-casper/test-vm",
"try-from-macro"
# needs a refactor
# "odra-casper/livenet"
]
Expand Down
1 change: 1 addition & 0 deletions core/src/contract_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ pub trait ContractContext {
fn emit_event(&self, event: &Bytes);
fn transfer_tokens(&self, to: &Address, amount: &U512);
fn revert(&self, error: OdraError) -> !;
fn get_named_arg_bytes(&self, name: &str) -> Bytes;
}
22 changes: 21 additions & 1 deletion core/src/contract_env.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::call_def::CallDef;
use crate::prelude::*;
use crate::{prelude::*, UnwrapOrRevert};
use crate::{Address, Bytes, CLTyped, FromBytes, OdraError, ToBytes, U512};

use crate::key_maker;
Expand Down Expand Up @@ -28,6 +28,22 @@ impl ContractEnv {
}
}

pub fn get_named_arg<T: FromBytes>(&self, name: &str) -> T {
let bytes = self.backend.borrow().get_named_arg_bytes(name);

let opt_result = match T::from_bytes(&bytes) {
Ok((value, remainder)) => {
if remainder.is_empty() {
Some(value)
} else {
None
}
}
Err(_) => None
};
UnwrapOrRevert::unwrap_or_revert(opt_result, self)
}

pub fn current_key(&self) -> Vec<u8> {
let index_bytes = key_maker::u32_to_hex(self.index);
let mapping_data_bytes = key_maker::bytes_to_hex(&self.mapping_data);
Expand All @@ -53,10 +69,12 @@ impl ContractEnv {
self.backend
.borrow()
.get_value(key)
// TODO: remove unwrap
.map(|bytes| T::from_bytes(&bytes).unwrap().0)
}

pub fn set_value<T: ToBytes + CLTyped>(&self, key: &[u8], value: T) {
// TODO: remove unwrap
let bytes = value.to_bytes().unwrap();
self.backend.borrow().set_value(key, Bytes::from(bytes));
}
Expand All @@ -69,6 +87,7 @@ impl ContractEnv {
pub fn call_contract<T: FromBytes>(&self, address: Address, call: CallDef) -> T {
let backend = self.backend.borrow();
let bytes = backend.call_contract(address, call);
// TODO: remove unwrap
T::from_bytes(&bytes).unwrap().0
}

Expand Down Expand Up @@ -99,6 +118,7 @@ impl ContractEnv {

pub fn emit_event<T: ToBytes>(&self, event: T) {
let backend = self.backend.borrow();
// TODO: remove unwrap
backend.emit_event(&event.to_bytes().unwrap().into())
}
}
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod odra_result;
pub mod prelude;
pub mod uints;
mod unchecked_getter;
mod unwrap_or_revert;
pub mod utils;
pub mod variable;

Expand All @@ -41,6 +42,7 @@ pub use item::OdraItem;
pub use module::ModuleCaller;
pub use odra_result::OdraResult;
pub use unchecked_getter::UncheckedGetter;
pub use unwrap_or_revert::UnwrapOrRevert;
pub use utils::serialize;

pub use casper_types;
Expand Down
33 changes: 33 additions & 0 deletions core/src/unwrap_or_revert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::{ContractEnv, ExecutionError, OdraError};

/// A trait that allows safe unwrapping in the context of a smart contract.
/// On failure the contract does not panic, but reverts calling [`ContractEnv::revert`](crate::ContractEnv::revert()).
/// Works with `Result` and `Option`.
pub trait UnwrapOrRevert<T> {
/// On success, unwraps the value into its inner type,
/// on failure, calls [`ContractEnv::revert`](crate::ContractEnv::revert()) with the passed error.
fn unwrap_or_revert_with<E: Into<OdraError>>(self, env: &ContractEnv, err: E) -> T;
/// On success, unwraps the value into its inner type,
/// on failure, calls [`ContractEnv::revert`](crate::ContractEnv::revert()) with the default error.
fn unwrap_or_revert(self, env: &ContractEnv) -> T;
}

impl<T, E: Into<OdraError>> UnwrapOrRevert<T> for Result<T, E> {
fn unwrap_or_revert_with<F: Into<OdraError>>(self, env: &ContractEnv, err: F) -> T {
self.unwrap_or_else(|_| env.revert(err))
}

fn unwrap_or_revert(self, env: &ContractEnv) -> T {
self.unwrap_or_else(|err| env.revert(err))
}
}

impl<T> UnwrapOrRevert<T> for Option<T> {
fn unwrap_or_revert_with<E: Into<OdraError>>(self, env: &ContractEnv, err: E) -> T {
self.unwrap_or_else(|| env.revert(err))
}

fn unwrap_or_revert(self, env: &ContractEnv) -> T {
self.unwrap_or_else(|| env.revert(ExecutionError::UnwrapError))
}
}
1 change: 0 additions & 1 deletion examples2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

[dependencies]
odra = { path = "../odra" }
odra-macros = { path = "../odra-macros" }

[[bin]]
name = "contract"
Expand Down
4 changes: 2 additions & 2 deletions examples2/src/counter_pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use odra::Mapping;
use odra::Module;
use odra::ModuleWrapper;

#[odra_macros::module]
#[odra::module]
pub struct CounterPack {
env: Rc<ContractEnv>,
counter0: ModuleWrapper<Counter>,
Expand All @@ -22,7 +22,7 @@ pub struct CounterPack {
counters_map: Mapping<u8, Counter>
}

#[odra_macros::module]
#[odra::module]
impl CounterPack {
pub fn get_count(&self, index_a: u8, index_b: u8) -> u32 {
match index_a {
Expand Down
4 changes: 2 additions & 2 deletions examples2/src/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ impl From<Erc20Error> for OdraError {
}
}

#[odra_macros::module]
#[odra::module]
pub struct Erc20 {
env: Rc<ContractEnv>,
total_supply: Variable<U256>,
balances: Mapping<Address, U256>
}

#[odra_macros::module]
#[odra::module]
impl Erc20 {
pub fn init(&mut self, total_supply: Option<U256>) {
if let Some(total_supply) = total_supply {
Expand Down
36 changes: 36 additions & 0 deletions odra-casper/wasm-env/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,42 @@ pub fn revert(error: u16) -> ! {
runtime::revert(ApiError::User(error))
}

pub fn get_named_arg(name: &str) -> Vec<u8> {
let arg_size = get_named_arg_size(name);
if arg_size > 0 {
let data_non_null_ptr = contract_api::alloc_bytes(arg_size);
let ret = unsafe {
ext_ffi::casper_get_named_arg(
name.as_bytes().as_ptr(),
name.len(),
data_non_null_ptr.as_ptr(),
arg_size
)
};
if ret != 0 {
runtime::revert(ApiError::from(ret as u32))
}
unsafe { Vec::from_raw_parts(data_non_null_ptr.as_ptr(), arg_size, arg_size) }
} else {
Vec::new()
}
}

fn get_named_arg_size(name: &str) -> usize {
let mut arg_size: usize = 0;
let ret = unsafe {
ext_ffi::casper_get_named_arg_size(
name.as_bytes().as_ptr(),
name.len(),
&mut arg_size as *mut usize
)
};
match ret {
0 => arg_size,
_ => runtime::revert(ApiError::from(ret as u32))
}
}

pub fn get_block_time() -> u64 {
runtime::get_blocktime().into()
}
Expand Down
4 changes: 4 additions & 0 deletions odra-casper/wasm-env/src/wasm_contract_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ impl ContractContext for WasmContractEnv {
fn revert(&self, error: OdraError) -> ! {
host_functions::revert(error.code())
}

fn get_named_arg_bytes(&self, name: &str) -> Bytes {
host_functions::get_named_arg(name).into()
}
}

impl WasmContractEnv {
Expand Down
2 changes: 2 additions & 0 deletions odra-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ quote = "1.0.33"
syn = { version = "2.0.29", features = ["full"] }
syn_derive = "0.1.8"
convert_case = { version = "0.5.0" }
derive_convert = "0.4.0"
derive-try-from ={ path = "../try-from-macro" }

[dev-dependencies]
pretty_assertions = "1.4.0"
Expand Down
41 changes: 10 additions & 31 deletions odra-macros/src/ast/deployer_item.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use derive_try_from::TryFromRef;

use crate::{ir::ModuleIR, utils};

use super::deployer_utils::{
Expand Down Expand Up @@ -48,11 +50,14 @@ impl TryFrom<&'_ ModuleIR> for DeployImplItem {
}
}

#[derive(syn_derive::ToTokens)]
#[derive(syn_derive::ToTokens, TryFromRef)]
#[source(ModuleIR)]
struct ContractInitFn {
#[expr(utils::syn::visibility_pub())]
vis: syn::Visibility,
sig: DeployerInitSignature,
#[syn(braced)]
#[default]
braces: syn::token::Brace,
#[syn(in = braces)]
caller: EntrypointCallerExpr,
Expand All @@ -62,38 +67,13 @@ struct ContractInitFn {
host_ref_instance: HostRefInstanceExpr
}

impl TryFrom<&'_ ModuleIR> for ContractInitFn {
type Error = syn::Error;

fn try_from(module: &'_ ModuleIR) -> Result<Self, Self::Error> {
Ok(Self {
vis: utils::syn::visibility_pub(),
sig: module.try_into()?,
braces: Default::default(),
caller: module.try_into()?,
new_contract: module.try_into()?,
host_ref_instance: module.try_into()?
})
}
}

#[derive(syn_derive::ToTokens)]
#[derive(syn_derive::ToTokens, TryFromRef)]
#[source(ModuleIR)]
pub struct DeployerItem {
struct_item: DeployStructItem,
impl_item: DeployImplItem
}

impl TryFrom<&'_ ModuleIR> for DeployerItem {
type Error = syn::Error;

fn try_from(module: &'_ ModuleIR) -> Result<Self, Self::Error> {
Ok(Self {
struct_item: module.try_into()?,
impl_item: module.try_into()?
})
}
}

#[cfg(test)]
mod deployer_impl {
use super::DeployerItem;
Expand All @@ -111,12 +91,11 @@ mod deployer_impl {
let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| {
match call_def.method() {
"init" => {
let result = Erc20::new(Rc::new(contract_env))
.init(call_def.get("total_supply").expect("arg not found"));
let result = execute_init(contract_env);
odra::ToBytes::to_bytes(&result).map(Into::into).unwrap()
}
"total_supply" => {
let result = Erc20::new(Rc::new(contract_env)).total_supply();
let result = execute_total_supply(contract_env);
odra::ToBytes::to_bytes(&result).map(Into::into).unwrap()
}
_ => panic!("Unknown method")
Expand Down
44 changes: 15 additions & 29 deletions odra-macros/src/ast/deployer_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{fn_utils, ref_utils};
use crate::{
ir::{FnArgIR, FnIR, ModuleIR},
ir::{FnIR, ModuleIR},
utils
};
use proc_macro2::TokenStream;
Expand Down Expand Up @@ -72,8 +72,8 @@ impl EntrypointCallerExpr {
let mut branches: Vec<CallerBranch> = module
.functions()
.iter()
.map(|f| Ok(CallerBranch::Function(FunctionCallBranch::new(module, f)?)))
.collect::<Result<_, syn::Error>>()?;
.map(|f| CallerBranch::Function(FunctionCallBranch::from(f)))
.collect();
branches.push(CallerBranch::Default(DefaultBranch));

Ok(parse_quote!(
Expand Down Expand Up @@ -178,38 +178,24 @@ struct FunctionCallBranch {
result_expr: syn::Expr
}

impl<'a> FunctionCallBranch {
pub fn new(module: &'a ModuleIR, func: &'a FnIR) -> Result<Self, syn::Error> {
let call_stmt = Self::call_stmt(module, func)?;
let result_expr = utils::expr::parse_bytes(&utils::ident::result());

Ok(Self {
impl From<&'_ FnIR> for FunctionCallBranch {
fn from(func: &'_ FnIR) -> Self {
Self {
function_name: func.name_str(),
arrow_token: Default::default(),
brace_token: Default::default(),
call_stmt,
result_expr
})
}

fn read_call_def_arg_expr(arg: &FnArgIR) -> Result<syn::Expr, syn::Error> {
let call_def_ident = utils::ident::call_def();
let name = arg.name_str()?;
Ok(parse_quote!(#call_def_ident.get(#name).expect("arg not found")))
call_stmt: Self::call_stmt(func),
result_expr: utils::expr::parse_bytes(&utils::ident::result())
}
}
}

fn call_stmt(module: &'a ModuleIR, func: &'a FnIR) -> Result<syn::Stmt, syn::Error> {
let args = func
.named_args()
.iter()
.map(Self::read_call_def_arg_expr)
.collect::<Result<syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>, syn::Error>>(
)?;

impl<'a> FunctionCallBranch {
fn call_stmt(func: &'a FnIR) -> syn::Stmt {
let result_ident = utils::ident::result();
let module_instance = module.module_instance_expr(utils::ident::contract_env())?;
let function_ident = func.name();
Ok(parse_quote!(let #result_ident = #module_instance.#function_ident(#args);))
let function_ident = func.execute_name();
let contract_env_ident = utils::ident::contract_env();
parse_quote!(let #result_ident = #function_ident(#contract_env_ident);)
}
}

Expand Down
Loading

0 comments on commit 426076c

Please sign in to comment.