diff --git a/Cargo.lock b/Cargo.lock index d66f0aa..cc25898 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,12 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + [[package]] name = "cc" version = "1.1.5" @@ -696,7 +702,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#626c9598be949aa3dbdd72e8a40531f68b01d6c2" +source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff", @@ -953,6 +959,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1862,10 +1877,11 @@ dependencies = [ [[package]] name = "hp-system" -version = "0.1.0" +version = "0.3.0" dependencies = [ "frame-support", "frame-system", + "sp-core", "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.11.0)", ] @@ -2414,6 +2430,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -2477,7 +2499,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.71", @@ -2634,7 +2656,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-call-hybrid-vm" -version = "0.1.0" +version = "0.3.0" dependencies = [ "evm", "fp-evm", @@ -2664,7 +2686,7 @@ dependencies = [ [[package]] name = "pallet-hybrid-vm" -version = "0.2.0" +version = "0.3.0" dependencies = [ "assert_matches", "blake2-rfc", @@ -2705,8 +2727,9 @@ dependencies = [ [[package]] name = "pallet-hybrid-vm-port" -version = "0.2.0-dev" +version = "0.3.0" dependencies = [ + "byte-slice-cast", "ethereum", "ethereum-types", "evm", @@ -2719,19 +2742,24 @@ dependencies = [ "frame-support", "frame-system", "hex", + "hp-system", "ink_env", "libsecp256k1", "pallet-balances", "pallet-contracts", "pallet-evm", "pallet-hybrid-vm", + "pallet-insecure-randomness-collective-flip", "pallet-timestamp", "parity-scale-codec", + "precompile-utils", "rlp", "scale-info", + "sha3 0.8.2", "sp-core", "sp-io", "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.11.0)", ] [[package]] @@ -2987,12 +3015,56 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precompile-utils" +version = "0.1.0" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v1.11.0#5779b59043ae018d07d84f4e8e0c49087f804d3a" +dependencies = [ + "environmental", + "evm", + "fp-evm", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "num_enum", + "pallet-evm", + "parity-scale-codec", + "precompile-utils-macro", + "sp-core", + "sp-io", + "sp-runtime", + "sp-weights 27.0.0", + "staging-xcm 7.0.0", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.1.0" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v1.11.0#5779b59043ae018d07d84f4e8e0c49087f804d3a" +dependencies = [ + "case", + "num_enum", + "prettyplease", + "proc-macro2", + "quote", + "sp-crypto-hashing", + "syn 1.0.109", +] + [[package]] name = "pretty_assertions" version = "0.7.2" @@ -3270,7 +3342,7 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof#626c9598be949aa3dbdd72e8a40531f68b01d6c2" +source = "git+https://github.com/w3f/ring-proof#665f5f51af5734c7b6d90b985dd6861d4c5b4752" dependencies = [ "ark-ec", "ark-ff", @@ -3929,7 +4001,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -3993,7 +4065,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "proc-macro2", "quote", @@ -4013,7 +4085,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "environmental", "parity-scale-codec", @@ -4148,7 +4220,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -4180,7 +4252,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "Inflector", "expander", @@ -4237,7 +4309,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" [[package]] name = "sp-storage" @@ -4254,7 +4326,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "impl-serde", "parity-scale-codec", @@ -4289,7 +4361,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "parity-scale-codec", "tracing", @@ -4361,7 +4433,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#4bcdf8166972b63a70b618c90424b9f7d64b719b" +source = "git+https://github.com/paritytech/polkadot-sdk#149c70938f2b29f8d92ba1cc952aeb63d4084e27" dependencies = [ "impl-trait-for-tuples", "log", @@ -4605,6 +4677,37 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -4741,6 +4844,7 @@ dependencies = [ "sharded-slab", "smallvec", "thread_local", + "time", "tracing", "tracing-core", "tracing-log", diff --git a/Cargo.toml b/Cargo.toml index 51c6b68..7563485 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ fp-self-contained = { git = "https://github.com/paritytech/frontier", branch = " fp-storage = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v1.11.0", default-features = false } pallet-evm = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v1.11.0", default-features = false } pallet-evm-precompile-simple = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v1.11.0", default-features = false } +precompile-utils = { git = "https://github.com/paritytech/frontier", branch = "polkadot-v1.11.0", default-features = false } # ink ink_env = { git = "https://github.com/paritytech/ink" } diff --git a/external/contract/src/erc20wasm/lib.rs b/external/contract/src/erc20wasm/lib.rs index 40bc500..f12736d 100644 --- a/external/contract/src/erc20wasm/lib.rs +++ b/external/contract/src/erc20wasm/lib.rs @@ -22,9 +22,7 @@ use ink_env::Environment; use ink_prelude::string::String; -use sp_core::H160; use ink_prelude::vec::Vec; -//use sp_std::vec::Vec; type AccountId = ::AccountId; #[ink::chain_extension( extension = 0 )] @@ -33,8 +31,6 @@ pub trait MyChainExtension { #[ink(function = 5, handle_status = false)] fn call_evm_extension(vm_input: Vec) -> String; - #[ink(function = 6, handle_status = false)] - fn h160_to_accountid(evm_address: H160) -> AccountId; } @@ -64,14 +60,14 @@ mod erc20 { use ink::storage::Lazy; use ink::storage::Mapping as StorageHashMap; - use ink_env::{hash, ReturnFlags}; use ink_prelude::string::String; use ink_prelude::string::ToString; use ink_prelude::vec; use ink_prelude::vec::Vec; - use precompile_utils::prelude::*; - use sp_core::U256; + use sp_core::U256; + //evm_fun_abi, wasm_message_name, wasm_message_selector + pub type EvmABI = (String, String, Option<[u8; 4]>); /// A simple ERC-20 contract. #[ink(storage)] @@ -155,115 +151,48 @@ mod erc20 { instance } - /// Evm ABI interface. + /// Get Evm ABI interface . #[ink(message)] - pub fn evm_abi_call(&mut self, para: Vec) -> Vec { - let who = self.env().caller(); - - let mut a: [u8; 4] = Default::default(); - a.copy_from_slice(&Self::hash_keccak_256(b"balanceOf(address)")[0..4]); - let balance_selector = u32::from_be_bytes(a); - - let mut a: [u8; 4] = Default::default(); - a.copy_from_slice(&Self::hash_keccak_256(b"transfer(address,uint256)")[0..4]); - let transfer_selector = u32::from_be_bytes(a); - - let evm_selector = if para.len() < 4 { 0 } else { - let mut a: [u8; 4] = Default::default(); - a.copy_from_slice(¶[0..4]); - u32::from_be_bytes(a) - }; - - match evm_selector { - // 1. balanceOf(address account) external view returns (uint256); - // balance_of(&self, owner: AccountId) -> Balance; - a if a == balance_selector => { - if para.len() < 5 { - self.env().emit_event(ParameterError { - caller: who, - parameter: para, - }); - ink_env::return_value::(ReturnFlags::REVERT, &13u8); - }; - let parameter = solidity::codec::decode_arguments::
(¶[4..]); - match parameter { - Ok(t) => { - let accountid = self.env().extension().h160_to_accountid(t.0); - - let return_result = self.balance_of(accountid); - solidity::codec::encode_arguments::(return_result) - } - Err(_) => { - self.env().emit_event(ParameterError { - caller: who, - parameter: para, - }); - ink_env::return_value::(ReturnFlags::REVERT, &12u8); - } - }; - } - // 2. transfer(address to, uint256 amount) external returns (bool); - // transfer(&mut self, to: AccountId, value: Balance) -> Result<()> - b if b == transfer_selector => { - if para.len() < 5 { - self.env().emit_event(ParameterError { - caller: who, - parameter: para, - }); - ink_env::return_value::(ReturnFlags::REVERT, &13u8); - }; - let parameter = solidity::codec::decode_arguments::<(Address, U256)>(¶[4..]); - match parameter { - Ok(t) => { - let accountid = self.env().extension().h160_to_accountid(t.0.0); - let balance = match Balance::try_from(t.1) { - Ok(t) => t, - Err(_) => { - self.env().emit_event(ParameterError { - caller: who, - parameter: para, - }); - ink_env::return_value::(ReturnFlags::REVERT, &1u8); - } - }; - let return_result = if self.transfer(accountid, balance).is_ok(){ - true - } else { false }; - solidity::codec::encode_arguments::(return_result) - } - Err(_) => { - self.env().emit_event(ParameterError { - caller: who, - parameter: para, - }); - ink_env::return_value::(ReturnFlags::REVERT, &2u8); - } - }; - } - // None - _ => { - self.env().emit_event(SelectorError { - caller: who, - selector: evm_selector, - }); - ink_env::return_value::(ReturnFlags::REVERT, &3u8); - } - } + pub fn hybridvm_evm_abi(&mut self) -> Vec { + let mut evm_abi: Vec = vec![]; + evm_abi.push(("name()returns(string)".to_string(), "name".to_string(), None)); + evm_abi.push(("symbol()returns(string)".to_string(), "symbol".to_string(), None)); + evm_abi.push(("decimals()returns(uint8)".to_string(), "decimals".to_string(), None)); + evm_abi.push(("totalSupply()returns(uint256)".to_string(), "total_supply_abi".to_string(), None)); + evm_abi.push(("balanceOf(address)returns(uint256)".to_string(), "balance_of_abi".to_string(), None)); + evm_abi.push(("allowance(address,address)returns(uint256)".to_string(), "allowance_abi".to_string(), None)); + evm_abi.push(("transfer(address,uint256)returns(bool)".to_string(), "transfer_abi".to_string(), None)); + evm_abi.push(("approve(address,uint256)returns(bool)".to_string(), "approve_abi".to_string(), None)); + evm_abi.push(("transferFrom(address,address,uint256)returns(bool)".to_string(), "transfer_from_abi".to_string(), None)); - vec![] - } - - pub fn hash_keccak_256(input: &[u8]) -> [u8; 32] { - let mut output = ::Type::default(); - ink_env::hash_bytes::(input, &mut output); - output + evm_abi + } + + #[ink(message)] + pub fn name(&self) -> String { + String::from("WasmTokenT") + } + + #[ink(message)] + pub fn symbol(&self) -> String { + String::from("WTT") } + #[ink(message)] + pub fn decimals(&self) -> u8 { + 18u8 + } + /// Returns the total token supply. #[ink(message)] pub fn total_supply(&self) -> Balance { self.total_supply } + + #[ink(message)] + pub fn total_supply_abi(&self) -> U256 { + self.total_supply.into() + } /// Returns the account balance for the specified `owner`. /// @@ -272,6 +201,11 @@ mod erc20 { pub fn balance_of(&self, owner: AccountId) -> Balance { self.balances.get(&owner).unwrap_or(0) } + + #[ink(message)] + pub fn balance_of_abi(&self, owner: AccountId) -> U256 { + self.balances.get(&owner).unwrap_or(0).into() + } /// Returns the amount which `spender` is still allowed to withdraw from `owner`. /// @@ -280,6 +214,11 @@ mod erc20 { pub fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance { self.allowances.get(&(owner, spender)).unwrap_or(0) } + + #[ink(message)] + pub fn allowance_abi(&self, owner: AccountId, spender: AccountId) -> U256 { + self.allowances.get(&(owner, spender)).unwrap_or(0).into() + } /// Transfers `value` amount of tokens from the caller's account to account `to`. /// @@ -294,6 +233,12 @@ mod erc20 { let from = self.env().caller(); self.transfer_from_to(from, to, value) } + + #[ink(message)] + pub fn transfer_abi(&mut self, to: AccountId, value: U256) -> bool { + let Ok(value) = Balance::try_from(value) else { return false }; + self.transfer(to, value).is_ok() + } /// Allows `spender` to withdraw from the caller's account multiple times, up to /// the `value` amount. @@ -312,6 +257,12 @@ mod erc20 { }); Ok(()) } + + #[ink(message)] + pub fn approve_abi(&mut self, spender: AccountId, value: U256) -> bool { + let Ok(value) = Balance::try_from(value) else { return false }; + self.approve(spender, value).is_ok() + } /// Transfers `value` tokens on the behalf of `from` to the account `to`. /// @@ -344,6 +295,17 @@ mod erc20 { self.allowances.insert((from, caller), &(allowance - value)); Ok(()) } + + #[ink(message)] + pub fn transfer_from_abi( + &mut self, + from: AccountId, + to: AccountId, + value: U256, + ) -> bool { + let Ok(value) = Balance::try_from(value) else { return false }; + self.transfer_from(from, to, value).is_ok() + } /// Transfers `value` amount of tokens from the caller's account to account `to`. /// @@ -375,7 +337,7 @@ mod erc20 { }); Ok(()) } - + // Test call EVM contract from this contract #[ink(message)] pub fn wasmCallEvm( diff --git a/frame/evm-precompile/call-hybrid-vm/Cargo.toml b/frame/evm-precompile/call-hybrid-vm/Cargo.toml index 59cbfe7..d9356a7 100644 --- a/frame/evm-precompile/call-hybrid-vm/Cargo.toml +++ b/frame/evm-precompile/call-hybrid-vm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-evm-precompile-call-hybrid-vm" -version = "0.3.0" +version = "0.4.0" description = "Call Hybrid vm precompiles for EVM pallet." repository.workspace = true edition.workspace = true diff --git a/frame/evm-precompile/call-hybrid-vm/src/lib.rs b/frame/evm-precompile/call-hybrid-vm/src/lib.rs index 4654c51..65b794e 100644 --- a/frame/evm-precompile/call-hybrid-vm/src/lib.rs +++ b/frame/evm-precompile/call-hybrid-vm/src/lib.rs @@ -16,11 +16,13 @@ #![cfg_attr(not(feature = "std"), no_std)] use core::marker::PhantomData; -use fp_evm::{ExitError, ExitSucceed, Precompile, PrecompileFailure}; -use fp_evm::{PrecompileHandle, PrecompileOutput, PrecompileResult}; +use fp_evm::{ + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, +}; use frame_system::RawOrigin; use hp_system::EvmHybridVMExtension; -use pallet_evm::AddressMapping; +use pallet_evm::{AddressMapping, GasWeightMapping}; pub struct CallHybridVM { _marker: PhantomData, @@ -37,7 +39,13 @@ where match T::call_hybrid_vm(origin.into(), handle.input().iter().cloned().collect(), target_gas) { - Ok(ret) => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: ret.0 }), + Ok(ret) => { + let gas_consume = ret.1; + let gas_limit = target_gas.unwrap_or(0); + let gas_record = if gas_consume > gas_limit { gas_limit } else { gas_consume }; + handle.record_cost(gas_record)?; + Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: ret.0 }) + }, Err(e) => { let err_str: &'static str = e.into(); diff --git a/frame/hybrid-vm-port/Cargo.toml b/frame/hybrid-vm-port/Cargo.toml index 7a12f5b..978bf4f 100644 --- a/frame/hybrid-vm-port/Cargo.toml +++ b/frame/hybrid-vm-port/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-hybrid-vm-port" -version = "0.3.0" +version = "0.4.0" license = "Apache-2.0" description = "HybridVM port pallet for HybridVM." edition = { workspace = true } @@ -28,8 +28,8 @@ fp-evm = { workspace = true } fp-rpc = { workspace = true } fp-storage = { workspace = true } pallet-evm = { workspace = true } -# ink! -ink_env = { workspace = true } +precompile-utils = { workspace = true } + #local pallet-hybrid-vm = { workspace = true, default-features = false } hp-system = { workspace = true, default-features = false } @@ -48,6 +48,8 @@ sp-core = { workspace = true, features = ["default"] } sp-std = { workspace = true, features = ["default"] } # Frontier fp-self-contained = { workspace = true, features = ["default"] } +# ink! +ink_env = { workspace = true, features = ["no-panic-handler"] } [features] default = ["std"] @@ -58,6 +60,7 @@ std = [ "rlp/std", "scale-codec/std", "scale-info/std", + "ink_env/std", # Substrate "frame-support/std", "frame-system/std", @@ -71,6 +74,7 @@ std = [ "fp-storage/std", "pallet-evm/std", "pallet-contracts/std", + "precompile-utils/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/frame/hybrid-vm-port/src/lib.rs b/frame/hybrid-vm-port/src/lib.rs index 1d90853..3e050cd 100644 --- a/frame/hybrid-vm-port/src/lib.rs +++ b/frame/hybrid-vm-port/src/lib.rs @@ -17,6 +17,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! # Ethereum pallet +//! +//! The Ethereum pallet works together with EVM pallet to provide full emulation +//! for Ethereum block processing. + // Modified by Alex Wang 2024 //! # Hybrid-vm-port pallet @@ -36,6 +41,8 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; +mod port; + use alloc::{vec, vec::Vec}; use core::marker::PhantomData; pub use ethereum::{ @@ -43,7 +50,7 @@ pub use ethereum::{ TransactionAction, TransactionV2 as Transaction, }; use ethereum_types::{Bloom, BloomInput, H160, H256, H64, U256}; -use evm::{ExitReason, ExitSucceed}; +use evm::ExitReason; use scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; // Substrate @@ -57,34 +64,22 @@ use frame_support::{ use frame_system::{pallet_prelude::OriginFor, CheckWeight, WeightInfo}; use sp_runtime::{ generic::DigestItem, - traits::{ - BlakeTwo256, DispatchInfoOf, Dispatchable, Hash, One, Saturating, UniqueSaturatedInto, Zero, - }, + traits::{DispatchInfoOf, Dispatchable, One, Saturating, UniqueSaturatedInto, Zero}, transaction_validity::{ InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchError, RuntimeDebug, SaturatedConversion, + RuntimeDebug, SaturatedConversion, }; // Frontier use fp_consensus::{PostLog, PreLog, FRONTIER_ENGINE_ID}; pub use fp_ethereum::TransactionData; use fp_ethereum::ValidatedTransaction as ValidatedTransactionT; use fp_evm::{ - CallInfo, CallOrCreateInfo, CheckEvmTransaction, CheckEvmTransactionConfig, - TransactionValidationError, UsedGas, + CallOrCreateInfo, CheckEvmTransaction, CheckEvmTransactionConfig, TransactionValidationError, }; pub use fp_rpc::TransactionStatus; use fp_storage::{EthereumStorageSchema, PALLET_ETHEREUM_SCHEMA}; -use hp_system::U256BalanceMapping; -use ink_env::call::{ExecutionInput, Selector}; -use pallet_contracts::chain_extension::SysConfig; -use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; -use pallet_evm::{AddressMapping, BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; -use pallet_hybrid_vm::UnifiedAddress; - -fn str2s(s: String) -> &'static str { - Box::leak(s.into_boxed_str()) -} +use pallet_evm::{BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum RawOrigin { @@ -122,7 +117,8 @@ impl Call where OriginFor: Into>>, T: Send + Sync + Config, - ::RuntimeCall: Dispatchable, + ::RuntimeCall: + Dispatchable, { pub fn is_self_contained(&self) -> bool { matches!(self, Call::transact { .. }) @@ -147,7 +143,7 @@ where pub fn pre_dispatch_self_contained( &self, origin: &H160, - dispatch_info: &DispatchInfoOf<::RuntimeCall>, + dispatch_info: &DispatchInfoOf<::RuntimeCall>, len: usize, ) -> Option> { if let Call::transact { transaction } = self { @@ -164,7 +160,7 @@ where pub fn validate_self_contained( &self, origin: &H160, - dispatch_info: &DispatchInfoOf<::RuntimeCall>, + dispatch_info: &DispatchInfoOf<::RuntimeCall>, len: usize, ) -> Option { if let Call::transact { transaction } = self { @@ -260,10 +256,10 @@ pub mod pallet { } } // Account for `on_finalize` weight: - // - read: frame_system::Pallet::::digest() - // - read: frame_system::Pallet::::block_number() - // - write: >::store_block() - // - write: >::remove() + // - read: frame_system::Pallet::::digest() + // - read: frame_system::Pallet::::block_number() + // - write: >::store_block() + // - write: >::remove() weight.saturating_add(T::DbWeight::get().reads_writes(2, 2)) } @@ -569,222 +565,10 @@ impl Pallet { builder.build() } - fn is_hybrid_vm_transaction(transaction: Transaction) -> bool { - let action = { - match transaction { - Transaction::Legacy(t) => t.action, - Transaction::EIP2930(t) => t.action, - Transaction::EIP1559(t) => t.action, - } - }; - - match action { - ethereum::TransactionAction::Call(target) => { - if pallet_hybrid_vm::HvmContracts::::contains_key(target) { - return true; - } - }, - _ => {}, - } - - false - } - - fn call_hybrid_vm( - source: H160, - transaction: Transaction, - ) -> Result<(PostDispatchInfo, CallOrCreateInfo), DispatchErrorWithPostInfo> { - let ( - input, - value, - gas_limit, - _max_fee_per_gas, - _max_priority_fee_per_gas, - _nonce, - action, - _access_list, - ) = { - match transaction { - // max_fee_per_gas and max_priority_fee_per_gas in legacy and 2930 transactions is - // the provided gas_price. - Transaction::Legacy(t) => ( - t.input.clone(), - t.value, - t.gas_limit, - Some(t.gas_price), - Some(t.gas_price), - Some(t.nonce), - t.action, - Vec::new(), - ), - Transaction::EIP2930(t) => { - let access_list: Vec<(H160, Vec)> = t - .access_list - .iter() - .map(|item| (item.address, item.storage_keys.clone())) - .collect(); - ( - t.input.clone(), - t.value, - t.gas_limit, - Some(t.gas_price), - Some(t.gas_price), - Some(t.nonce), - t.action, - access_list, - ) - }, - Transaction::EIP1559(t) => { - let access_list: Vec<(H160, Vec)> = t - .access_list - .iter() - .map(|item| (item.address, item.storage_keys.clone())) - .collect(); - ( - t.input.clone(), - t.value, - t.gas_limit, - Some(t.max_fee_per_gas), - Some(t.max_priority_fee_per_gas), - Some(t.nonce), - t.action, - access_list, - ) - }, - } - }; - - match action { - ethereum::TransactionAction::Call(target) => { - let vm_target = pallet_hybrid_vm::Pallet::::hvm_contracts(target); - let target = match vm_target { - Some(UnifiedAddress::::WasmVM(t)) => t, - None => { - return Err(DispatchErrorWithPostInfo { - post_info: PostDispatchInfo { - actual_weight: None, - pays_fee: Pays::Yes, - }, - error: DispatchError::from("Not HybridVM Contract(REVERT)"), - })?; - }, - }; - - let mut a: [u8; 4] = Default::default(); - a.copy_from_slice(&BlakeTwo256::hash(b"evm_abi_call")[0..4]); - let evm_abi_call = ExecutionInput::new(Selector::new(a)).push_arg(input); - - let mut gas_limit_u64 = u64::max_value(); - if gas_limit.lt(&U256::from(gas_limit_u64)) { - gas_limit_u64 = gas_limit.as_u64(); - } - let weight_limit: Weight = - ::GasWeightMapping::gas_to_weight( - gas_limit_u64, - false, - ); - - let origin = ::AddressMapping::into_account_id(source); - let balance_result = - ::U256BalanceMapping::u256_to_balance(value); - let balance = match balance_result { - Ok(t) => t, - Err(_) => { - return Err(DispatchErrorWithPostInfo { - post_info: PostDispatchInfo { - actual_weight: None, - pays_fee: Pays::Yes, - }, - error: DispatchError::from( - "Call HybridVM Contract value is error(REVERT)", - ), - })?; - }, - }; - - let info = pallet_contracts::Pallet::::bare_call( - origin, - target.into(), - balance, - weight_limit, - None, - evm_abi_call.encode(), - DebugInfo::Skip, - CollectEvents::Skip, - Determinism::Enforced, - ); - let output: Vec; - match info.result { - Ok(return_value) => { - if !return_value.did_revert() { - // because return_value.data = MessageResult, so, the first byte is zhe Ok() Code, be removed - output = return_value.data[1..].iter().cloned().collect(); - let call_info = CallInfo { - exit_reason: ExitReason::Succeed(ExitSucceed::Returned), - value: output, - used_gas: UsedGas { - standard: U256::from( - ::GasWeightMapping::weight_to_gas( - info.gas_consumed, - ), - ), - effective: U256::from(0u64), - }, - weight_info: None, - logs: vec![], - }; - return Ok(( - PostDispatchInfo { - actual_weight: Some(info.gas_consumed), - pays_fee: Pays::Yes, - }, - CallOrCreateInfo::Call(call_info), - )); - } else { - let mut return_code = String::from("None"); - if return_value.data.len() > 0 { - return_code = return_value.data[0].to_string(); - } - return Err(DispatchErrorWithPostInfo { - post_info: PostDispatchInfo { - actual_weight: Some(info.gas_consumed), - pays_fee: Pays::Yes, - }, - error: DispatchError::from(str2s( - ["Call wasm contract failed(REVERT):", &return_code].concat(), - )), - }); - } - }, - Err(e) => { - return Err(DispatchErrorWithPostInfo { - post_info: PostDispatchInfo { - actual_weight: None, - pays_fee: Pays::Yes, - }, - error: e, - }); - }, - } - }, - _ => { - return Err(DispatchErrorWithPostInfo { - post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, - error: DispatchError::from("Not HybridVM Contract Call(REVERT)"), - }); - }, - } - } - fn apply_validated_transaction( source: H160, transaction: Transaction, ) -> Result<(PostDispatchInfo, CallOrCreateInfo), DispatchErrorWithPostInfo> { - //firstly, check if it's the ethereum transaction or HybridVM transaction - if Self::is_hybrid_vm_transaction(transaction.clone()) { - return Self::call_hybrid_vm(source, transaction); - } - let (to, _, info) = Self::execute(source, &transaction, None)?; let pending = Pending::::get(); @@ -896,6 +680,11 @@ impl Pallet { } }; + let pay = match Self::is_hybrid_vm_transaction(transaction.clone()) { + true => Pays::Yes, + _ => Pays::No, + }; + Pending::::append((transaction, status, receipt)); Self::deposit_event(Event::Executed { @@ -923,7 +712,7 @@ impl Pallet { } Some(gas_to_weight) }, - pays_fee: Pays::No, + pays_fee: pay, }, info, )) @@ -945,6 +734,10 @@ impl Pallet { let is_transactional = true; let validate = false; + if Self::is_hybrid_vm_transaction(transaction.clone()) { + return Self::call_hybrid_vm(from, transaction_data); + } + let ( input, value, diff --git a/frame/hybrid-vm-port/src/mock.rs b/frame/hybrid-vm-port/src/mock.rs index e61fdcb..841a2cc 100644 --- a/frame/hybrid-vm-port/src/mock.rs +++ b/frame/hybrid-vm-port/src/mock.rs @@ -30,12 +30,12 @@ use frame_support::{ weights::Weight, ConsensusEngineId, PalletId, }; -use sp_core::crypto::UncheckedFrom; -use sp_core::{hashing::keccak_256, ConstBool, H160, H256, U256}; + +use sp_core::{crypto::UncheckedFrom, hashing::keccak_256, ConstBool, H160, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, Convert, Dispatchable, IdentityLookup}, - AccountId32, BuildStorage, Perbill, + AccountId32, BuildStorage, DispatchError, Perbill, }; // Frontier use pallet_evm::{AddressMapping, BalanceOf, EnsureAddressTruncated, FeeCalculator}; @@ -45,7 +45,7 @@ use pallet_contracts::chain_extension::{Environment, Ext, InitState, RetVal}; // HybridVM use byte_slice_cast::AsByteSlice; -use hp_system::{AccountId32Mapping, AccountIdMapping, U256BalanceMapping}; +use hp_system::{AccountIdMapping, U256BalanceMapping}; use super::*; use crate::IntermediateStateRoot; @@ -54,6 +54,8 @@ pub type SignedExtra = (frame_system::CheckSpecVersion,); type Balance = u128; +type AccountId = AccountId32; + frame_support::construct_runtime! { pub enum Test { System: frame_system::{Pallet, Call, Config, Storage, Event}, @@ -85,7 +87,7 @@ impl frame_system::Config for Test { type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = AccountId32; + type AccountId = AccountId; type Lookup = IdentityLookup; type Block = frame_system::mocking::MockBlock; type BlockHashCount = BlockHashCount; @@ -150,21 +152,21 @@ parameter_types! { } pub struct HashedAddressMapping; -impl AddressMapping for HashedAddressMapping { - fn into_account_id(address: H160) -> AccountId32 { +impl AddressMapping for HashedAddressMapping { + fn into_account_id(address: H160) -> AccountId { let mut data = [0u8; 32]; data[0..20].copy_from_slice(&address[..]); - AccountId32::from(Into::<[u8; 32]>::into(data)) + AccountId::from(Into::<[u8; 32]>::into(data)) } } pub struct CompactAddressMapping; -impl AddressMapping for CompactAddressMapping { - fn into_account_id(address: H160) -> AccountId32 { +impl AddressMapping for CompactAddressMapping { + fn into_account_id(address: H160) -> AccountId { let mut data = [0u8; 32]; data[0..20].copy_from_slice(&address[..]); - AccountId32::from(data) + AccountId::from(data) } } @@ -209,7 +211,8 @@ impl pallet_contracts::chain_extension::ChainExtension for HybridVMChainEx fn call(&mut self, env: Environment) -> Result where E: Ext, - ::AccountId: UncheckedFrom<::Hash> + AsRef<[u8]>, + ::AccountId: + UncheckedFrom<::Hash> + AsRef<[u8]>, { let func_id = env.func_id(); match func_id { @@ -270,10 +273,10 @@ parameter_types! { } pub struct EnsureAccount(sp_std::marker::PhantomData<(T, A)>); -impl>> +impl>> EnsureOrigin<::RuntimeOrigin> for EnsureAccount where - ::AccountId: From, + ::AccountId: From, { type Success = T::AccountId; @@ -332,17 +335,9 @@ impl pallet_contracts::Config for Test { type Xcm = (); } -pub struct GasPrice; -impl Get> for GasPrice { - fn get() -> Option { - Some(U256::from(100_000_000_000u64)) - } -} - parameter_types! { pub const EnableCallEVM: bool = true; pub const EnableCallWasmVM: bool = true; - pub const GasLimit: u64 = 10_000_000u64; } impl U256BalanceMapping for Test { @@ -361,26 +356,13 @@ impl AccountIdMapping for Test { } } -impl AccountId32Mapping for Test { - fn id32_to_id(id32: AccountId32) -> ::AccountId { - id32.into() - } - - fn id_to_id32(account_id: ::AccountId) -> AccountId32 { - account_id.into() - } -} - impl pallet_hybrid_vm::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type U256BalanceMapping = Self; type AccountIdMapping = Self; - type AccountId32Mapping = Self; type EnableCallEVM = EnableCallEVM; type EnableCallWasmVM = EnableCallWasmVM; - type GasLimit = GasLimit; - type GasPrice = GasPrice; } parameter_types! { @@ -452,7 +434,7 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall { pub struct AccountInfo { pub address: H160, - pub account_id: AccountId32, + pub account_id: AccountId, pub private_key: H256, } @@ -465,11 +447,7 @@ fn address_build(seed: u8) -> AccountInfo { let mut data = [0u8; 32]; data[0..20].copy_from_slice(&address[..]); - AccountInfo { - private_key, - account_id: AccountId32::from(Into::<[u8; 32]>::into(data)), - address, - } + AccountInfo { private_key, account_id: AccountId::from(Into::<[u8; 32]>::into(data)), address } } // This function basically just builds a genesis storage key/value store according to diff --git a/frame/hybrid-vm-port/src/port.rs b/frame/hybrid-vm-port/src/port.rs new file mode 100644 index 0000000..8235dcd --- /dev/null +++ b/frame/hybrid-vm-port/src/port.rs @@ -0,0 +1,436 @@ +// Copyright (C) HybridVM. +// This file is part of HybridVM. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; + +use fp_evm::{CallInfo, ExitSucceed, UsedGas}; +use hp_system::{AccountIdMapping, U256BalanceMapping}; +use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; +use pallet_evm::{AddressMapping, ExitError}; +use pallet_hybrid_vm::UnifiedAddress; +use precompile_utils::{ + prelude::*, + solidity::codec::{ + bytes::{BoundedBytesString, BytesKind, StringKind}, + Reader, Writer, + }, +}; +use sp_runtime::{traits::ConstU32, DispatchError}; + +fn str2s(s: String) -> &'static str { + Box::leak(s.into_boxed_str()) +} + +macro_rules! b { + ($data_type:ident, $winput:ident, $reader:ident, $err_data:ident, $($s:expr, $t:ty)*) => { + $( + if $data_type == $s { + <$t>::read(&mut $reader).map_err(|_| $err_data)?.encode_to(&mut $winput); + continue; + } + )* + } +} + +macro_rules! c { + ($data_type:ident, $writer:ident, $woutput:ident, $($s:expr, $t:ty)*) => { + $( + if $data_type == $s { + $writer = $writer.write(<$t as Decode>::decode(&mut $woutput).map_err(|_| 2)?); + continue; + } + )* + } +} +impl Pallet { + pub fn is_hybrid_vm_transaction(transaction: Transaction) -> bool { + let action = { + match transaction { + Transaction::Legacy(t) => t.action, + Transaction::EIP2930(t) => t.action, + Transaction::EIP1559(t) => t.action, + } + }; + + match action { + ethereum::TransactionAction::Call(target) => { + if pallet_hybrid_vm::HvmContracts::::contains_key(target) { + return true; + } + }, + _ => {}, + } + + false + } + + fn build_wasm_call<'a>( + input: &Vec, + abi0: [u8; 4], + abi_fun: &'a str, + ) -> Result<(Vec, Vec<&'a str>), DispatchErrorWithPostInfo> { + let err = DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, + error: DispatchError::from("Fun abi error(REVERT)"), + }; + + let err_data = DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, + error: DispatchError::from("Decode input data error(REVERT)"), + }; + + let index_s1 = abi_fun.find('(').ok_or(err)?; + let index_e1 = abi_fun.find(')').ok_or(err)?; + + let mut index_s2: usize = 0; + let mut index_e2: usize = 1; + + if abi_fun.len() > index_e1 { + if abi_fun[index_e1 + 1..].find('(').is_some() { + index_s2 = abi_fun[index_e1 + 1..].find('(').ok_or(err)? + index_e1 + 1; + index_e2 = abi_fun[index_e1 + 1..].find(')').ok_or(err)? + index_e1 + 1; + } + } + + if index_e1 < index_s1 || index_e2 < index_s2 { + return Err(err); + } + + let input_type: Vec<&str> = abi_fun[index_s1 + 1..index_e1].split(',').collect(); + let output_type: Vec<&str> = abi_fun[index_s2 + 1..index_e2].split(',').collect(); + + // wasm type: AccountId,bool,Vec,H256,String,u8,u16,u32,u64,u128,U256,Vec + let data_type = "address,bool,bytes,bytes32,string,uint8,uint16,uint32,uint64,uint128,uint256,address[],bool[],bytes32[],uint8[],uint16[],uint32[],uint64[],uint128[],uint256[]"; + + if input_type.iter().find(|&x| data_type.find(x).is_none()).is_some() { + return Err(err); + } + if output_type.iter().find(|&x| data_type.find(x).is_none()).is_some() { + return Err(err); + } + + let mut winput: Vec = vec![]; + abi0.encode_to(&mut winput); + + let mut reader = Reader::new_skip_selector(input).map_err(|_| err_data)?; + + for data_type in input_type { + if data_type == "address" { + let address = Address::read(&mut reader).map_err(|_| err_data)?; + let account_id = + ::AddressMapping::into_account_id(address.into()); + account_id.encode_to(&mut winput); + continue; + } else if data_type == "address[]" { + let addresses = Vec::
::read(&mut reader).map_err(|_| err_data)?; + let account_ids: Vec<_> = addresses + .iter() + .map(|&x| ::AddressMapping::into_account_id(x.into())) + .collect(); + account_ids.encode_to(&mut winput); + continue; + } else if data_type == "bytes" { + Vec::::from( + BoundedBytesString::>::read(&mut reader) + .map_err(|_| err_data)?, + ) + .encode_to(&mut winput); + continue; + } else if data_type == "string" { + String::try_from( + BoundedBytesString::>::read(&mut reader) + .map_err(|_| err_data)?, + ) + .map_err(|_| err_data)? + .encode_to(&mut winput); + continue; + } + + b!(data_type, winput, reader, err_data, "bool", bool "bytes32", H256 "uint8", u8 "uint16", u16 "uint32", u32 "uint64", u64 "uint128", u128 "uint256", U256); + b!(data_type, winput, reader, err_data, "bool[]", bool "bytes32[]", H256 "uint8[]", u8 "uint16[]", u16 "uint32[]", u32 "uint64[]", u64 "uint128[]", u128 "uint256[]", U256); + + return Err(err_data); + } + + Ok((winput, output_type)) + } + + fn convert_wasm_return(wasm_output: Vec, output_type: Vec<&str>) -> Result, u8> { + let mut woutput = wasm_output.as_slice(); + _ = as Decode>::decode(&mut woutput).map_err(|_| 1)?; + + let mut writer = Writer::new(); + for data_type in output_type { + if data_type == "address" { + let account_id = + <::AccountId as Decode>::decode(&mut woutput) + .map_err(|_| 3)?; + // address: H160 + let address = + ::AccountIdMapping::into_address(account_id); + writer = writer.write(Address::from(address)); + continue; + } else if data_type == "address[]" { + let account_ids = + ::AccountId> as Decode>::decode(&mut woutput) + .map_err(|_| 4)?; + for account_id in account_ids { + let address = + ::AccountIdMapping::into_address(account_id); + writer = writer.write(Address::from(address)); + } + continue; + } else if data_type == "bytes" { + let bounded_bytes = BoundedBytesString::>::from( + as Decode>::decode(&mut woutput).map_err(|_| 5)?, + ); + writer = writer.write(bounded_bytes); + continue; + } else if data_type == "string" { + let bounded_string = + BoundedBytesString::>::try_from( + ::decode(&mut woutput).map_err(|_| 5)?, + ) + .map_err(|_| 5)?; + writer = writer.write(bounded_string); + continue; + } + + c!(data_type, writer, woutput, "bool", bool "bytes32", H256 "uint8", u8 "uint16", u16 "uint32", u32 "uint64", u64 "uint128", u128 "uint256", U256); + c!(data_type, writer, woutput, "bool[]", bool "bytes32[]", H256 "uint8[]", u8 "uint16[]", u16 "uint32[]", u32 "uint64[]", u64 "uint128[]", u128 "uint256[]", U256); + + return Err(6); + } + + Ok(writer.build()) + } + + pub fn call_hybrid_vm( + source: H160, + t: TransactionData, + ) -> Result<(Option, Option, CallOrCreateInfo), DispatchErrorWithPostInfo> { + let to = match t.action { + ethereum::TransactionAction::Call(target) => Some(target), + _ => None, + }; + let base_weight = Weight::from_parts(10_000, 0); + + Ok(Self::exec_hybrid_vm(source, t).unwrap_or_else(|e| { + let used_gas = U256::from(::GasWeightMapping::weight_to_gas( + e.post_info.actual_weight.unwrap_or(base_weight), + )); + let error_str: &'static str = e.error.into(); + let reason = match e.error { + t if t == DispatchError::from(pallet_contracts::Error::::OutOfGas) => { + ExitReason::Error(ExitError::OutOfGas) + }, + _ => ExitReason::Error(ExitError::Other(std::borrow::Cow::Borrowed(error_str))), + }; + let call_info = CallInfo { + exit_reason: reason, + value: vec![], + used_gas: UsedGas { standard: used_gas, effective: used_gas }, + weight_info: None, + logs: vec![], + }; + (to, None, fp_evm::CallOrCreateInfo::Call(call_info)) + })) + } + + pub fn exec_hybrid_vm( + source: H160, + t: TransactionData, + ) -> Result<(Option, Option, CallOrCreateInfo), DispatchErrorWithPostInfo> { + let ( + input, + value, + gas_limit, + _max_fee_per_gas, + _max_priority_fee_per_gas, + _nonce, + action, + _access_list, + ) = ( + t.input, + t.value, + t.gas_limit, + t.max_fee_per_gas, + t.max_priority_fee_per_gas, + t.nonce, + t.action, + t.access_list, + ); + + let base_weight = Some(Weight::from_parts(10_000, 0)); + + match action { + ethereum::TransactionAction::Call(target) => { + let vm_target = pallet_hybrid_vm::Pallet::::hvm_contracts(target); + match vm_target { + Some(UnifiedAddress::::WasmVM(t)) => { + let selector = if input.len() < 4 { + 0 + } else { + let mut a: [u8; 4] = Default::default(); + a.copy_from_slice(&input[0..4]); + u32::from_be_bytes(a) + }; + + let abi = pallet_hybrid_vm::Pallet::::evm_fun_abi(target, selector) + .ok_or(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: base_weight, + pays_fee: Pays::Yes, + }, + error: DispatchError::from("No abi of the selector(REVERT)"), + })?; + + let err = DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: base_weight, + pays_fee: Pays::Yes, + }, + error: DispatchError::from("Fun abi error(REVERT)"), + }; + let abi_fun = String::from_utf8(abi.1.to_vec()).map_err(|_| err)?; + let (wasm_call, output_type) = + Self::build_wasm_call(&input, abi.0, &abi_fun)?; + + let mut gas_limit_u64 = u64::max_value(); + if gas_limit.lt(&U256::from(gas_limit_u64)) { + gas_limit_u64 = gas_limit.as_u64(); + } + let weight_limit: Weight = + ::GasWeightMapping::gas_to_weight( + gas_limit_u64, + false, + ); + + let origin = + ::AddressMapping::into_account_id(source); + let balance_result = + ::U256BalanceMapping::u256_to_balance( + value, + ); + let balance = match balance_result { + Ok(t) => t, + Err(_) => { + return Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: base_weight, + pays_fee: Pays::Yes, + }, + error: DispatchError::from( + "Call HybridVM Contract value is error(REVERT)", + ), + }) + }, + }; + + let info = pallet_contracts::Pallet::::bare_call( + origin, + t.into(), + balance, + weight_limit, + None, + wasm_call, + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + + match info.result { + Ok(return_value) => { + if !return_value.did_revert() { + // because return_value.data = MessageResult, so, the + // first byte is zhe Ok() Code, be removed + let err_data = DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(info.gas_consumed), + pays_fee: Pays::Yes, + }, + error: DispatchError::from("Return data decode error!"), + }; + let output = + Self::convert_wasm_return(return_value.data, output_type) + .map_err(|_| err_data)?; + let used_gas = U256::from( + ::GasWeightMapping::weight_to_gas( + info.gas_consumed, + ), + ); + let call_info = CallInfo { + exit_reason: ExitReason::Succeed(ExitSucceed::Returned), + value: output, + used_gas: UsedGas { + standard: used_gas, + effective: used_gas, + }, + weight_info: None, + logs: vec![], + }; + return Ok(( + Some(target), + None, + CallOrCreateInfo::Call(call_info), + )); + } else { + let mut return_code = String::from("None"); + if return_value.data.len() > 1 { + return_code = return_value.data[1].to_string(); + } + return Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(info.gas_consumed), + pays_fee: Pays::Yes, + }, + error: DispatchError::from(str2s( + ["Call wasm contract failed(REVERT):", &return_code] + .concat(), + )), + }); + } + }, + Err(e) => { + return Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: base_weight, + pays_fee: Pays::Yes, + }, + error: e, + }); + }, + } + }, + None => { + return Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: base_weight, + pays_fee: Pays::Yes, + }, + error: DispatchError::from("Not HybridVM Contract(REVERT)"), + }); + }, + }; + }, + _ => { + return Err(DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { actual_weight: base_weight, pays_fee: Pays::Yes }, + error: DispatchError::from("Not HybridVM Contract Call(REVERT)"), + }); + }, + } + } +} diff --git a/frame/hybrid-vm-port/src/tests/eip1559.rs b/frame/hybrid-vm-port/src/tests/eip1559.rs index 82126ed..e09d6df 100644 --- a/frame/hybrid-vm-port/src/tests/eip1559.rs +++ b/frame/hybrid-vm-port/src/tests/eip1559.rs @@ -624,7 +624,7 @@ fn call_hybrid_vm_works() { ::AddressMapping::into_account_id(alice.address); let substrate_bob = ::AddressMapping::into_account_id(bob.address); - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); ext.execute_with(|| { @@ -655,14 +655,14 @@ fn call_hybrid_vm_works() { let wasm_addr = Contracts::contract_address(&substrate_alice, &wasm_code_hash, &new_call.encode(), &[]); - //3. regist contract + // 3. regist contract assert_ok!(HybridVM::regist_contract( RuntimeOrigin::signed(substrate_alice.clone()), UnifiedAddress::::WasmVM(wasm_addr.clone()), )); - //4. Transfer Token to substrate_bob + // 4. Transfer Token to substrate_bob let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"transfer")[0..4]); let transfer_call = ExecutionInput::new(Selector::new(a)); @@ -685,7 +685,7 @@ fn call_hybrid_vm_works() { .unwrap(); assert!(!result.did_revert()); - //5. Get substrate_bob balance of wasm token + // 5. Get substrate_bob balance of wasm token let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"balance_of")[0..4]); let balance_of_call = ExecutionInput::new(Selector::new(a)); @@ -711,7 +711,7 @@ fn call_hybrid_vm_works() { println!("result data before:{:?}", result); let bob_balance_before = result.data; - //6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob + // 6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob let wasm_contract = ::AccountIdMapping::into_address(wasm_addr.clone()); let transfer_selector = &Keccak256::digest(b"transfer(address,uint256)")[0..4]; @@ -738,7 +738,7 @@ fn call_hybrid_vm_works() { let result = Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), t); assert_ok!(result); - //7. Get bob balance of wasm token + // 7. Get bob balance of wasm token let result = Contracts::bare_call( substrate_bob.clone(), wasm_addr.clone(), @@ -757,7 +757,7 @@ fn call_hybrid_vm_works() { println!("result data after:{:?}", result); let bob_balance_after = result.data; - //8. Test the balance of bob being correct + // 8. Test the balance of bob being correct let after = as Decode>::decode(&mut &bob_balance_after[..]) .unwrap() .unwrap(); diff --git a/frame/hybrid-vm-port/src/tests/eip2930.rs b/frame/hybrid-vm-port/src/tests/eip2930.rs index 241c054..72bc2fe 100644 --- a/frame/hybrid-vm-port/src/tests/eip2930.rs +++ b/frame/hybrid-vm-port/src/tests/eip2930.rs @@ -539,7 +539,7 @@ fn call_hybrid_vm_works() { ::AddressMapping::into_account_id(alice.address); let substrate_bob = ::AddressMapping::into_account_id(bob.address); - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); ext.execute_with(|| { @@ -570,14 +570,14 @@ fn call_hybrid_vm_works() { let wasm_addr = Contracts::contract_address(&substrate_alice, &wasm_code_hash, &new_call.encode(), &[]); - //3. regist contract + // 3. regist contract assert_ok!(HybridVM::regist_contract( RuntimeOrigin::signed(substrate_alice.clone()), UnifiedAddress::::WasmVM(wasm_addr.clone()), )); - //4. Transfer Token to substrate_bob + // 4. Transfer Token to substrate_bob let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"transfer")[0..4]); let transfer_call = ExecutionInput::new(Selector::new(a)); @@ -600,7 +600,7 @@ fn call_hybrid_vm_works() { .unwrap(); assert!(!result.did_revert()); - //5. Get substrate_bob balance of wasm token + // 5. Get substrate_bob balance of wasm token let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"balance_of")[0..4]); let balance_of_call = ExecutionInput::new(Selector::new(a)); @@ -626,7 +626,7 @@ fn call_hybrid_vm_works() { println!("result data before:{:?}", result); let bob_balance_before = result.data; - //6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob + // 6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob let wasm_contract = ::AccountIdMapping::into_address(wasm_addr.clone()); let transfer_selector = &Keccak256::digest(b"transfer(address,uint256)")[0..4]; @@ -653,7 +653,7 @@ fn call_hybrid_vm_works() { let result = Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), t); assert_ok!(result); - //7. Get bob balance of wasm token + // 7. Get bob balance of wasm token let result = Contracts::bare_call( substrate_bob.clone(), wasm_addr.clone(), @@ -672,7 +672,7 @@ fn call_hybrid_vm_works() { println!("result data after:{:?}", result); let bob_balance_after = result.data; - //8. Test the balance of bob being correct + // 8. Test the balance of bob being correct let after = as Decode>::decode(&mut &bob_balance_after[..]) .unwrap() .unwrap(); diff --git a/frame/hybrid-vm-port/src/tests/legacy.rs b/frame/hybrid-vm-port/src/tests/legacy.rs index 08fe853..1635801 100644 --- a/frame/hybrid-vm-port/src/tests/legacy.rs +++ b/frame/hybrid-vm-port/src/tests/legacy.rs @@ -535,7 +535,7 @@ fn call_hybrid_vm_works() { ::AddressMapping::into_account_id(alice.address); let substrate_bob = ::AddressMapping::into_account_id(bob.address); - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); ext.execute_with(|| { @@ -566,14 +566,14 @@ fn call_hybrid_vm_works() { let wasm_addr = Contracts::contract_address(&substrate_alice, &wasm_code_hash, &new_call.encode(), &[]); - //3. regist contract + // 3. regist contract assert_ok!(HybridVM::regist_contract( RuntimeOrigin::signed(substrate_alice.clone()), UnifiedAddress::::WasmVM(wasm_addr.clone()), )); - //4. Transfer Token to substrate_bob + // 4. Transfer Token to substrate_bob let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"transfer")[0..4]); let transfer_call = ExecutionInput::new(Selector::new(a)); @@ -596,7 +596,7 @@ fn call_hybrid_vm_works() { .unwrap(); assert!(!result.did_revert()); - //5. Get substrate_bob balance of wasm token + // 5. Get substrate_bob balance of wasm token let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"balance_of")[0..4]); let balance_of_call = ExecutionInput::new(Selector::new(a)); @@ -622,7 +622,7 @@ fn call_hybrid_vm_works() { println!("result data before:{:?}", result); let bob_balance_before = result.data; - //6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob + // 6. Ethereum call hybrid vm (wasm contract) transfer wasm token to bob let wasm_contract = ::AccountIdMapping::into_address(wasm_addr.clone()); let transfer_selector = &Keccak256::digest(b"transfer(address,uint256)")[0..4]; @@ -649,7 +649,7 @@ fn call_hybrid_vm_works() { let result = Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), t); assert_ok!(result); - //7. Get bob balance of wasm token + // 7. Get bob balance of wasm token let result = Contracts::bare_call( substrate_bob.clone(), wasm_addr.clone(), @@ -668,7 +668,7 @@ fn call_hybrid_vm_works() { println!("result data after:{:?}", result); let bob_balance_after = result.data; - //8. Test the balance of bob being correct + // 8. Test the balance of bob being correct let after = as Decode>::decode(&mut &bob_balance_after[..]) .unwrap() .unwrap(); diff --git a/frame/hybrid-vm-port/src/tests/mod.rs b/frame/hybrid-vm-port/src/tests/mod.rs index 099e323..b3ba585 100644 --- a/frame/hybrid-vm-port/src/tests/mod.rs +++ b/frame/hybrid-vm-port/src/tests/mod.rs @@ -34,11 +34,11 @@ use crate::{ }; use fp_self_contained::CheckedExtrinsic; -use sp_runtime::codec::{Decode, Encode}; -use sp_runtime::traits::{BlakeTwo256, Hash}; -use std::error::Error; -use std::fs::File; -use std::io::Read; +use sp_runtime::{ + codec::{Decode, Encode}, + traits::{BlakeTwo256, Hash}, +}; +use std::{error::Error, fs::File, io::Read}; mod eip1559; mod eip2930; diff --git a/frame/hybrid-vm/Cargo.toml b/frame/hybrid-vm/Cargo.toml index 6a7d17f..f46c45a 100644 --- a/frame/hybrid-vm/Cargo.toml +++ b/frame/hybrid-vm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-hybrid-vm" -version = "0.3.0" +version = "0.4.0" description = "Engine for Hybrid vm" repository.workspace = true edition.workspace = true @@ -36,6 +36,9 @@ pallet-contracts = { workspace = true, default-features = false} pallet-evm = { workspace = true, default-features = false} fp-evm = { workspace = true, default-features = false} +#ink +ink_env = { workspace = true, features = ["no-panic-handler"] } + # hybrid vm hp-system = { workspace = true, default-features = false } @@ -53,8 +56,6 @@ pallet-transaction-payment = { workspace = true, default-features = false} pallet-evm-precompile-simple = { workspace = true, default-features = false} -ink_env = { workspace = true } - pallet-evm-precompile-call-hybrid-vm = { workspace = true } [features] @@ -82,4 +83,17 @@ std = [ "pallet-contracts/std", "pallet-evm/std", "fp-evm/std", + "ink_env/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-evm/runtime-benchmarks", + "pallet-contracts/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-evm/try-runtime", + "pallet-contracts/try-runtime", +] \ No newline at end of file diff --git a/frame/hybrid-vm/fixtures/erc20.wasm b/frame/hybrid-vm/fixtures/erc20.wasm index 8ca1d85..c2f7835 100644 Binary files a/frame/hybrid-vm/fixtures/erc20.wasm and b/frame/hybrid-vm/fixtures/erc20.wasm differ diff --git a/frame/hybrid-vm/src/interoperate/mod.rs b/frame/hybrid-vm/src/interoperate/mod.rs index 52fa02c..9cdb105 100644 --- a/frame/hybrid-vm/src/interoperate/mod.rs +++ b/frame/hybrid-vm/src/interoperate/mod.rs @@ -25,20 +25,21 @@ use alloc::string::{String, ToString}; use codec::{Decode, Encode}; use core::fmt; -use sp_std::vec; -use sp_std::vec::Vec; -use sp_std::{prelude::*, str}; +use sp_std::{prelude::*, str, vec, vec::Vec}; -use pallet_contracts::chain_extension::{Environment, Ext, InitState, RetVal}; -use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; -use sp_runtime::app_crypto::sp_core::{H160, U256}; -use sp_runtime::DispatchError; +use pallet_contracts::{ + chain_extension::{Environment, Ext, InitState, RetVal}, + CollectEvents, DebugInfo, Determinism, +}; +use sp_runtime::{ + app_crypto::sp_core::{H160, U256}, + DispatchError, +}; use serde::{Deserialize, Serialize}; -use fp_evm::ExecutionInfoV2; -use frame_support::sp_runtime::AccountId32; -use pallet_evm::Runner; +use fp_evm::{ExecutionInfoV2, FeeCalculator}; +use pallet_evm::{GasWeightMapping, Runner}; use super::*; use frame_support::pallet_prelude::*; @@ -81,21 +82,22 @@ impl InterCall { target_gas: Weight, ) -> Result<(Vec, Weight)> { if !T::EnableCallWasmVM::get() { - return Err(DispatchError::from("EnableCallWasmVM is false, can't call wasm VM.")); + return Err(DispatchError::from(Error::::DisableCallWasmVM)); } let input: Vec; - let target: AccountId32; + let target: Vec; match vm_codec::wasm_encode(&data[32..].iter().cloned().collect()) { Ok(r) => (input, target) = r, - Err(e) => return Err(DispatchError::from(str2s(e.to_string()))), + Err(_) => return Err(DispatchError::from(Error::::WasmEncodeError)), } let gas_limit: Weight = target_gas; let origin = ensure_signed(origin)?; - let target = T::AccountId32Mapping::id32_to_id(target); + let target = ::AccountId::decode(&mut target.as_slice()) + .map_err(|_| DispatchError::from(Error::::AccountIdDecodeError))?; let info = pallet_contracts::Pallet::::bare_call( origin, @@ -112,7 +114,8 @@ impl InterCall { match info.result { Ok(return_value) => { if !return_value.did_revert() { - // because return_value.data = MessageResult, so, the first byte is zhe Ok() Code, be removed + // because return_value.data = MessageResult, so, the first byte is zhe + // Ok() Code, be removed output = vm_codec::wasm_decode( &data[32..].iter().cloned().collect(), &return_value.data[1..].iter().cloned().collect(), @@ -120,7 +123,7 @@ impl InterCall { "", ); } else { - return Err(DispatchError::from("Call wasm contract failed(REVERT)")); + return Err(DispatchError::from(Error::::WasmContractRevert)); } }, Err(e) => return Err(e), @@ -128,7 +131,7 @@ impl InterCall { match output { Ok(r) => return Ok((r, info.gas_consumed)), - Err(e) => return Err(DispatchError::from(str2s(e.to_string()))), + Err(_) => return Err(DispatchError::from(Error::::WasmDecodeError)), } } } @@ -136,9 +139,12 @@ impl InterCall { impl InterCall { pub fn call_evm>(mut env: Environment) -> Result { if !C::EnableCallEVM::get() { - return Err(DispatchError::from("EnableCallEVM is false, can't call evm.")); + return Err(DispatchError::from(Error::::DisableCallEvm)); } + let gas_meter = env.ext().gas_meter(); + let gas_limit = + ::GasWeightMapping::weight_to_gas(gas_meter.gas_left()); let caller = env.ext().caller(); let source = C::AccountIdMapping::into_address(caller.account_id()?.clone()); @@ -150,18 +156,19 @@ impl InterCall { match vm_codec::evm_encode(&input0) { Ok(r) => (input, target) = r, - Err(e) => { - return Err(DispatchError::from(str2s(e.to_string()))); + Err(_) => { + return Err(DispatchError::from(Error::::EvmEncodeError)); }, } + let gas_price = ::FeeCalculator::min_gas_price(); let info = ::Runner::call( source, target, input, U256::default(), - C::GasLimit::get(), - C::GasPrice::get(), + gas_limit, + Some(gas_price.0), None, Some(pallet_evm::Pallet::::account_basic(&source).0.nonce), Vec::new(), @@ -174,14 +181,16 @@ impl InterCall { let output: ResultBox>; match info { - Ok(r) => match r { - ExecutionInfoV2 { exit_reason: success, value: v1, .. } => { - if success.is_succeed() { - output = vm_codec::evm_decode(&input0, &v1, true, ""); - } else { - return Err(DispatchError::from("Call EVM failed ")); - } - }, + Ok(r) => { + match r { + ExecutionInfoV2 { exit_reason: success, value: v1, .. } => { + if success.is_succeed() { + output = vm_codec::evm_decode(&input0, &v1, true, ""); + } else { + return Err(DispatchError::from(Error::::EVMExecuteFailed)); + } + }, + }; }, Err(e) => { return Err(DispatchError::from(e.error.into())); @@ -192,13 +201,13 @@ impl InterCall { Ok(r) => { let output = envbuf .write(&r, false, None) - .map_err(|_| DispatchError::from("ChainExtension failed to write result")); + .map_err(|_| DispatchError::from(Error::::ChainExtensionWriteError)); match output { Ok(_) => return Ok(RetVal::Converging(0)), Err(e) => return Err(e), } }, - Err(e) => return Err(DispatchError::from(str2s(e.to_string()))), + Err(_) => return Err(DispatchError::from(Error::::EvmDecodeError)), } } } @@ -234,13 +243,11 @@ impl fmt::Display for CustomError { pub mod vm_codec { use super::*; - use codec::Compact; - use codec::Encode; + use codec::{Compact, Encode}; use core::mem::size_of; use sha3::{Digest, Keccak256}; - use sp_runtime::{traits::BlakeTwo256, AccountId32}; - use sp_std::convert::TryInto; - use sp_std::str::FromStr; + use sp_runtime::traits::BlakeTwo256; + use sp_std::{convert::TryInto, str::FromStr}; type Result = sp_std::result::Result; @@ -289,10 +296,10 @@ pub mod vm_codec { let mut data_ex: Vec = Vec::new(); let mut i: usize = 0; - // 256 bit for per fix parameter, dyn parameter occupy 256bit offset value, and value add after all fix paramter - // dyn parameter in offset one 256bit length value, and after real value - // uint int using big endian, and patch 0 in high bit. else for address byte patch 0 in low bit. - // array's inputValue: len, v1,v2, ..., vlen. + // 256 bit for per fix parameter, dyn parameter occupy 256bit offset value, and value add + // after all fix paramter dyn parameter in offset one 256bit length value, and after real + // value uint int using big endian, and patch 0 in high bit. else for address byte patch + // 0 in low bit. array's inputValue: len, v1,v2, ..., vlen. for p in call_vm.InputType { let value = call_vm.InputValue.get(i).ok_or(CustomError::new("Data number error"))?; let mut value_data: Vec = Vec::new(); @@ -617,21 +624,18 @@ pub mod vm_codec { Ok(String::encode(&return_json)) } - pub fn wasm_encode(input: &Vec) -> Result<(Vec, AccountId32)> { + pub fn wasm_encode(input: &Vec) -> Result<(Vec, Vec)> { let call_vm: CallVM = t!(serde_json::from_slice(input.as_slice())); let account = call_vm.Account; - let mut bytes = [0u8; 32]; - let target = t!(hex::decode_to_slice(&account[2..], &mut bytes) - .map_err(|_| "invalid hex address.") - .map(|_| AccountId32::from(bytes))); + let target = t!(hex::decode(&account[2..]).map_err(|_| "invalid hex address.")); let selector = &::hash(call_vm.Fun.as_bytes())[0..4]; let mut data: Vec = Vec::new(); let mut i: usize = 0; - // scale codec LE: fixlength per fixed-width parameter, dyn parameter: prefixed with a compact encoding of the number of items - // compact integer with compact encoding: 00--one byte 01--two bytes 10--four bytes 11--big number - // The upper six bits are the number of bytes following - // list inputValue: Vector u8 u8 u8 "3", "12","34","56" + // scale codec LE: fixlength per fixed-width parameter, dyn parameter: prefixed with a + // compact encoding of the number of items compact integer with compact encoding: 00--one + // byte 01--two bytes 10--four bytes 11--big number The upper six bits are the + // number of bytes following list inputValue: Vector u8 u8 u8 "3", "12","34","56" for p in call_vm.InputType { let value = call_vm.InputValue.get(i).ok_or(CustomError::new("Data number error"))?; let mut value_data: Vec = Vec::new(); @@ -649,7 +653,9 @@ pub mod vm_codec { //"f32" => value_data.append(&mut to_scale::(&value)), //"f64" => value_data.append(&mut to_scale::(&value)), "bool" => value_data.append(&mut to_scale::(&value)), // false: 00 true: 01 - "enum" => value_data.append(&mut to_scale::(&value)), //Option Result are enum: None 00 Some 01 Ok 00 Err 01 + "enum" => value_data.append(&mut to_scale::(&value)), /* Option Result are + * enum: None 00 Some + * 01 Ok 00 Err 01 */ //Option : None 00 Some true 01 Some false 02 "char" => { let c = value.chars().next().ok_or(CustomError::new("Char value error"))?; @@ -687,9 +693,11 @@ pub mod vm_codec { ret } - //number 0 Vec , when it has Vector or Enum type, then the number 0+1 Vec it set number x Vec, Vec has detail info and Enum has index nmber x of the second Vec means 00:vec 01:vec - //example Vec [ ... "10", ...] 10th ["u8","string"...] "0" means none - //example Enum [ ... "13", ...] 13th ["16","17","18"] 16th ["u8","string"] "0" means none index is the index position's type + //number 0 Vec , when it has Vector or Enum type, then the number 0+1 Vec it + // set number x Vec, Vec has detail info and Enum has index nmber x of the second + // Vec means 00:vec 01:vec example Vec [ ... "10", ...] 10th ["u8","string"...] + // "0" means none example Enum [ ... "13", ...] 13th ["16","17","18"] 16th + // ["u8","string"] "0" means none index is the index position's type pub fn wasm_decode( input: &Vec, output: &Vec, @@ -798,7 +806,8 @@ pub mod vm_codec { } } }, - //Option Result are enum: None 00 Some 01, Ok00 Err01; Option : None 00 Some true 01 Some false 02 + //Option Result are enum: None 00 Some 01, Ok00 Err01; Option : None 00 + // Some true 01 Some false 02 "enum" => { let a = output[*offset] as usize; call_return.ReturnValue.push(to_string_value::(&output, offset)); @@ -876,7 +885,8 @@ pub mod vm_codec { let val_bytes = [a, output[*offset + 1], output[*offset + 2], output[*offset + 3]]; val = u32::from_le_bytes(val_bytes) as usize; }, - _ => return Err(CustomError::new("Not support.")), //ob11 not support, which up six is the bignumber length. + _ => return Err(CustomError::new("Not support.")), /* ob11 not support, which up six + * is the bignumber length. */ } b = b * 2; diff --git a/frame/hybrid-vm/src/lib.rs b/frame/hybrid-vm/src/lib.rs index 9d53ebf..684a279 100644 --- a/frame/hybrid-vm/src/lib.rs +++ b/frame/hybrid-vm/src/lib.rs @@ -23,15 +23,25 @@ mod tests; mod interoperate; use self::interoperate::InterCall; -use ethereum::TransactionV2 as Transaction; -use frame_support::traits::{tokens::fungible::Inspect, Currency, Get}; -use frame_support::RuntimeDebugNoBound; -use pallet_contracts::chain_extension::{Environment, Ext, InitState, RetVal}; +use frame_support::{ + traits::{tokens::fungible::Inspect, ConstU32, Currency, Get}, + weights::Weight, + RuntimeDebugNoBound, +}; +use ink_env::call::{ExecutionInput, Selector}; +use pallet_contracts::{ + chain_extension::{Environment, Ext, InitState, RetVal}, + CollectEvents, DebugInfo, Determinism, +}; +use sha3::{Digest, Keccak256}; use sp_core::{H160, U256}; -use sp_runtime::{AccountId32, DispatchError}; +use sp_runtime::{ + traits::{BlakeTwo256, Hash}, + BoundedVec, DispatchError, +}; use sp_std::vec::Vec; //use sp_std::fmt::Debug; -use hp_system::{AccountId32Mapping, AccountIdMapping, U256BalanceMapping}; +use hp_system::{AccountIdMapping, U256BalanceMapping}; pub use self::pallet::*; @@ -41,8 +51,16 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + const WEIGHT_LIMIT: Weight = Weight::from_parts(1_000_000_000_000, u64::MAX); + type Result = sp_std::result::Result; + //evm_fun_abi, wasm_message_name, wasm_message_selector + pub type EvmABI = (String, String, Option<[u8; 4]>); + + //wasm_message_selector, evm_fun_abi + pub type FunABI = ([u8; 4], BoundedVec>); + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, RuntimeDebugNoBound, PartialEq)] #[scale_info(skip_type_params(T))] pub enum UnifiedAddress { @@ -60,30 +78,28 @@ pub mod pallet { type AccountIdMapping: AccountIdMapping; - type AccountId32Mapping: AccountId32Mapping; - #[pallet::constant] type EnableCallEVM: Get; #[pallet::constant] type EnableCallWasmVM: Get; - - #[pallet::constant] - type GasLimit: Get; - - #[pallet::constant] - type GasPrice: Get>; } #[pallet::pallet] pub struct Pallet(_); - // HybridVM contracts + // HybridVM contracts, keys: address #[pallet::storage] #[pallet::getter(fn hvm_contracts)] pub type HvmContracts = StorageMap<_, Twox64Concat, H160, UnifiedAddress, OptionQuery>; + // HybridVM EVM ABI, keys: address+selector + #[pallet::storage] + #[pallet::getter(fn evm_fun_abi)] + pub type EvmABInfo = + StorageDoubleMap<_, Twox64Concat, H160, Twox64Concat, u32, FunABI, OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -99,7 +115,18 @@ pub mod pallet { EVMExecuteFailed, WasmVMExecuteFailed, UnifiedAddressError, - NoWasmVMContract, + NoWasmContractOrCallError, + EvmABIDecodeError, + EvmABIError, + WasmContractRevert, + DisableCallWasmVM, + DisableCallEvm, + AccountIdDecodeError, + WasmEncodeError, + WasmDecodeError, + EvmEncodeError, + EvmDecodeError, + ChainExtensionWriteError, } #[pallet::hooks] @@ -108,7 +135,7 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(1))] + #[pallet::weight(Weight::from_parts(10_000, 0) + T::DbWeight::get().writes(3))] pub fn regist_contract( origin: OriginFor, unified_address: UnifiedAddress, @@ -117,18 +144,62 @@ pub mod pallet { match unified_address.clone() { UnifiedAddress::::WasmVM(account) => { - let value = pallet_contracts::Pallet::::get_storage(account.clone(), vec![]); - match value { - Err(t) => { - if t == pallet_contracts::ContractAccessError::DoesntExist { - return Err(Error::::NoWasmVMContract.into()); - } - }, - _ => {}, + let mut a: [u8; 4] = Default::default(); + a.copy_from_slice(&BlakeTwo256::hash(b"hybridvm_evm_abi")[0..4]); + let abi_of_call = ExecutionInput::new(Selector::new(a)); + + let result = pallet_contracts::Pallet::::bare_call( + who.clone(), + account.clone(), + 0u8.into(), + WEIGHT_LIMIT, + None, + abi_of_call.encode(), + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ) + .result?; + + if result.did_revert() { + return Err(Error::::WasmContractRevert.into()); + } + + let evm_abi = > as Decode>::decode(&mut &result.data[..]) + .map_err(|_| DispatchError::from(Error::::EvmABIDecodeError))? + .map_err(|_| DispatchError::from(Error::::EvmABIDecodeError))?; + + let mut abi_info: Vec<(u32, FunABI)> = vec![]; + for abi in evm_abi { + let index = abi + .0 + .find(')') + .ok_or::(Error::::EvmABIError.into())?; + let mut a: [u8; 4] = Default::default(); + a.copy_from_slice(&Keccak256::digest(abi.0[0..index + 1].as_bytes())[0..4]); + let selector = u32::from_be_bytes(a); + + let wasm_selector = match abi.2 { + Some(t) => t, + None => { + let mut a: [u8; 4] = Default::default(); + a.copy_from_slice(&BlakeTwo256::hash(abi.1.as_bytes())[0..4]); + a + }, + }; + let evm_fun_abi = + BoundedVec::>::truncate_from(abi.0.into_bytes()); + abi_info.push((selector, (wasm_selector, evm_fun_abi))); } + let address = T::AccountIdMapping::into_address(account); HvmContracts::::insert(address, unified_address.clone()); + _ = EvmABInfo::::clear_prefix(address, 10000, None); + + for abi in abi_info { + EvmABInfo::::insert(address, abi.0, abi.1); + } Self::deposit_event(Event::RegistContract(address, unified_address, who)); Ok(().into()) @@ -137,10 +208,7 @@ pub mod pallet { } } - impl Pallet - where - T::AccountId: From + Into, - { + impl Pallet { pub fn call_wasm_vm( origin: OriginFor, data: Vec, diff --git a/frame/hybrid-vm/src/mock.rs b/frame/hybrid-vm/src/mock.rs index a6adfe6..1e393c5 100644 --- a/frame/hybrid-vm/src/mock.rs +++ b/frame/hybrid-vm/src/mock.rs @@ -17,17 +17,17 @@ use super::*; use byte_slice_cast::AsByteSlice; use fp_evm::Precompile; -use frame_support::pallet_prelude::*; use frame_support::{ derive_impl, dispatch::DispatchClass, + pallet_prelude::*, parameter_types, traits::{ConstU128, ConstU32, ConstU64, FindAuthor, Get}, weights::Weight, ConsensusEngineId, }; use frame_system::pallet_prelude::*; -use hp_system::{AccountId32Mapping, AccountIdMapping, EvmHybridVMExtension, U256BalanceMapping}; +use hp_system::{AccountIdMapping, EvmHybridVMExtension, U256BalanceMapping}; use pallet_contracts::chain_extension::SysConfig; use pallet_evm::{ AddressMapping, BalanceOf, EnsureAddressTruncated, FeeCalculator, GasWeightMapping, @@ -183,11 +183,11 @@ where pub struct CompactAddressMapping; -impl AddressMapping for CompactAddressMapping { - fn into_account_id(address: H160) -> AccountId32 { +impl AddressMapping for CompactAddressMapping { + fn into_account_id(address: H160) -> AccountId { let mut data = [0u8; 32]; data[0..20].copy_from_slice(&address[..]); - AccountId32::from(data) + AccountId::from(data) } } @@ -323,7 +323,7 @@ parameter_types! { } pub struct EnsureAccount(sp_std::marker::PhantomData<(T, A)>); -impl>> +impl>> EnsureOrigin<::RuntimeOrigin> for EnsureAccount where ::AccountId: From, @@ -385,17 +385,9 @@ impl pallet_contracts::Config for Test { type Xcm = (); } -pub struct GasPrice; -impl Get> for GasPrice { - fn get() -> Option { - Some(U256::from(100_000_000_000u64)) - } -} - parameter_types! { pub const EnableCallEVM: bool = true; pub const EnableCallWasmVM: bool = true; - pub const GasLimit: u64 = 10_000_000u64; } impl U256BalanceMapping for Test { @@ -414,26 +406,13 @@ impl AccountIdMapping for Test { } } -impl AccountId32Mapping for Test { - fn id32_to_id(id32: AccountId32) -> ::AccountId { - id32.into() - } - - fn id_to_id32(account_id: ::AccountId) -> AccountId32 { - account_id.into() - } -} - impl pallet_hybrid_vm::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type U256BalanceMapping = Self; type AccountIdMapping = Self; - type AccountId32Mapping = Self; type EnableCallEVM = EnableCallEVM; type EnableCallWasmVM = EnableCallWasmVM; - type GasLimit = GasLimit; - type GasPrice = GasPrice; } const A: [u8; 32] = [ @@ -443,8 +422,8 @@ const B: [u8; 32] = [ 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, ]; -pub const ALICE: AccountId32 = AccountId32::new(A); -pub const BOB: AccountId32 = AccountId32::new(B); +pub const ALICE: AccountId = AccountId::new(A); +pub const BOB: AccountId = AccountId::new(B); const A_SHADOW: [u8; 32] = [ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -452,9 +431,10 @@ const A_SHADOW: [u8; 32] = [ const B_SHADOW: [u8; 32] = [ 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; -// Account shadow is the account which data is the source account data with the last 12 bytes setting zero -pub const ALICE_SHADOW: AccountId32 = AccountId32::new(A_SHADOW); -pub const BOB_SHADOW: AccountId32 = AccountId32::new(B_SHADOW); +// Account shadow is the account which data is the source account data with the last 12 bytes +// setting zero +pub const ALICE_SHADOW: AccountId = AccountId::new(A_SHADOW); +pub const BOB_SHADOW: AccountId = AccountId::new(B_SHADOW); pub struct ExtBuilder { existential_deposit: u64, diff --git a/frame/hybrid-vm/src/tests.rs b/frame/hybrid-vm/src/tests.rs index 3da9faf..3494d8a 100644 --- a/frame/hybrid-vm/src/tests.rs +++ b/frame/hybrid-vm/src/tests.rs @@ -67,8 +67,7 @@ where Ok((wasm_binary, code_hash)) } -use std::fs::File; -use std::io::Read; +use std::{fs::File, io::Read}; fn read_a_file(filename: &str) -> std::io::Result> { let mut file = File::open(filename)?; @@ -103,7 +102,7 @@ where // Perform test for wasm contract calling EVM contract to transfer EVM ERC20 token #[test] fn test_wasm_call_evm() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -136,7 +135,7 @@ fn test_wasm_call_evm() { //assert!(ContractInfoOf::::contains_key(&wasm_addr)); - //3. Create EVM contract and tranfer to bob token + // 3. Create EVM contract and tranfer to bob token let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -212,7 +211,7 @@ fn test_wasm_call_evm() { }; println!("Alice transfer to Bob token:{}", transfer_result); - //4. Get BOB_SHADOW balance of EVM token + // 4. Get BOB_SHADOW balance of EVM token let balance_of_selector = &Keccak256::digest(b"balanceOf(address)")[0..4]; let source_bob = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&BOB_SHADOW)[0..20])); @@ -253,7 +252,8 @@ fn test_wasm_call_evm() { }; println!("bob_balance_before={}", bob_balance_before); - //5. Call wasm contract to call evm transfer evm token to bob. H160: evm contract address, H160: bob's address u128: value + // 5. Call wasm contract to call evm transfer evm token to bob. H160: evm contract address, + // H160: bob's address u128: value let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"wasmCallEvm")[0..4]); let call = ExecutionInput::new(Selector::new(a)); @@ -281,7 +281,7 @@ fn test_wasm_call_evm() { assert!(!result.did_revert()); println!("Alice transfer to Bob from wasm_call_evm:{}", transfer_value); - //6. Get BOB_SHADOW balance of EVM token + // 6. Get BOB_SHADOW balance of EVM token let call4evm = ::Runner::call( source_bob, evm_addr, @@ -314,7 +314,7 @@ fn test_wasm_call_evm() { }, }; println!("bob_balance_after={}", bob_balance_after); - //7. Test the balance of BOB_SHADOW being correct + // 7. Test the balance of BOB_SHADOW being correct assert_eq!(bob_balance_after, bob_balance_before + transfer_value); }); } @@ -322,7 +322,7 @@ fn test_wasm_call_evm() { // Perform test for EVM contract calling wasm contract to transfer wasm ERC20 token #[test] fn test_evm_call_wasm() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -378,7 +378,7 @@ fn test_evm_call_wasm() { assert!(!result.did_revert()); - //3. Create EVM contract + // 3. Create EVM contract let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -410,7 +410,7 @@ fn test_evm_call_wasm() { }, } - //4. Get BOB_SHADOW balance of wasm token + // 4. Get BOB_SHADOW balance of wasm token let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"balance_of")[0..4]); let balance_of_call = ExecutionInput::new(Selector::new(a)); @@ -436,7 +436,8 @@ fn test_evm_call_wasm() { println!("result data before:{:?}", result); let bob_balance_before = result.data; - //5. Call EVM contract to call wasm contract transfer wasm token to bob, the last bytes32 is the wasm contract accountid + // 5. Call EVM contract to call wasm contract transfer wasm token to bob, the last bytes32 + // is the wasm contract accountid let evm_call_wasm_selector = &Keccak256::digest(b"evmCallWasm(bytes32,uint256,bytes32)")[0..4]; @@ -475,7 +476,7 @@ fn test_evm_call_wasm() { assert!(&call4evm.unwrap().exit_reason.is_succeed()); println!("Alice transfer to Bob from evm_call_wasm:{}", transfer_value); - //6. Get BOB_SHADOW balance of wasm token + // 6. Get BOB_SHADOW balance of wasm token let result = Contracts::bare_call( BOB_SHADOW.clone(), wasm_addr.clone(), @@ -494,7 +495,7 @@ fn test_evm_call_wasm() { println!("result data after:{:?}", result); let bob_balance_after = result.data; - //7. Test the balance of BOB_SHADOW being correct + // 7. Test the balance of BOB_SHADOW being correct let after = as Decode>::decode(&mut &bob_balance_after[..]) .unwrap() .unwrap(); @@ -508,7 +509,7 @@ fn test_evm_call_wasm() { // Perform test for wasm contract calling EVM contract to get bob's EVM ERC20 token balance #[test] fn test_wasm_call_evm_balance() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -541,7 +542,7 @@ fn test_wasm_call_evm_balance() { //assert!(ContractInfoOf::::contains_key(&wasm_addr)); - //3. Create EVM contract and tranfer to bob token + // 3. Create EVM contract and tranfer to bob token let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -618,7 +619,8 @@ fn test_wasm_call_evm_balance() { println!("Alice transfer to Bob evm result:{}, tokens:{}", transfer_result, token); - //4. Call wasm contract to call evm for getting BOB_SHADOW balance of EVM token. H160: evm contract address, H160: BOB_SHADOW's address + // 4. Call wasm contract to call evm for getting BOB_SHADOW balance of EVM token. H160: evm + // contract address, H160: BOB_SHADOW's address let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"wasmCallEvmBalance")[0..4]); let call = ExecutionInput::new(Selector::new(a)); @@ -651,7 +653,7 @@ fn test_wasm_call_evm_balance() { .unwrap(); println!("BOB_SHADOW's evm token balance:{:?}", balance_return); - //5. Test the evm token balance of BOB_SHADOW being correct + // 5. Test the evm token balance of BOB_SHADOW being correct assert_eq!(balance_return, token); }); } @@ -659,7 +661,7 @@ fn test_wasm_call_evm_balance() { // Perform test for EVM contract calling wasm contract to get bob's wasm ERC20 token balance #[test] fn test_evm_call_wasm_balance() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -716,7 +718,7 @@ fn test_evm_call_wasm_balance() { assert!(!result.did_revert()); println!("Alice transfer to Bob wasm token:{}", token); - //3. Create EVM contract + // 3. Create EVM contract let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -748,7 +750,8 @@ fn test_evm_call_wasm_balance() { }, } - //4. Call evm contract to call wasm for getting BOB_SHADOW balance of wasm token. H160: BOB_SHADOW's address , the last bytes32 is the wasm contract accountid + // 4. Call evm contract to call wasm for getting BOB_SHADOW balance of wasm token. H160: + // BOB_SHADOW's address , the last bytes32 is the wasm contract accountid let evm_call_wasm_selector = &Keccak256::digest(b"evmCallWasmBalance(bytes32,bytes32)")[0..4]; @@ -794,15 +797,16 @@ fn test_evm_call_wasm_balance() { println!("BOB_SHADOW's wasm token balance:{}", bob_balance); - //5. Test the wasm token balance of BOB_SHADOW being correct + // 5. Test the wasm token balance of BOB_SHADOW being correct assert_eq!(bob_balance, token); }); } -// Perform test for wasm contract calling EVM echo contract, testing parameters of different data types. +// Perform test for wasm contract calling EVM echo contract, testing parameters of different data +// types. #[test] fn test_wasm_call_evm_echo() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -835,7 +839,7 @@ fn test_wasm_call_evm_echo() { //assert!(ContractInfoOf::::contains_key(&wasm_addr)); - //3. Create EVM contract + // 3. Create EVM contract let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -867,7 +871,7 @@ fn test_wasm_call_evm_echo() { }, } - //4. Call wasm contract to call evm using parameters of different data types. + // 4. Call wasm contract to call evm using parameters of different data types. let mut a: [u8; 4] = Default::default(); a.copy_from_slice(&BlakeTwo256::hash(b"wasmCallEvmProxy")[0..4]); let call = ExecutionInput::new(Selector::new(a)); @@ -912,16 +916,17 @@ fn test_wasm_call_evm_echo() { i += 1; } println!("array:{:?}", echo_arr); - //5. Test whether the evm echo result is correct + // 5. Test whether the evm echo result is correct assert_eq!(&echo_string, "test string!"); assert_eq!(echo_arr[0..echo_arr_len], [231usize, 19usize, 6usize][..]); }); } -// Perform test for EVM contract calling wasm echo contract, testing parameters of different data types. +// Perform test for EVM contract calling wasm echo contract, testing parameters of different data +// types. #[test] fn test_evm_call_wasm_echo() { - // 1. Get wasm and evm contract bin + // 1. Get wasm and evm contract bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); let (evm, _evm_code_hash) = contract_module::("erc20_evm_bytecode.txt", false).unwrap(); @@ -953,7 +958,7 @@ fn test_evm_call_wasm_echo() { assert_ok!(creation); //assert!(ContractInfoOf::::contains_key(&wasm_addr)); - //3. Create EVM contract + // 3. Create EVM contract let source = H160::from_slice(&(AsRef::<[u8; 32]>::as_ref(&ALICE_SHADOW)[0..20])); let creation4evm = ::Runner::create( @@ -985,7 +990,7 @@ fn test_evm_call_wasm_echo() { }, } - //4. Call evm contract to call wasm using parameters of different data types. + // 4. Call evm contract to call wasm using parameters of different data types. let evm_call_wasm_selector = &Keccak256::digest(b"evmCallWasmProxy(string)")[0..4]; let wasm_contract: [u8; 32] = wasm_addr.clone().into(); @@ -1060,7 +1065,7 @@ fn test_evm_call_wasm_echo() { i += 1; } println!("array:{:?}", echo_arr); - //5. Test whether the wasm echo result is correct + // 5. Test whether the wasm echo result is correct assert_eq!(&echo_string, "test string!"); assert_eq!(echo_arr[0..echo_arr_len], [231usize, 19usize, 6usize][..]); }); @@ -1069,7 +1074,7 @@ fn test_evm_call_wasm_echo() { // Perform test for regist_contract. #[test] fn regist_contract_works() { - // 1. Get wasm bin + // 1. Get wasm bin let (wasm, wasm_code_hash) = contract_module::("erc20.wasm", true).unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| {