From e458bfd308f960c58c444890401a0aabf4a39085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Zieli=C5=84ski?= Date: Tue, 9 Aug 2022 14:46:03 +0000 Subject: [PATCH] Version 0.0.1 (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Version 0.0.1 Co-authored-by: Kuba Płaskonka Co-authored-by: Krzysztof Pobiarżyn --- .github/workflows/odra-casper-ci.yml | 32 + .gitignore | 11 +- LICENSE | 22 + Makefile | 34 + README.md | 3 + backend/CHANGELOG.md | 9 + backend/Cargo.toml | 23 +- backend/README.md | 4 + backend/src/backend.rs | 64 +- backend/src/{backend => }/casper_env.rs | 99 ++- backend/src/codegen.rs | 117 ++- backend/src/codegen/arg.rs | 36 + backend/src/codegen/call.rs | 117 --- backend/src/codegen/constructor.rs | 76 +- backend/src/codegen/entrypoints_def.rs | 113 +++ backend/src/codegen/ty.rs | 82 ++- .../{entrypoints.rs => wasm_entrypoint.rs} | 44 +- backend/src/lib.rs | 7 +- common/Cargo.toml | 15 - common/src/address.rs | 109 --- common/src/lib.rs | 2 - common/src/odra_address_wrapper.rs | 92 --- rust-toolchain | 1 + shared/CHANGELOG.md | 8 + shared/Cargo.toml | 14 + shared/README.md | 3 + {common => shared}/src/casper_address.rs | 107 ++- shared/src/lib.rs | 1 + test_env/.gitignore | 1 - test_env/CHANGELOG.md | 10 + test_env/Cargo.toml | 10 +- test_env/README.md | 3 + test_env/build.rs | 41 -- test_env/getter_proxy.wasm | Bin 0 -> 61239 bytes test_env/getter_proxy/.gitignore | 1 - test_env/getter_proxy/CHANGELOG.md | 7 + test_env/getter_proxy/Cargo.lock | 692 ------------------ test_env/getter_proxy/Cargo.toml | 4 +- test_env/getter_proxy/README.md | 3 + test_env/getter_proxy/bin/getter_proxy.rs | 12 +- test_env/src/env.rs | 87 ++- test_env/src/lib.rs | 48 +- 42 files changed, 824 insertions(+), 1340 deletions(-) create mode 100644 .github/workflows/odra-casper-ci.yml create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 backend/CHANGELOG.md create mode 100644 backend/README.md rename backend/src/{backend => }/casper_env.rs (77%) delete mode 100644 backend/src/codegen/call.rs create mode 100644 backend/src/codegen/entrypoints_def.rs rename backend/src/codegen/{entrypoints.rs => wasm_entrypoint.rs} (52%) delete mode 100644 common/Cargo.toml delete mode 100644 common/src/address.rs delete mode 100644 common/src/lib.rs delete mode 100644 common/src/odra_address_wrapper.rs create mode 100644 rust-toolchain create mode 100644 shared/CHANGELOG.md create mode 100644 shared/Cargo.toml create mode 100644 shared/README.md rename {common => shared}/src/casper_address.rs (62%) create mode 100644 shared/src/lib.rs delete mode 100644 test_env/.gitignore create mode 100644 test_env/CHANGELOG.md create mode 100644 test_env/README.md delete mode 100644 test_env/build.rs create mode 100755 test_env/getter_proxy.wasm delete mode 100644 test_env/getter_proxy/.gitignore create mode 100644 test_env/getter_proxy/CHANGELOG.md delete mode 100644 test_env/getter_proxy/Cargo.lock create mode 100644 test_env/getter_proxy/README.md 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 0000000000000000000000000000000000000000..4d2a314e4a715692378c8f816d93c12c3c23ee14 GIT binary patch literal 61239 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 literal 0 HcmV?d00001 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 +}