diff --git a/.github/workflows/odra-casper-ci.yml b/.github/workflows/odra-casper-ci.yml new file mode 100644 index 0000000..84f2b39 --- /dev/null +++ b/.github/workflows/odra-casper-ci.yml @@ -0,0 +1,32 @@ +name: odra-casper-ci + +on: + push: + branches: + - master + - develop + paths-ignore: + - "**.md" + + pull_request: + branches: + - master + - develop + - feature/* + paths-ignore: + - "**.md" + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v1 + - run: make prepare + - run: make build-test-env + - run: make check-lint + - run: make test diff --git a/.gitignore b/.gitignore index 27e2eb4..94b157d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ -# Generated by Cargo -# will have compiled files and executables -/*/target/ -/*/Cargo.lock -# These are backup files generated by rustfmt -**/*.rs.bk -.idea \ No newline at end of file +*/target/ +*/Cargo.lock +test_env/getter_proxy/target +test_env/getter_proxy/Cargo.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6426d8a --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022 odradev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..549de7c --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +prepare: + sudo apt install wabt + rustup target add wasm32-unknown-unknown + +test: + cd shared && cargo test + cd backend && cargo test + +build-test-env: + cd test_env && cargo build --release + +clippy: + cd backend && cargo clippy --all-targets -- -D warnings + cd shared && cargo clippy --all-targets -- -D warnings + cd test_env && cargo clippy --all-targets -- -D warnings + cd test_env/getter_proxy && cargo clippy --all-targets -- -D warnings + +check-lint: clippy + cd backend && cargo fmt -- --check + cd shared && cargo fmt -- --check + cd test_env && cargo fmt -- --check + cd test_env/getter_proxy && cargo fmt -- --check + +lint: clippy + cd backend && cargo fmt + cd shared && cargo fmt + cd test_env && cargo fmt + cd test_env/getter_proxy && cargo fmt + +clean: + cd backend && cargo clean + cd shared && cargo clean + cd test_env && cargo clean + cd test_env/getter_proxy && cargo clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..1653993 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Odra Casper + +Implementation of the Casper backend for the Odra. diff --git a/backend/CHANGELOG.md b/backend/CHANGELOG.md new file mode 100644 index 0000000..88d1674 --- /dev/null +++ b/backend/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +Changelog for `odra-casper-backend`. + +## [0.0.1] - 2022-07-18 +### Added +- `codegen` module that is used to generate wasm file. +- `backend` module that exports all required `#[no_mangle]` functions and interacts with the Casper Host. +- `CHANGELOG.md` and `README.md` files. diff --git a/backend/Cargo.toml b/backend/Cargo.toml index f51bcbc..180887c 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,13 +1,17 @@ [package] -name = "casper_backend" -version = "0.1.0" +name = "odra-casper-backend" +version = "0.0.1" edition = "2021" +authors = ["Jakub Płaskonka ", "Krzysztof Pobiarżyn ", "Maciej Zieliński "] +license = "MIT" +repository = "https://github.com/odradev/odra-casper" +description = "Odra backend bindings and codegen utility for the Casper Blockchain." +keywords = ["wasm", "webassembly", "blockchain"] +categories = ["wasm", "smart contracts"] [dependencies] -odra = { git = "https://github.com/odradev/odra.git", default-features = false, features = [ "wasm" ] } -# odra = { path = "../../../odra/core", default-features = false, features = ["wasm"] } -casper-contract = { version = "1.4.4", default-features = false, features = ["std", "test-support"] } -casper-commons = { path = "../common", features = [ "wasm" ] } +casper-contract = { version = "1.4.4", default-features = false, features = ["std"] } +casper-types = "1.5.0" proc-macro2 = "1.0.39" quote = "1.0.18" syn = "1.0.96" @@ -15,8 +19,5 @@ hex = "0.4.3" convert_case = "0.5.0" pretty_assertions = "1.2.1" lazy_static = "1.4.0" - -[features] -default = [ "codegen", "backend" ] -codegen = [] -backend = [] +odra-casper-shared = { version = "0.0.1", path = "../shared" } +odra = { version = "0.0.1", default-features = false, features = [ "wasm" ] } diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..b08401f --- /dev/null +++ b/backend/README.md @@ -0,0 +1,4 @@ +# Odra Casper Backend + +This crate implements `#[no_mangle]` bindings for `odra::external_api::contract_env::ContractEnv`. +It also provides codegen capabilites used to build a Casper WebAssembly file. diff --git a/backend/src/backend.rs b/backend/src/backend.rs index 259a496..49b4d6f 100644 --- a/backend/src/backend.rs +++ b/backend/src/backend.rs @@ -1,18 +1,12 @@ -mod casper_env; -use lazy_static::lazy_static; - -use std::{ - collections::{hash_map::DefaultHasher, BTreeMap}, - hash::{Hash, Hasher}, - marker::PhantomData, - sync::Mutex, -}; -pub use casper_commons::{odra_address_wrapper::OdraAddressWrapper, address::Address}; pub use casper_contract::{ self, contract_api::{runtime, storage}, }; -use odra::types::{URef, Key, Address as OdraAddress, CLValue, ContractPackageHash, RuntimeArgs, EventData, OdraError}; +pub use casper_types; +use odra::types::{Address as OdraAddress, CLValue, EventData, ExecutionError, RuntimeArgs}; +pub use odra_casper_shared::casper_address::CasperAddress; + +use crate::casper_env; #[no_mangle] pub fn __get_blocktime() -> u64 { @@ -21,51 +15,48 @@ pub fn __get_blocktime() -> u64 { #[no_mangle] pub fn __caller() -> OdraAddress { - casper_env::caller().into() + OdraAddress::try_from(casper_env::caller()).unwrap() } #[no_mangle] -pub fn __set_var(key: &[u8], value: &CLValue) { - let name = std::str::from_utf8(key).unwrap(); - casper_env::set_cl_value(name, value.clone()); +pub fn __self_address() -> OdraAddress { + OdraAddress::try_from(casper_env::self_address()).unwrap() } #[no_mangle] -fn __get_var(key: &[u8]) -> Option { - let name = std::str::from_utf8(key).unwrap(); - casper_env::get_cl_value(name) +pub fn __set_var(key: &str, value: &CLValue) { + casper_env::set_cl_value(key, value.clone()); } #[no_mangle] -fn __set_dict_value(dict: &[u8], key: &[u8], value: &CLValue) { - let dict = std::str::from_utf8(dict).unwrap(); - casper_env::set_dict_value(dict, key, value); +fn __get_var(key: &str) -> Option { + casper_env::get_cl_value(key) } #[no_mangle] -fn __get_dict_value(dict: &[u8], key: &[u8]) -> Option { - let dict = std::str::from_utf8(dict).unwrap(); - casper_env::get_dict_value(dict, key) +fn __set_dict_value(dict: &str, key: &[u8], value: &CLValue) { + casper_env::set_dict_value(dict, key, value); } #[no_mangle] -fn __revert(reason: &OdraError) -> ! { - let code = match reason { - OdraError::ExecutionError(code, _) => *code, - _ => 0 - }; - casper_env::revert(code); +fn __get_dict_value(dict: &str, key: &[u8]) -> Option { + casper_env::get_dict_value(dict, key) } #[no_mangle] -fn __print(message: &str) { - casper_env::print(message); +fn __revert(reason: &ExecutionError) -> ! { + casper_env::revert(reason.code()); } +// #[no_mangle] +// fn __print(message: &str) { +// casper_env::print(message); +// } + #[no_mangle] pub fn __call_contract(address: &OdraAddress, entrypoint: &str, args: &RuntimeArgs) -> Vec { - let address: Address = OdraAddressWrapper::new(*address).into(); - casper_env::call_contract(address, entrypoint, args.clone()) + let casper_address = CasperAddress::try_from(*address).unwrap(); + casper_env::call_contract(casper_address, entrypoint, args.clone()) } #[no_mangle] @@ -73,8 +64,7 @@ fn __emit_event(event: &EventData) { casper_env::emit_event(event); } -// @TODO: rename to -pub fn is_named_arg_exist(name: &str) -> bool { +pub fn named_arg_exists(name: &str) -> bool { let mut arg_size: usize = 0; let ret = unsafe { casper_contract::ext_ffi::casper_get_named_arg_size( @@ -83,5 +73,5 @@ pub fn is_named_arg_exist(name: &str) -> bool { &mut arg_size as *mut usize, ) }; - odra::types::api_error::result_from(ret).is_ok() + casper_types::api_error::result_from(ret).is_ok() } diff --git a/backend/src/backend/casper_env.rs b/backend/src/casper_env.rs similarity index 77% rename from backend/src/backend/casper_env.rs rename to backend/src/casper_env.rs index 70257fe..662f477 100644 --- a/backend/src/backend/casper_env.rs +++ b/backend/src/casper_env.rs @@ -1,20 +1,23 @@ -use std::{collections::{BTreeSet, BTreeMap}, sync::Mutex}; +use lazy_static::lazy_static; +use std::{collections::BTreeMap, sync::Mutex}; -use casper_commons::address::Address; use casper_contract::{ - contract_api::{self, runtime, storage::{self, dictionary_put}}, + contract_api::{ + self, runtime, + storage::{self, dictionary_put}, + }, unwrap_or_revert::UnwrapOrRevert, }; -use lazy_static::lazy_static; -use odra::types::{ +use casper_types::{ api_error, bytesrepr::{Bytes, FromBytes, ToBytes}, - contracts::NamedKeys, system::CallStackElement, - ApiError, CLTyped, CLValue, ContractPackageHash, ContractVersion, EntryPoints, RuntimeArgs, - URef, Key, EventData, + ApiError, CLTyped, CLValue, ContractVersion, Key, RuntimeArgs, URef, }; +use odra::types::EventData; +use odra_casper_shared::casper_address::CasperAddress; + lazy_static! { static ref SEEDS: Mutex> = Mutex::new(BTreeMap::new()); } @@ -22,7 +25,6 @@ lazy_static! { const EVENTS: &str = "__events"; const EVENTS_LENGTH: &str = "__events_length"; - /// Save value to the storage. pub fn set_cl_value(name: &str, value: CLValue) { let bytes: Bytes = value.to_bytes().unwrap_or_revert().into(); @@ -69,7 +71,8 @@ pub fn set_dict_value(seed: &str, key: &[u8], value: &CLValue) { pub fn get_dict_value(seed: &str, key: &[u8]) -> Option { let seed = get_seed(seed); - let bytes: Option = storage::dictionary_get(seed, &to_dictionary_key(key)).unwrap_or_revert(); + let bytes: Option = + storage::dictionary_get(seed, &to_dictionary_key(key)).unwrap_or_revert(); bytes.map(|bytes| { let (result, _rest) = CLValue::from_bytes(&bytes).unwrap_or_revert(); result @@ -80,18 +83,18 @@ pub fn get_dict_value(seed: &str, key: &[u8]) -> Option { /// /// For `Session` and `StoredSession` variants it will return account hash, and for `StoredContract` /// case it will use contract hash as the address. -fn call_stack_element_to_address(call_stack_element: CallStackElement) -> Address { +fn call_stack_element_to_address(call_stack_element: CallStackElement) -> CasperAddress { match call_stack_element { - CallStackElement::Session { account_hash } => Address::from(account_hash), + CallStackElement::Session { account_hash } => CasperAddress::from(account_hash), CallStackElement::StoredSession { account_hash, .. } => { // Stored session code acts in account's context, so if stored session // wants to interact, caller's address will be used. - Address::from(account_hash) + CasperAddress::from(account_hash) } CallStackElement::StoredContract { contract_package_hash, .. - } => Address::from(contract_package_hash), + } => CasperAddress::from(contract_package_hash), } } @@ -106,24 +109,34 @@ fn take_call_stack_elem(n: usize) -> CallStackElement { /// /// This function ensures that only session code can execute this function, and disallows stored /// session/stored contracts. -pub fn caller() -> Address { +pub fn caller() -> CasperAddress { let second_elem = take_call_stack_elem(1); call_stack_element_to_address(second_elem) } /// Gets the address of the currently run contract -pub fn self_address() -> Address { +pub fn self_address() -> CasperAddress { let first_elem = take_call_stack_elem(0); call_stack_element_to_address(first_elem) } /// Record event to the contract's storage. pub fn emit_event(event: &EventData) { - // TODO: Optimalize get_key and set_key - let events_length: u32 = get_key(EVENTS_LENGTH).unwrap_or_default(); + let (events_length, key): (u32, URef) = match runtime::get_key(EVENTS_LENGTH) { + None => { + let key = storage::new_uref(0u32); + runtime::put_key(EVENTS_LENGTH, Key::from(key)); + (0u32, key) + } + Some(value) => { + let key = value.try_into().unwrap_or_revert(); + let value = storage::read(key).unwrap_or_revert().unwrap_or_revert(); + (value, key) + } + }; let events_seed: URef = get_seed(EVENTS); dictionary_put(events_seed, &events_length.to_string(), event.clone()); - set_key(EVENTS_LENGTH, events_length + 1); + storage::write(key, events_length + 1); } /// Convert any key to hash. @@ -133,7 +146,11 @@ pub fn to_dictionary_key(key: &[u8]) -> String { } /// Calls a contract method by Address -pub fn call_contract(address: Address, entry_point: &str, runtime_args: RuntimeArgs) -> Vec { +pub fn call_contract( + address: CasperAddress, + entry_point: &str, + runtime_args: RuntimeArgs, +) -> Vec { let contract_package_hash = address.as_contract_package_hash().unwrap_or_revert(); let contract_version: Option = None; @@ -162,7 +179,7 @@ pub fn call_contract(address: Address, entry_point: &str, runtime_args: RuntimeA unsafe { bytes_written.assume_init() } }; - let serialized_result = if bytes_written == 0 { + if bytes_written == 0 { // If no bytes were written, the host buffer hasn't been set and hence shouldn't be read. vec![] } else { @@ -175,34 +192,7 @@ pub fn call_contract(address: Address, entry_point: &str, runtime_args: RuntimeA read_host_buffer_into(&mut dest).unwrap_or_revert(); dest - }; - serialized_result -} - -pub fn install_contract( - package_hash: &str, - entry_points: EntryPoints, - initializer: impl FnOnce(ContractPackageHash), -) { - // Create a new contract package hash for the contract. - let (contract_package_hash, _) = storage::create_contract_package_at_hash(); - runtime::put_key(package_hash, contract_package_hash.into()); - storage::add_contract_version(contract_package_hash, entry_points, NamedKeys::new()); - - let init_access: URef = - storage::create_contract_user_group(contract_package_hash, "init", 1, Default::default()) - .unwrap_or_revert() - .pop() - .unwrap_or_revert(); - - // Call contrustor method. - initializer(contract_package_hash); - - // Revoke access to init. - let mut urefs = BTreeSet::new(); - urefs.insert(init_access); - storage::remove_contract_user_group_urefs(contract_package_hash, "init", urefs) - .unwrap_or_revert(); + } } pub fn get_block_time() -> u64 { @@ -213,9 +203,9 @@ pub fn revert(error: u16) -> ! { runtime::revert(ApiError::User(error)) } -pub fn print(message: &str) { - runtime::print(message) -} +// pub fn print(message: &str) { +// runtime::print(message) +// } fn to_ptr(t: T) -> (*const u8, usize, Vec) { let bytes = t.into_bytes().unwrap_or_revert(); @@ -242,7 +232,8 @@ fn read_host_buffer_into(dest: &mut [u8]) -> Result { fn get_seed(name: &str) -> URef { let mut seeds = SEEDS.lock().unwrap(); - match seeds.get(name) { + let maybe_seed = seeds.get(name); + match maybe_seed { Some(seed) => *seed, None => { let key: Key = match runtime::get_key(name) { @@ -257,4 +248,4 @@ fn get_seed(name: &str) -> URef { seed } } -} \ No newline at end of file +} diff --git a/backend/src/codegen.rs b/backend/src/codegen.rs index 90d0640..600ccec 100644 --- a/backend/src/codegen.rs +++ b/backend/src/codegen.rs @@ -1,20 +1,19 @@ -use convert_case::Casing; +use self::{ + constructor::WasmConstructor, entrypoints_def::ContractEntrypoints, + wasm_entrypoint::WasmEntrypoint, +}; use odra::contract_def::{ContractDef, EntrypointType}; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote, ToTokens}; -use syn::punctuated::Punctuated; -use syn::{Path, PathSegment, Token}; - -use self::{call::ContractEntrypoints, constructor::WasmConstructor, entrypoints::WasmEntrypoint}; +use syn::{punctuated::Punctuated, Path, PathSegment, Token}; mod arg; -mod call; mod constructor; -mod entrypoints; +mod entrypoints_def; mod ty; +mod wasm_entrypoint; // TODO: Put those functions into trait inside odra, so each backend will implement them - pub fn gen_contract(contract_def: ContractDef, fqn: String) -> TokenStream2 { let entrypoints = generate_entrypoints(&contract_def, fqn.clone()); let call_fn = generate_call(&contract_def, fqn + "Ref"); @@ -22,8 +21,8 @@ pub fn gen_contract(contract_def: ContractDef, fqn: String) -> TokenStream2 { quote! { #![no_main] - use odra::instance::Instance; - use casper_backend; + use odra::Instance; + use odra_casper_backend as casper_backend; #call_fn @@ -36,8 +35,7 @@ fn generate_entrypoints(contract_def: &ContractDef, fqn: String) -> TokenStream2 contract_def .entrypoints .iter() - .map(|ep| WasmEntrypoint(&ep, path).to_token_stream()) - .flatten() + .flat_map(|ep| WasmEntrypoint(ep, path).to_token_stream()) .collect::() } @@ -66,7 +64,7 @@ fn generate_call(contract_def: &ContractDef, ref_fqn: String) -> TokenStream2 { casper_backend::backend::casper_contract::contract_api::storage::add_contract_version( contract_package_hash, entry_points, - odra::types::contracts::NamedKeys::new() + casper_backend::backend::casper_types::contracts::NamedKeys::new() ); #call_constructor @@ -83,9 +81,96 @@ fn fqn_to_path(fqn: String) -> Path { .map(|ident| PathSegment::from(format_ident!("{}", ident))), ); - let path = syn::Path { + syn::Path { leading_colon: None, segments, - }; - path + } +} + +#[cfg(test)] +fn assert_eq_tokens(left: A, right: B) { + let left = left.to_token_stream().to_string(); + let right = right.to_token_stream().to_string(); + pretty_assertions::assert_str_eq!(left, right); +} + +#[cfg(test)] +mod tests { + use odra::contract_def::{Argument, ContractDef, Entrypoint, EntrypointType}; + use odra::types::CLType; + use quote::{quote, ToTokens}; + + use super::constructor::WasmConstructor; + use super::entrypoints_def::ContractEntrypoints; + use super::wasm_entrypoint::WasmEntrypoint; + use super::{assert_eq_tokens, gen_contract}; + + #[test] + fn test_contract_codegen() { + let constructor = Entrypoint { + ident: String::from("construct_me"), + args: vec![Argument { + ident: String::from("value"), + ty: CLType::I32, + }], + ret: CLType::Unit, + ty: EntrypointType::Constructor, + }; + let entrypoint = Entrypoint { + ident: String::from("call_me"), + args: vec![], + ret: CLType::Bool, + ty: EntrypointType::Public, + }; + + let path: syn::Path = syn::parse_str("my_contract::MyContract").unwrap(); + let ref_path: syn::Path = syn::parse_str("my_contract::MyContractRef").unwrap(); + + let fqn = path.to_token_stream().to_string().replace(' ', ""); + + let contract_def = ContractDef { + ident: String::from("MyContract"), + entrypoints: vec![constructor.clone(), entrypoint.clone()], + }; + + let result = gen_contract(contract_def, fqn); + + let expected_constructor_no_mangle = WasmEntrypoint(&constructor, &path); + let expected_entrypoint_no_mangle = WasmEntrypoint(&entrypoint, &path); + let entrypoints = vec![constructor.clone(), entrypoint.clone()]; + let expected_entrypoints = ContractEntrypoints(&entrypoints); + let expected_constructor_if = WasmConstructor(vec![&constructor], &ref_path); + + assert_eq_tokens( + result, + quote! { + #![no_main] + + use odra::Instance; + use odra_casper_backend as casper_backend; + #[no_mangle] + fn call() { + let (contract_package_hash , _) = casper_backend::backend::casper_contract::contract_api::storage::create_contract_package_at_hash(); + casper_backend::backend::casper_contract::contract_api::runtime::put_key( + "my_contract_package_hash", + contract_package_hash.into() + ); + + #expected_entrypoints + + casper_backend::backend::casper_contract::contract_api::storage::add_contract_version( + contract_package_hash, + entry_points, + casper_backend::backend::casper_types::contracts::NamedKeys::new() + ); + + #expected_constructor_if + } + + #expected_constructor_no_mangle + + #expected_entrypoint_no_mangle + }, + ); + } } diff --git a/backend/src/codegen/arg.rs b/backend/src/codegen/arg.rs index 5c4e3d9..157fb6c 100644 --- a/backend/src/codegen/arg.rs +++ b/backend/src/codegen/arg.rs @@ -17,3 +17,39 @@ impl ToTokens for CasperArgs<'_> { }); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::codegen::assert_eq_tokens; + use odra::types::CLType; + + #[test] + fn test_empty_args() { + let args = vec![]; + let args = CasperArgs(&args); + assert_eq_tokens(args, quote!()); + } + + #[test] + fn test_two_args() { + let args = vec![ + Argument { + ident: String::from("a"), + ty: CLType::Bool, + }, + Argument { + ident: String::from("b_c"), + ty: CLType::String, + }, + ]; + let args = CasperArgs(&args); + assert_eq_tokens( + args, + quote!( + let a = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg(stringify!(a)); + let b_c = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg(stringify!(b_c)); + ), + ); + } +} diff --git a/backend/src/codegen/call.rs b/backend/src/codegen/call.rs deleted file mode 100644 index 521536a..0000000 --- a/backend/src/codegen/call.rs +++ /dev/null @@ -1,117 +0,0 @@ -use odra::contract_def::{Argument, Entrypoint, EntrypointType}; -use proc_macro2::TokenStream; -use quote::{format_ident, quote, ToTokens, TokenStreamExt}; - -use super::ty::WrappedType; - -pub(crate) struct ContractEntrypoints<'a>(pub &'a Vec); - -impl ToTokens for ContractEntrypoints<'_> { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(quote!(let mut entry_points = odra::types::EntryPoints::new();)); - tokens.append_all( - self.0 - .iter() - .map(|ep| ContractEntrypoints::build_entry_point(ep)), - ); - } -} - -impl ContractEntrypoints<'_> { - fn build_entry_point(entrypoint: &Entrypoint) -> TokenStream { - let entrypoint_ident = format_ident!("{}", entrypoint.ident); - let params = EntrypointParams(&entrypoint.args); - let ret = WrappedType(&entrypoint.ret); - let access = match &entrypoint.ty { - EntrypointType::Constructor => quote! { - odra::types::EntryPointAccess::Groups(vec![odra::types::Group::new("constructor")]) - }, - EntrypointType::Public => quote! { odra::types::EntryPointAccess::Public }, - }; - quote! { - entry_points.add_entry_point( - odra::types::EntryPoint::new( - stringify!(#entrypoint_ident), - #params, - #ret, - #access, - odra::types::EntryPointType::Contract, - ) - ); - } - } -} - -struct EntrypointParams<'a>(pub &'a Vec); - -impl ToTokens for EntrypointParams<'_> { - fn to_tokens(&self, tokens: &mut TokenStream) { - if self.0.is_empty() { - tokens.extend(quote!(Vec::::new())); - } else { - let params_content = self - .0 - .iter() - .map(|arg| { - let arg_ident = format_ident!("{}", arg.ident); - let ty = WrappedType(&arg.ty); - quote!(params.push(odra::types::Parameter::new(stringify!(#arg_ident), #ty));) - }) - .flatten() - .collect::(); - - let params = quote! { - { - let mut params: Vec = Vec::new(); - #params_content - params - } - }; - - tokens.extend(params); - }; - } -} - -#[cfg(test)] -mod test { - use std::vec; - - use odra::contract_def::{Entrypoint, EntrypointType}; - use pretty_assertions::assert_str_eq; - use quote::ToTokens; - - use super::ContractEntrypoints; - - #[test] - fn parse_cl_type() { - let a = vec![Entrypoint { - ident: "A".to_string(), - args: vec![], - ret: odra::types::CLType::Map { - key: Box::new(odra::types::CLType::Bool), - value: Box::new(odra::types::CLType::U128), - }, - ty: EntrypointType::Public, - }]; - let ep = ContractEntrypoints(&a); - let result = ep.to_token_stream(); - - assert_str_eq!( - result.to_string(), - quote::quote! { - let mut entry_points = odra::types::EntryPoints::new(); - entry_points.add_entry_point( - odra::types::EntryPoint::new( - stringify!(A), - Vec::::new(), - odra::types::CLType::Bool, - odra::types::EntryPointAccess::Public, - odra::types::EntryPointType::Contract, - ) - ); - } - .to_string() - ); - } -} diff --git a/backend/src/codegen/constructor.rs b/backend/src/codegen/constructor.rs index b1269b3..872dffe 100644 --- a/backend/src/codegen/constructor.rs +++ b/backend/src/codegen/constructor.rs @@ -28,22 +28,23 @@ impl ToTokens for WasmConstructor<'_> { let ref_ident = &self.1; let constructor_matching: proc_macro2::TokenStream = data .iter() - .map(|(entrypoint_ident, casper_args, fn_args)| { + .flat_map(|(entrypoint_ident, casper_args, fn_args)| { quote! { stringify!(#entrypoint_ident) => { + let casper_address = casper_backend::backend::CasperAddress::from(contract_package_hash); + let odra_address = odra::types::Address::try_from(casper_address).unwrap_or_revert(); let contract_ref = #ref_ident::at(odra_address); #casper_args contract_ref.#entrypoint_ident( #fn_args ); }, } }) - .flatten() .collect(); tokens.extend(quote! { - if casper_backend::backend::is_named_arg_exist("constructor") { + if casper_backend::backend::named_arg_exists("constructor") { use casper_backend::backend::casper_contract::unwrap_or_revert::UnwrapOrRevert; - let constructor_access: odra::types::URef = + let constructor_access: casper_backend::backend::casper_types::URef = casper_backend::backend::casper_contract::contract_api::storage::create_contract_user_group( contract_package_hash, "constructor", @@ -54,13 +55,8 @@ impl ToTokens for WasmConstructor<'_> { .pop() .unwrap_or_revert(); - let casper_address = casper_backend::backend::Address::from(contract_package_hash); - let odra_address: odra::types::Address = casper_address.into(); - let back = casper_backend::casper_commons::odra_address_wrapper::OdraAddressWrapper::new(odra_address); - let back: casper_backend::backend::Address = back.into(); - let constructor_name = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg::( - "constructor", + "constructor" ); let constructor_name = constructor_name.as_str(); @@ -81,3 +77,63 @@ impl ToTokens for WasmConstructor<'_> { }); } } + +#[cfg(test)] +mod tests { + use odra::contract_def::{Argument, EntrypointType}; + use odra::types::CLType; + + use crate::codegen::assert_eq_tokens; + + use super::*; + + #[test] + fn test_constructor() { + let constructor = Entrypoint { + ident: String::from("construct_me"), + args: vec![Argument { + ident: String::from("value"), + ty: CLType::I32, + }], + ret: CLType::Unit, + ty: EntrypointType::Constructor, + }; + let path: Path = syn::parse2( + quote! { + my_contract::MyContract + } + .to_token_stream(), + ) + .unwrap(); + + let wasm_constructor = WasmConstructor(vec![&constructor], &path); + assert_eq_tokens( + wasm_constructor, + quote! { + if casper_backend::backend::named_arg_exists("constructor") { + use casper_backend::backend::casper_contract::unwrap_or_revert::UnwrapOrRevert; + let constructor_access: casper_backend::backend::casper_types::URef = casper_backend::backend::casper_contract::contract_api::storage::create_contract_user_group( + contract_package_hash , "constructor" , 1 , Default::default() + ).unwrap_or_revert().pop().unwrap_or_revert(); + let constructor_name = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg::("constructor"); + let constructor_name = constructor_name.as_str(); + match constructor_name { + stringify!(construct_me) => { + let casper_address = casper_backend::backend::CasperAddress::from(contract_package_hash); + let odra_address = odra::types::Address::try_from(casper_address).unwrap_or_revert(); + let contract_ref = my_contract::MyContract::at(odra_address); + let value = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg (stringify!(value)); + contract_ref.construct_me(value); + }, + _ => {} + }; + let mut urefs = std::collections::BTreeSet::new(); + urefs.insert(constructor_access); + casper_backend::backend::casper_contract::contract_api::storage::remove_contract_user_group_urefs( + contract_package_hash , "constructor" , urefs + ).unwrap_or_revert(); + } + }, + ); + } +} diff --git a/backend/src/codegen/entrypoints_def.rs b/backend/src/codegen/entrypoints_def.rs new file mode 100644 index 0000000..82678cd --- /dev/null +++ b/backend/src/codegen/entrypoints_def.rs @@ -0,0 +1,113 @@ +use odra::contract_def::{Argument, Entrypoint, EntrypointType}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens, TokenStreamExt}; + +use super::ty::WrappedType; + +pub(crate) struct ContractEntrypoints<'a>(pub &'a Vec); + +impl ToTokens for ContractEntrypoints<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(quote!(let mut entry_points = casper_backend::backend::casper_types::EntryPoints::new();)); + tokens.append_all(self.0.iter().map(ContractEntrypoints::build_entry_point)); + } +} + +impl ContractEntrypoints<'_> { + fn build_entry_point(entrypoint: &Entrypoint) -> TokenStream { + let entrypoint_ident = format_ident!("{}", entrypoint.ident); + let params = EntrypointParams(&entrypoint.args); + let ret = WrappedType(&entrypoint.ret); + let access = match &entrypoint.ty { + EntrypointType::Constructor => quote! { + casper_backend::backend::casper_types::EntryPointAccess::Groups(vec![casper_backend::backend::casper_types::Group::new("constructor")]) + }, + EntrypointType::Public => { + quote! { casper_backend::backend::casper_types::EntryPointAccess::Public } + } + }; + quote! { + entry_points.add_entry_point( + casper_backend::backend::casper_types::EntryPoint::new( + stringify!(#entrypoint_ident), + #params, + #ret, + #access, + casper_backend::backend::casper_types::EntryPointType::Contract, + ) + ); + } + } +} + +struct EntrypointParams<'a>(pub &'a Vec); + +impl ToTokens for EntrypointParams<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.0.is_empty() { + tokens.extend(quote!(Vec::< + casper_backend::backend::casper_types::Parameter, + >::new())); + } else { + let params_content = self + .0 + .iter() + .flat_map(|arg| { + let arg_ident = format_ident!("{}", arg.ident); + let ty = WrappedType(&arg.ty); + quote!(params.push(casper_backend::backend::casper_types::Parameter::new(stringify!(#arg_ident), #ty));) + }) + .collect::(); + + let params = quote! { + { + let mut params: Vec = Vec::new(); + #params_content + params + } + }; + + tokens.extend(params); + }; + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::codegen::assert_eq_tokens; + use odra::types::CLType; + + #[test] + fn parse_cl_type() { + let a = vec![Entrypoint { + ident: String::from("call_me"), + args: vec![Argument { + ident: String::from("value"), + ty: CLType::I32, + }], + ret: CLType::Bool, + ty: EntrypointType::Public, + }]; + let ep = ContractEntrypoints(&a); + assert_eq_tokens( + ep, + quote! { + let mut entry_points = casper_backend::backend::casper_types::EntryPoints::new(); + entry_points.add_entry_point( + casper_backend::backend::casper_types::EntryPoint::new( + stringify!(call_me), + { + let mut params: Vec = Vec::new(); + params.push(casper_backend::backend::casper_types::Parameter::new(stringify!(value), casper_backend::backend::casper_types::CLType::I32)); + params + }, + casper_backend::backend::casper_types::CLType::Bool, + casper_backend::backend::casper_types::EntryPointAccess::Public, + casper_backend::backend::casper_types::EntryPointType::Contract, + ) + ); + }, + ); + } +} diff --git a/backend/src/codegen/ty.rs b/backend/src/codegen/ty.rs index ac7ed9f..e0af23e 100644 --- a/backend/src/codegen/ty.rs +++ b/backend/src/codegen/ty.rs @@ -7,35 +7,37 @@ pub(super) struct WrappedType<'a>(pub &'a CLType); impl ToTokens for WrappedType<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let stream = match &self.0 { - CLType::Bool => quote!(odra::types::CLType::Bool), - CLType::I32 => quote!(odra::types::CLType::I32), - CLType::I64 => quote!(odra::types::CLType::I64), - CLType::U8 => quote!(odra::types::CLType::U8), - CLType::U32 => quote!(odra::types::CLType::U32), - CLType::U64 => quote!(odra::types::CLType::U64), - CLType::U128 => quote!(odra::types::CLType::U128), - CLType::U256 => quote!(odra::types::CLType::U256), - CLType::U512 => quote!(odra::types::CLType::U512), - CLType::Unit => quote!(odra::types::CLType::Unit), - CLType::String => quote!(odra::types::CLType::String), + CLType::Bool => quote!(casper_backend::backend::casper_types::CLType::Bool), + CLType::I32 => quote!(casper_backend::backend::casper_types::CLType::I32), + CLType::I64 => quote!(casper_backend::backend::casper_types::CLType::I64), + CLType::U8 => quote!(casper_backend::backend::casper_types::CLType::U8), + CLType::U32 => quote!(casper_backend::backend::casper_types::CLType::U32), + CLType::U64 => quote!(casper_backend::backend::casper_types::CLType::U64), + CLType::U128 => quote!(casper_backend::backend::casper_types::CLType::U128), + CLType::U256 => quote!(casper_backend::backend::casper_types::CLType::U256), + CLType::U512 => quote!(casper_backend::backend::casper_types::CLType::U512), + CLType::Unit => quote!(casper_backend::backend::casper_types::CLType::Unit), + CLType::String => quote!(casper_backend::backend::casper_types::CLType::String), CLType::Option(ty) => { let value_stream = WrappedType(&**ty).to_token_stream(); - quote!(odra::types::CLType::Option(Box::new(#value_stream))) + quote!(casper_backend::backend::casper_types::CLType::Option(Box::new(#value_stream))) } - CLType::Any => quote!(odra::types::CLType::Any), - CLType::Key => quote!(odra::types::CLType::Key), - CLType::URef => quote!(odra::types::CLType::URef), - CLType::PublicKey => quote!(odra::types::CLType::PublicKey), + CLType::Any => quote!(casper_backend::backend::casper_types::CLType::Any), + CLType::Key => quote!(casper_backend::backend::casper_types::CLType::Key), + CLType::URef => quote!(casper_backend::backend::casper_types::CLType::URef), + CLType::PublicKey => quote!(casper_backend::backend::casper_types::CLType::PublicKey), CLType::List(ty) => { let value_stream = WrappedType(&**ty).to_token_stream(); - quote!(odra::types::CLType::List(Box::new(#value_stream))) + quote!(casper_backend::backend::casper_types::CLType::List(Box::new(#value_stream))) + } + CLType::ByteArray(bytes) => { + quote!(casper_backend::backend::casper_types::CLType::ByteArray(#bytes)) } - CLType::ByteArray(bytes) => quote!(odra::types::CLType::ByteArray(#bytes)), CLType::Result { ok, err } => { let ok_stream = WrappedType(&**ok).to_token_stream(); let err_stream = WrappedType(&**err).to_token_stream(); quote! { - odra::types::CLType::Result { + casper_backend::backend::casper_types::CLType::Result { ok: Box::new(#ok_stream), err: Box::new(#err_stream), } @@ -45,7 +47,7 @@ impl ToTokens for WrappedType<'_> { let key_stream = WrappedType(&**key).to_token_stream(); let value_stream = WrappedType(&**value).to_token_stream(); quote! { - odra::types::CLType::Map { + casper_backend::backend::casper_types::CLType::Map { key: Box::new(#key_stream), value: Box::new(#value_stream), } @@ -55,7 +57,7 @@ impl ToTokens for WrappedType<'_> { let ty = &**ty.get(0).unwrap(); let ty = WrappedType(ty).to_token_stream(); quote! { - odra::types::CLType::Tuple1([#ty]) + casper_backend::backend::casper_types::CLType::Tuple1([#ty]) } } CLType::Tuple2(ty) => { @@ -64,7 +66,7 @@ impl ToTokens for WrappedType<'_> { let t2 = &**ty.get(1).unwrap(); let t2 = WrappedType(t2).to_token_stream(); quote! { - odra::types::CLType::Tuple2([#t1, #t2]) + casper_backend::backend::casper_types::CLType::Tuple2([#t1, #t2]) } } CLType::Tuple3(ty) => { @@ -75,10 +77,44 @@ impl ToTokens for WrappedType<'_> { let t3 = &**ty.get(2).unwrap(); let t3 = WrappedType(t3).to_token_stream(); quote! { - odra::types::CLType::Tuple2([#t1, #t2, #t3]) + casper_backend::backend::casper_types::CLType::Tuple2([#t1, #t2, #t3]) } } }; tokens.extend(stream); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::codegen::assert_eq_tokens; + + #[test] + fn test_simple_type() { + let ty = CLType::Bool; + let wrapped_type = WrappedType(&ty); + assert_eq_tokens( + wrapped_type, + quote!(casper_backend::backend::casper_types::CLType::Bool), + ); + } + + #[test] + fn test_complex_type() { + let ty = CLType::Option(Box::new(CLType::Tuple2([ + Box::new(CLType::Bool), + Box::new(CLType::I32), + ]))); + let wrapped_type = WrappedType(&ty); + assert_eq_tokens( + wrapped_type, + quote!(casper_backend::backend::casper_types::CLType::Option( + Box::new(casper_backend::backend::casper_types::CLType::Tuple2([ + casper_backend::backend::casper_types::CLType::Bool, + casper_backend::backend::casper_types::CLType::I32 + ])) + )), + ); + } +} diff --git a/backend/src/codegen/entrypoints.rs b/backend/src/codegen/wasm_entrypoint.rs similarity index 52% rename from backend/src/codegen/entrypoints.rs rename to backend/src/codegen/wasm_entrypoint.rs index b0a06f5..7822bfd 100644 --- a/backend/src/codegen/entrypoints.rs +++ b/backend/src/codegen/wasm_entrypoint.rs @@ -26,7 +26,7 @@ impl ToTokens for WasmEntrypoint<'_> { use casper_backend::backend::casper_contract::unwrap_or_revert::UnwrapOrRevert; #args let result = contract.#entrypoint_ident(#fn_args); - let result = odra::types::CLValue::from_t(result).unwrap_or_revert(); + let result = casper_backend::backend::casper_types::CLValue::from_t(result).unwrap_or_revert(); casper_backend::backend::casper_contract::contract_api::runtime::ret(result); }, }; @@ -42,3 +42,45 @@ impl ToTokens for WasmEntrypoint<'_> { }); } } + +#[cfg(test)] +mod tests { + use crate::codegen::assert_eq_tokens; + use odra::contract_def::{Argument, EntrypointType}; + use odra::types::CLType; + + use super::*; + + #[test] + fn test_constructor() { + let entrypoint = Entrypoint { + ident: String::from("construct_me"), + args: vec![Argument { + ident: String::from("value"), + ty: CLType::I32, + }], + ret: CLType::Unit, + ty: EntrypointType::Public, + }; + let path: Path = syn::parse2( + quote! { + my_contract::MyContract + } + .to_token_stream(), + ) + .unwrap(); + + let wasm_entrypoint = WasmEntrypoint(&entrypoint, &path); + assert_eq_tokens( + wasm_entrypoint, + quote!( + #[no_mangle] + fn construct_me() { + let contract = my_contract::MyContract::instance("contract"); + let value = casper_backend::backend::casper_contract::contract_api::runtime::get_named_arg(stringify!(value)); + contract.construct_me(value); + } + ), + ); + } +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index e76343d..af631c4 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,8 +1,3 @@ -#[cfg(feature = "backend")] pub mod backend; - -#[cfg(feature = "backend")] -pub use casper_commons; - -#[cfg(feature = "codegen")] +mod casper_env; pub mod codegen; diff --git a/common/Cargo.toml b/common/Cargo.toml deleted file mode 100644 index 20f0519..0000000 --- a/common/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "casper-commons" -version = "0.1.0" -edition = "2021" - -[dependencies] -# odra-types = { path = "../../../odra/types" } -# odra = { path = "../../../odra/core", default-features = false } -casper-types = "1.5.0" -odra-types = { git = "https://github.com/odradev/odra.git" } -odra = { git = "https://github.com/odradev/odra.git" } - -[features] -wasm = [ "odra/wasm" ] -wasm-test = [ "odra/wasm-test" ] \ No newline at end of file diff --git a/common/src/address.rs b/common/src/address.rs deleted file mode 100644 index 890d1e0..0000000 --- a/common/src/address.rs +++ /dev/null @@ -1,109 +0,0 @@ -use odra_types::{ - account::AccountHash, - bytesrepr::{self, FromBytes, ToBytes}, - CLType, CLTyped, ContractPackageHash, Key, Address as OdraAddress -}; - -/// An enum representing an [`AccountHash`] or a [`ContractPackageHash`]. -/// -/// It is taken from [`CasperLabs's ERC20`](https://raw.githubusercontent.com/casper-ecosystem/erc20/master/erc20/src/address.rs). -/// It is copied instead of imported for the flexebility. -#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Debug)] -pub enum Address { - /// Represents an account hash. - Account(AccountHash), - /// Represents a contract package hash. - Contract(ContractPackageHash), -} - -impl Address { - /// Returns the inner account hash if `self` is the `Account` variant. - pub fn as_account_hash(&self) -> Option<&AccountHash> { - if let Self::Account(v) = self { - Some(v) - } else { - None - } - } - - /// Returns the inner contract hash if `self` is the `Contract` variant. - pub fn as_contract_package_hash(&self) -> Option<&ContractPackageHash> { - if let Self::Contract(v) = self { - Some(v) - } else { - None - } - } - - pub fn is_contract(&self) -> bool { - self.as_contract_package_hash().is_some() - } -} - -impl From for Address { - fn from(contract_package_hash: ContractPackageHash) -> Self { - Self::Contract(contract_package_hash) - } -} - -impl From for Address { - fn from(account_hash: AccountHash) -> Self { - Self::Account(account_hash) - } -} - -impl From
for Key { - fn from(address: Address) -> Self { - match address { - Address::Account(account_hash) => Key::Account(account_hash), - Address::Contract(contract_package_hash) => Key::Hash(contract_package_hash.value()), - } - } -} - -impl CLTyped for Address { - fn cl_type() -> CLType { - CLType::Key - } -} - -impl ToBytes for Address { - fn to_bytes(&self) -> Result, bytesrepr::Error> { - Key::from(*self).to_bytes() - } - - fn serialized_length(&self) -> usize { - Key::from(*self).serialized_length() - } -} - -impl FromBytes for Address { - fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> { - let (key, remainder) = Key::from_bytes(bytes)?; - - let address = match key { - Key::Account(account_hash) => Address::Account(account_hash), - Key::Hash(raw_contract_package_hash) => { - let contract_package_hash = ContractPackageHash::new(raw_contract_package_hash); - Address::Contract(contract_package_hash) - } - _ => return Err(bytesrepr::Error::Formatting), - }; - - Ok((address, remainder)) - } -} - -impl Into for Address { - fn into(self) -> OdraAddress { - OdraAddress::new(&self.to_bytes().unwrap()) - } -} - -impl From<&OdraAddress> for Address { - fn from(address: &OdraAddress) -> Self { - let bytes = address.bytes(); - //TODO to add error handling -
::from_bytes(bytes).unwrap().0 - } -} diff --git a/common/src/lib.rs b/common/src/lib.rs deleted file mode 100644 index ca73fd6..0000000 --- a/common/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod address; -pub mod odra_address_wrapper; \ No newline at end of file diff --git a/common/src/odra_address_wrapper.rs b/common/src/odra_address_wrapper.rs deleted file mode 100644 index 8fd4b40..0000000 --- a/common/src/odra_address_wrapper.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::ops::Deref; - -use odra::types::Address as OdraAddress; -use casper_types::{account::AccountHash, ContractPackageHash, bytesrepr::{FromBytes, ToBytes}}; -use crate::address::Address as CasperAddress; - -#[derive(Debug)] -pub struct OdraAddressWrapper(OdraAddress); - -impl OdraAddressWrapper { - pub fn new(address: OdraAddress) -> Self { - Self(address) - } -} - -impl Deref for OdraAddressWrapper { - type Target = OdraAddress; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From for OdraAddressWrapper { - fn from(hash: AccountHash) -> Self { - let casper_address: CasperAddress = hash.into(); - OdraAddressWrapper(casper_address.into()) - } -} - -impl From for OdraAddressWrapper { - fn from(hash: ContractPackageHash) -> Self { - let casper_address: CasperAddress = hash.into(); - casper_address.into() - } -} - -impl From for OdraAddressWrapper { - fn from(address: CasperAddress) -> Self { - let bytes = address.to_bytes().unwrap(); - OdraAddressWrapper(OdraAddress::new(bytes.as_slice())) - } -} - -impl Into for OdraAddressWrapper { - fn into(self) -> CasperAddress { - let vec = self.to_bytes().unwrap(); - CasperAddress::from_vec(vec).unwrap().0 - } -} - -impl Into for OdraAddressWrapper { - fn into(self) -> ContractPackageHash { - let mut bytes_vec = self.bytes().to_vec(); - bytes_vec.resize(casper_types::KEY_HASH_LENGTH, 0); - let mut bytes = [0u8; casper_types::KEY_HASH_LENGTH]; - bytes.copy_from_slice(bytes_vec.as_slice()); - - ContractPackageHash::new(bytes) - } -} - -#[cfg(test)] -mod tests { - use casper_types::ContractPackageHash; - use odra::types::Address as OdraAddress; - use crate::address::Address as CasperAddress; - use super::OdraAddressWrapper; - - #[test] - fn test_address() { - let casper_addr = ContractPackageHash::new([1u8; 32]); - let odra_addr = OdraAddressWrapper::from(casper_addr); - let result: ContractPackageHash = odra_addr.into(); - assert_eq!(result, casper_addr); - } - - #[test] - fn test_casper_address_to_odra_address() { - use odra::types::bytesrepr::ToBytes; - let casper_addr_ph = ContractPackageHash::new([3u8; 32]); - let casper_addr = CasperAddress::from(casper_addr_ph); - let odra_addr: OdraAddress = casper_addr.into(); - let odra_addr = OdraAddressWrapper::new(odra_addr); - let result: CasperAddress = odra_addr.into(); - assert_eq!(result, casper_addr); - assert_eq!(result.as_contract_package_hash().unwrap(), &casper_addr_ph); - } -} - -// Contract(ContractPackageHash(582e0b80ca8dd681697222ae235d480605f62a957f8aaddd42a74613e1521300)) -// Address { data: "582e0b80ca8dd681697222ae235d480605f62a957f8aaddd42a74613e15213000000000000000000000000000000000000000000000000000000000000000000" } diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..2eecf88 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly-2022-06-17 diff --git a/shared/CHANGELOG.md b/shared/CHANGELOG.md new file mode 100644 index 0000000..1a82043 --- /dev/null +++ b/shared/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +Changelog for `odra-casper-shared`. + +## [0.0.1] - 2022-07-18 +### Added +- `casper_address::CasperAddress` struct. +- `CHANGELOG.md` and `README.md` files. diff --git a/shared/Cargo.toml b/shared/Cargo.toml new file mode 100644 index 0000000..3033c49 --- /dev/null +++ b/shared/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "odra-casper-shared" +version = "0.0.1" +edition = "2021" +authors = ["Jakub Płaskonka ", "Krzysztof Pobiarżyn ", "Maciej Zieliński "] +license = "MIT" +repository = "https://github.com/odradev/odra-casper" +description = "Utilites uses by the Odra Casper-related crates." +keywords = ["wasm", "webassembly", "blockchain"] +categories = ["wasm", "smart contracts"] + +[dependencies] +casper-types = "1.5.0" +odra-types = "0.0.1" diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 0000000..1614537 --- /dev/null +++ b/shared/README.md @@ -0,0 +1,3 @@ +# Odra Casper Shared + +This crate holds code that is shared between `odra-casper-backend` and `odra-casper-test-env` crates. diff --git a/common/src/casper_address.rs b/shared/src/casper_address.rs similarity index 62% rename from common/src/casper_address.rs rename to shared/src/casper_address.rs index 3636592..c86d221 100644 --- a/common/src/casper_address.rs +++ b/shared/src/casper_address.rs @@ -1,15 +1,12 @@ use casper_types::{ account::AccountHash, bytesrepr::{self, FromBytes, ToBytes}, - CLType, CLTyped, ContractPackageHash, Key, + CLType, CLTyped, ContractPackageHash, Key, }; use odra_types::Address as OdraAddress; /// An enum representing an [`AccountHash`] or a [`ContractPackageHash`]. -/// -/// It is taken from [`CasperLabs's ERC20`](https://raw.githubusercontent.com/casper-ecosystem/erc20/master/erc20/src/address.rs). -/// It is copied instead of imported for the flexebility. #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum CasperAddress { /// Represents an account hash. @@ -37,6 +34,7 @@ impl CasperAddress { } } + /// Returns true if `self` is the `Contract` variant. pub fn is_contract(&self) -> bool { self.as_contract_package_hash().is_some() } @@ -58,7 +56,9 @@ impl From for Key { fn from(address: CasperAddress) -> Self { match address { CasperAddress::Account(account_hash) => Key::Account(account_hash), - CasperAddress::Contract(contract_package_hash) => Key::Hash(contract_package_hash.value()), + CasperAddress::Contract(contract_package_hash) => { + Key::Hash(contract_package_hash.value()) + } } } } @@ -69,8 +69,10 @@ impl TryFrom for CasperAddress { fn try_from(key: Key) -> Result { match key { Key::Account(account_hash) => Ok(CasperAddress::Account(account_hash)), - Key::Hash(contract_package_hash) => Ok(CasperAddress::Contract(ContractPackageHash::new(contract_package_hash))), - _ => Err(String::from("Unsupport Key type.")) + Key::Hash(contract_package_hash) => Ok(CasperAddress::Contract( + ContractPackageHash::new(contract_package_hash), + )), + _ => Err(String::from("Unsupport Key type.")), } } } @@ -97,8 +99,9 @@ impl FromBytes for CasperAddress { let address = match key { Key::Account(account_hash) => CasperAddress::Account(account_hash), - Key::Hash(raw_contract_package_hash) => - CasperAddress::Contract(ContractPackageHash::new(raw_contract_package_hash)), + Key::Hash(raw_contract_package_hash) => { + CasperAddress::Contract(ContractPackageHash::new(raw_contract_package_hash)) + } _ => return Err(bytesrepr::Error::Formatting), }; @@ -106,17 +109,21 @@ impl FromBytes for CasperAddress { } } -impl Into for CasperAddress { - fn into(self) -> OdraAddress { - OdraAddress::new(&self.to_bytes().unwrap()) +impl TryFrom for OdraAddress { + type Error = bytesrepr::Error; + + fn try_from(value: CasperAddress) -> Result { + let bytes = value.to_bytes()?; + Ok(OdraAddress::new(&bytes)) } } -impl From<&OdraAddress> for CasperAddress { - fn from(address: &OdraAddress) -> Self { - let bytes = address.bytes(); - //TODO to add error handling - ::from_bytes(bytes).unwrap().0 +impl TryFrom for CasperAddress { + type Error = bytesrepr::Error; + + fn try_from(value: OdraAddress) -> Result { + let (casper_address, _) = CasperAddress::from_bytes(value.bytes())?; + Ok(casper_address) } } @@ -125,8 +132,10 @@ mod tests { use super::*; // TODO: casper-types > 1.5.0 will have prefix fixed. - const CONTRACT_PACKAGE_HASH: &str = "contract-package-wasm7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a"; - const ACCOUNT_HASH: &str = "account-hash-3b4ffcfb21411ced5fc1560c3f6ffed86f4885e5ea05cde49d90962a48a14d95"; + const CONTRACT_PACKAGE_HASH: &str = + "contract-package-wasm7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a"; + const ACCOUNT_HASH: &str = + "account-hash-3b4ffcfb21411ced5fc1560c3f6ffed86f4885e5ea05cde49d90962a48a14d95"; fn mock_account_hash() -> AccountHash { AccountHash::from_formatted_str(ACCOUNT_HASH).unwrap() @@ -136,12 +145,10 @@ mod tests { ContractPackageHash::from_formatted_str(CONTRACT_PACKAGE_HASH).unwrap() } - fn test- - #[test] fn test_casper_address_account_hash_conversion() { let account_hash = mock_account_hash(); - + // It is possible to convert CasperAddress back to AccountHash. let casper_address = CasperAddress::from(account_hash); assert_eq!(casper_address.as_account_hash().unwrap(), &account_hash); @@ -152,23 +159,19 @@ mod tests { // And it is not a contract. assert!(!casper_address.is_contract()); - // It can be converted into a Key and back to CasperAddress. - let key = Key::from(casper_address); - let restored = CasperAddress::try_from(key); - assert_eq!(restored.unwrap(), casper_address); - - // It can be converted into bytes and back. - let bytes = casper_address.to_bytes().unwrap(); - + test_casper_address_conversions(casper_address); } #[test] fn test_casper_address_contract_package_hash_conversion() { let contract_package_hash = mock_contract_package_hash(); let casper_address = CasperAddress::from(contract_package_hash); - + // It is possible to convert CasperAddress back to ContractPackageHash. - assert_eq!(casper_address.as_contract_package_hash().unwrap(), &contract_package_hash); + assert_eq!( + casper_address.as_contract_package_hash().unwrap(), + &contract_package_hash + ); // It is not possible to convert CasperAddress to AccountHash. assert!(casper_address.as_account_hash().is_none()); @@ -176,10 +179,20 @@ mod tests { // And it is a contract. assert!(casper_address.is_contract()); + test_casper_address_conversions(casper_address); + } + + fn test_casper_address_conversions(casper_address: CasperAddress) { // It can be converted into a Key and back to CasperAddress. let key = Key::from(casper_address); let restored = CasperAddress::try_from(key); assert_eq!(restored.unwrap(), casper_address); + + // It can be converted into bytes and back. + let bytes = casper_address.to_bytes().unwrap(); + let (restored, rest) = CasperAddress::from_bytes(&bytes).unwrap(); + assert!(rest.is_empty()); + assert_eq!(restored, casper_address); } #[test] @@ -190,4 +203,32 @@ mod tests { Err(String::from("Unsupport Key type.")) ); } -} \ No newline at end of file + + #[test] + fn test_casper_address_account_hash_to_odra_address_conversion_() { + let casper_address = CasperAddress::from(mock_account_hash()); + test_casper_address_to_odra_address_conversion(casper_address); + } + + #[test] + fn test_casper_address_contract_package_hash_to_odra_address_conversion_() { + let casper_address = CasperAddress::from(mock_contract_package_hash()); + test_casper_address_to_odra_address_conversion(casper_address); + } + + fn test_casper_address_to_odra_address_conversion(casper_address: CasperAddress) { + let odra_address = OdraAddress::try_from(casper_address).unwrap(); + let restored = CasperAddress::try_from(odra_address).unwrap(); + assert_eq!(restored, casper_address); + } + + #[test] + fn test_casper_address_from_bad_odra_address_fails() { + // Only 0 and 1 is allowd to be on the first place. + let odra_address = OdraAddress::new(&[2, 2, 3]); + assert_eq!( + CasperAddress::try_from(odra_address), + Err(bytesrepr::Error::Formatting) + ); + } +} diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 0000000..1190567 --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1 @@ +pub mod casper_address; diff --git a/test_env/.gitignore b/test_env/.gitignore deleted file mode 100644 index 5e0e3b2..0000000 --- a/test_env/.gitignore +++ /dev/null @@ -1 +0,0 @@ -getter_proxy.wasm \ No newline at end of file diff --git a/test_env/CHANGELOG.md b/test_env/CHANGELOG.md new file mode 100644 index 0000000..57f85f7 --- /dev/null +++ b/test_env/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +Changelog for `odra-casper-test-env`. + +## [0.0.1] - 2022-07-18 +### Added +- `getter_proxy` child crate. +- `CHANGELOG.md` and `README.md` files. +- `env::CasperTestEnv` that wraps Casper's `InMemoryWasmTestBuilder`. +- `#[no_mangle]` functions used to communicate with the `libodra_test_env.so` diff --git a/test_env/Cargo.toml b/test_env/Cargo.toml index 4062faa..8337671 100644 --- a/test_env/Cargo.toml +++ b/test_env/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "casper_test_env" -version = "0.1.0" +name = "odra-casper-test-env" +version = "0.0.1" edition = "2021" [dependencies] -odra = { git = "https://github.com/odradev/odra.git", default-features = false, features = [ "wasm-test" ] } -# odra = { path = "../../../odra/core", default-features = false, features = [ "wasm-test" ] } -casper-commons = { path = "../common", features = [ "wasm-test" ] } casper-engine-test-support = { version = "2.0.3", features = ["test-support"] } casper-execution-engine = { version = "2.0.0" } casper-types = "1.5.0" +odra-casper-shared = { version = "0.0.1", path = "../shared" } +odra = { version = "0.0.1", default-features = false, features = [ "wasm-test" ] } + [lib] crate-type = ["cdylib"] name = "odra_test_env" diff --git a/test_env/README.md b/test_env/README.md new file mode 100644 index 0000000..87664f1 --- /dev/null +++ b/test_env/README.md @@ -0,0 +1,3 @@ +# Odra Casper Backend + +This crate proides `libodra_test_env.so` shared library, that is used to test Casper contracts against `CasperTestEnv`. diff --git a/test_env/build.rs b/test_env/build.rs deleted file mode 100644 index fee875c..0000000 --- a/test_env/build.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::path::Path; -use std::process::Command; - -fn main() { - if Path::new("getter_proxy.wasm").exists() { - return; - } - - Command::new("cargo") - .current_dir("getter_proxy") - .args(vec![ - "build", - "--release", - "--no-default-features", - "--bin", - "getter_proxy", - "--target", - "wasm32-unknown-unknown" - ]) - .output() - .expect("Couldn't build getter proxy"); - - let source = "getter_proxy/target/wasm32-unknown-unknown/release/getter_proxy.wasm"; - let target = "./getter_proxy.wasm"; - Command::new("cp") - .args(vec![source, target]) - .output() - .expect("Couldn't copy getter proxy"); - - let wasm_output = Command::new("wasm-strip").arg("getter_proxy.wasm").output(); - - match wasm_output { - Ok(_) => {} - Err(output) => { - println!( - "There was an error while running wasmstrip:\n{}\nContinuing anyway...", - output - ); - } - } -} diff --git a/test_env/getter_proxy.wasm b/test_env/getter_proxy.wasm new file mode 100755 index 0000000..4d2a314 Binary files /dev/null and b/test_env/getter_proxy.wasm differ diff --git a/test_env/getter_proxy/.gitignore b/test_env/getter_proxy/.gitignore deleted file mode 100644 index eb5a316..0000000 --- a/test_env/getter_proxy/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/test_env/getter_proxy/CHANGELOG.md b/test_env/getter_proxy/CHANGELOG.md new file mode 100644 index 0000000..9cf49c9 --- /dev/null +++ b/test_env/getter_proxy/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +Changelog for `odra-casper-getter-proxy`. + +## [0.0.1] - 2022-07-18 +### Added +- `getter_proxy.rs` binary file. diff --git a/test_env/getter_proxy/Cargo.lock b/test_env/getter_proxy/Cargo.lock deleted file mode 100644 index 08014c3..0000000 --- a/test_env/getter_proxy/Cargo.lock +++ /dev/null @@ -1,692 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98fcd36dda4e17b7d7abc64cb549bf0201f4ab71e00700c798ca7e62ed3761fa" -dependencies = [ - "funty", - "radium", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" -dependencies = [ - "crypto-mac 0.8.0", - "digest", - "opaque-debug", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "casper-contract" -version = "1.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790b76807d64788758208757b0a17970bf756cb7c392f55b1a22021a34f95991" -dependencies = [ - "casper-types", - "hex_fmt", - "wee_alloc", -] - -[[package]] -name = "casper-types" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e82a13d1784104fd021a38da56c69da94e84b26b03c2cf3d8da3895a16c8c" -dependencies = [ - "base16", - "base64", - "bitflags", - "blake2", - "ed25519-dalek", - "hex", - "hex_fmt", - "k256", - "num", - "num-derive", - "num-integer", - "num-rational", - "num-traits", - "rand 0.8.5", - "serde", - "serde_bytes", - "serde_json", - "uint", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" -dependencies = [ - "byteorder", - "digest", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "ecdsa" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fbdb4ff710acb4db8ca29f93b897529ea6d6a45626d5183b47e012aa6ae7e4" -dependencies = [ - "elliptic-curve", - "hmac", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "sha2", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2db227e61a43a34915680bdda462ec0e212095518020a88a1f91acd16092c39" -dependencies = [ - "bitvec", - "digest", - "ff", - "funty", - "generic-array", - "group", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "ff" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01646e077d4ebda82b73f1bca002ea1e91561a77df2431a9e79729bcc31950ef" -dependencies = [ - "bitvec", - "rand_core 0.5.1", - "subtle", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getter_proxy" -version = "0.1.0" -dependencies = [ - "casper-contract", - "casper-types", -] - -[[package]] -name = "group" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc11f9f5fbf1943b48ae7c2bf6846e7d827a512d1be4f23af708f5ca5d01dde1" -dependencies = [ - "ff", - "rand_core 0.5.1", - "subtle", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex_fmt" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest", -] - -[[package]] -name = "itoa" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" - -[[package]] -name = "k256" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4476a0808212a9e81ce802eb1a0cfc60e73aea296553bacc0fac7e1268bc572a" -dependencies = [ - "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", -] - -[[package]] -name = "libc" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro2" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "rand_chacha", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "ryu" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" - -[[package]] -name = "serde" -version = "1.0.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer", - "cfg-if 1.0.0", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "signature" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" -dependencies = [ - "digest", - "rand_core 0.5.1", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-ident" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" - -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wee_alloc" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "memory_units", - "winapi", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/test_env/getter_proxy/Cargo.toml b/test_env/getter_proxy/Cargo.toml index d95f626..c608c25 100644 --- a/test_env/getter_proxy/Cargo.toml +++ b/test_env/getter_proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "getter_proxy" -version = "0.1.0" +name = "odra-casper-getter-proxy" +version = "0.0.1" edition = "2021" [dependencies] diff --git a/test_env/getter_proxy/README.md b/test_env/getter_proxy/README.md new file mode 100644 index 0000000..d587d1f --- /dev/null +++ b/test_env/getter_proxy/README.md @@ -0,0 +1,3 @@ +# Odra Casper Backend + +This crate provides `getter_proxy.wasm` that can be used to call any Casper contract and save result into caller's named key. It bypasses Casper's limitation to return value from contract call. diff --git a/test_env/getter_proxy/bin/getter_proxy.rs b/test_env/getter_proxy/bin/getter_proxy.rs index edd5e19..4197510 100644 --- a/test_env/getter_proxy/bin/getter_proxy.rs +++ b/test_env/getter_proxy/bin/getter_proxy.rs @@ -3,17 +3,21 @@ extern crate alloc; -use core::mem::MaybeUninit; use core::convert::TryInto; +use core::mem::MaybeUninit; use alloc::{string::String, vec::Vec}; +use casper_contract::contract_api::storage; use casper_contract::{ contract_api::{self, runtime}, ext_ffi, unwrap_or_revert::UnwrapOrRevert, }; -use casper_contract::contract_api::storage; -use casper_types::{api_error, bytesrepr::{Bytes, FromBytes, ToBytes}, ApiError, ContractPackageHash, ContractVersion, RuntimeArgs, CLTyped}; +use casper_types::{ + api_error, + bytesrepr::{Bytes, FromBytes, ToBytes}, + ApiError, CLTyped, ContractPackageHash, ContractVersion, RuntimeArgs, +}; #[no_mangle] fn call() { @@ -113,4 +117,4 @@ pub fn set_key(name: &str, value: T) { runtime::put_key(name, key); } } -} \ No newline at end of file +} diff --git a/test_env/src/env.rs b/test_env/src/env.rs index 9ca143f..0133d43 100644 --- a/test_env/src/env.rs +++ b/test_env/src/env.rs @@ -1,23 +1,22 @@ use std::{cell::RefCell, path::PathBuf}; -use odra::types::{OdraError, VmError, EventData, event::Error as EventError}; -use casper_commons::address::Address; use casper_engine_test_support::{ DeployItemBuilder, ExecuteRequestBuilder, InMemoryWasmTestBuilder, ARG_AMOUNT, DEFAULT_ACCOUNT_INITIAL_BALANCE, DEFAULT_GENESIS_CONFIG, DEFAULT_GENESIS_CONFIG_HASH, DEFAULT_PAYMENT, }; -use casper_execution_engine::core::engine_state::{ self, - run_genesis_request::RunGenesisRequest, GenesisAccount, +use casper_execution_engine::core::engine_state::{ + self, run_genesis_request::RunGenesisRequest, GenesisAccount, }; +pub use casper_execution_engine::core::execution::Error as CasperExecutionError; use casper_types::{ - ApiError, account::AccountHash, bytesrepr::{Bytes, FromBytes, ToBytes}, - runtime_args, CLTyped, ContractPackageHash, Key, Motes, PublicKey, RuntimeArgs, SecretKey, - U512, URef, ContractHash, + runtime_args, ApiError, CLTyped, ContractHash, ContractPackageHash, Key, Motes, PublicKey, + RuntimeArgs, SecretKey, URef, U512, }; -pub use casper_execution_engine::core::execution::Error as ExecutionError; +use odra::types::{event::EventError, EventData, ExecutionError, OdraError, VmError}; +use odra_casper_shared::casper_address::CasperAddress; thread_local! { pub static ENV: RefCell = RefCell::new(CasperTestEnv::new()); @@ -27,18 +26,18 @@ const EVENTS: &str = "__events"; const EVENTS_LENGTH: &str = "__events_length"; pub struct CasperTestEnv { - accounts: Vec
, - active_account: Address, + accounts: Vec, + active_account: CasperAddress, context: InMemoryWasmTestBuilder, block_time: u64, calls_counter: u32, - error: Option + error: Option, } impl CasperTestEnv { pub fn new() -> Self { let mut genesis_config = DEFAULT_GENESIS_CONFIG.clone(); - let mut accounts: Vec
= Vec::new(); + let mut accounts: Vec = Vec::new(); for i in 0..20 { // Create keypair. let secret_key = SecretKey::ed25519_from_bytes([i; 32]).unwrap(); @@ -67,12 +66,12 @@ impl CasperTestEnv { builder.run_genesis(&run_genesis_request).commit(); Self { - active_account: accounts[0].clone(), + active_account: accounts[0], context: builder, accounts, block_time: 0, calls_counter: 0, - error: None + error: None, } } @@ -126,20 +125,17 @@ impl CasperTestEnv { let active_account = self.active_account_hash(); - let result = if self.context.is_error() { + if self.context.is_error() { self.error = Some(parse_error(self.context.get_error().unwrap())); None } else if has_return { - let result: Bytes = self.get_account_value(active_account, "result"); - Some(result) + Some(self.get_account_value(active_account, "result")) } else { None - }; - - result + } } - pub fn set_caller(&mut self, account: Address) { + pub fn set_caller(&mut self, account: CasperAddress) { self.active_account = account; } @@ -147,11 +143,11 @@ impl CasperTestEnv { *self.active_account.as_account_hash().unwrap() } - pub fn get_account(&self, n: usize) -> Address { + pub fn get_account(&self, n: usize) -> CasperAddress { *self.accounts.get(n).unwrap() } - pub fn as_account(&mut self, account: Address) { + pub fn as_account(&mut self, account: CasperAddress) { self.active_account = account; } @@ -188,12 +184,12 @@ impl CasperTestEnv { self.error.clone() } - pub fn get_event(&self, address: Address, index: i32) -> Result { - let address = address.as_contract_package_hash().unwrap().clone(); + pub fn get_event(&self, address: CasperAddress, index: i32) -> Result { + let address = address.as_contract_package_hash().unwrap(); let contract_hash: ContractHash = self .context - .get_contract_package(address) + .get_contract_package(*address) .unwrap() .current_contract_hash() .unwrap(); @@ -210,7 +206,11 @@ impl CasperTestEnv { let events_length: u32 = self .context - .query(None, Key::Hash(contract_hash.value()), &[String::from(EVENTS_LENGTH)]) + .query( + None, + Key::Hash(contract_hash.value()), + &[String::from(EVENTS_LENGTH)], + ) .unwrap() .as_cl_value() .unwrap() @@ -218,7 +218,7 @@ impl CasperTestEnv { .into_t() .unwrap(); - let event_position: u32 = odra::test_utils::event_absolute_position(events_length, index)?; + let event_position = odra::utils::event_absolute_position(events_length as usize, index)?; match self.context.query_dictionary_item( None, @@ -226,25 +226,38 @@ impl CasperTestEnv { &event_position.to_string(), ) { Ok(val) => { - let value: Bytes = val.as_cl_value().unwrap().clone().into_t::().unwrap(); + let value: Bytes = val + .as_cl_value() + .unwrap() + .clone() + .into_t::() + .unwrap(); Ok(value.inner_bytes().clone()) } - Err(e) => { - Err(EventError::IndexOutOfBounds) - }, + Err(_) => Err(EventError::IndexOutOfBounds), } } } +impl Default for CasperTestEnv { + fn default() -> Self { + Self::new() + } +} + fn parse_error(err: engine_state::Error) -> OdraError { if let engine_state::Error::Exec(exec_err) = err { match exec_err { - ExecutionError::Revert(ApiError::User(id)) => OdraError::execution_err(id, ""), - ExecutionError::InvalidContext => OdraError::VmError(VmError::InvalidContext), - ExecutionError::NoSuchMethod(name) => OdraError::VmError(VmError::NoSuchMethod(name)), - _ => OdraError::VmError(VmError::Other(format!("Casper ExecError: {}", exec_err.to_string()))), + CasperExecutionError::Revert(ApiError::User(id)) => { + OdraError::ExecutionError(ExecutionError::new(id, "")) + } + CasperExecutionError::InvalidContext => OdraError::VmError(VmError::InvalidContext), + CasperExecutionError::NoSuchMethod(name) => { + OdraError::VmError(VmError::NoSuchMethod(name)) + } + _ => OdraError::VmError(VmError::Other(format!("Casper ExecError: {}", exec_err))), } } else { - OdraError::VmError(VmError::Other(format!("Casper EngineStateError: {}", err.to_string()))) + OdraError::VmError(VmError::Other(format!("Casper EngineStateError: {}", err))) } } diff --git a/test_env/src/lib.rs b/test_env/src/lib.rs index 1aa888b..d7bbc8f 100644 --- a/test_env/src/lib.rs +++ b/test_env/src/lib.rs @@ -1,27 +1,27 @@ use crate::env::ENV; use casper_types::{bytesrepr::Bytes, RuntimeArgs}; -use odra::types::{Address as OdraAddress, OdraError, EventData, event::Error as EventError}; -use casper_commons::{address::Address as CasperAddress, odra_address_wrapper::OdraAddressWrapper}; +use odra::types::{event::EventError, Address as OdraAddress, EventData, OdraError}; +use odra_casper_shared::casper_address::CasperAddress; pub mod env; #[no_mangle] fn backend_name() -> String { - "Casper test backend".to_string() + "CasperVM".to_string() } #[no_mangle] fn register_contract(name: &str, args: &RuntimeArgs) -> OdraAddress { ENV.with(|env| { let wasm_name = format!("{}.wasm", name); - env.borrow_mut() - .deploy_contract(&wasm_name, args.clone()); - + env.borrow_mut().deploy_contract(&wasm_name, args.clone()); + let contract_package_hash = format!("{}_package_hash", name); - let contract_package_hash = env.borrow().get_contract_package_hash(&contract_package_hash); + let contract_package_hash = env + .borrow() + .get_contract_package_hash(&contract_package_hash); let casper_address: CasperAddress = contract_package_hash.into(); - let wrapped_address: OdraAddressWrapper = casper_address.into(); - *wrapped_address + OdraAddress::try_from(casper_address).unwrap() }) } @@ -33,39 +33,39 @@ pub fn call_contract( has_return: bool, ) -> Option { ENV.with(|env| { - let contract_hash: CasperAddress = OdraAddressWrapper::new(addr.to_owned()).into(); - let contract_hash = contract_hash.as_contract_package_hash().unwrap(); - env.borrow_mut() - .call_contract(*contract_hash, entrypoint, args.to_owned(), has_return) + let casper_address = CasperAddress::try_from(*addr).unwrap(); + let contract_package_hash = casper_address.as_contract_package_hash().unwrap(); + env.borrow_mut().call_contract( + *contract_package_hash, + entrypoint, + args.to_owned(), + has_return, + ) }) } #[no_mangle] pub fn set_caller(address: &OdraAddress) { ENV.with(|env| { - let odra_address = OdraAddressWrapper::new(address.to_owned()); - env.borrow_mut().as_account(odra_address.into()) + let casper_address = CasperAddress::try_from(*address).unwrap(); + env.borrow_mut().as_account(casper_address) }) } #[no_mangle] pub fn get_account(n: usize) -> OdraAddress { - ENV.with(|env| { - env.borrow().get_account(n).into() - }) + ENV.with(|env| OdraAddress::try_from(env.borrow().get_account(n)).unwrap()) } #[no_mangle] pub fn get_error() -> Option { - ENV.with(|env| { - env.borrow().get_error() - }) + ENV.with(|env| env.borrow().get_error()) } #[no_mangle] pub fn get_event(address: &OdraAddress, index: i32) -> Result { ENV.with(|env| { - let odra_address = OdraAddressWrapper::new(address.to_owned()); - env.borrow().get_event(odra_address.into(), index) + let casper_address = CasperAddress::try_from(*address).unwrap(); + env.borrow().get_event(casper_address, index) }) -} \ No newline at end of file +}