diff --git a/Cargo.lock b/Cargo.lock index cb2502d9b0f..f4989287b65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2501,8 +2501,7 @@ checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "evm" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8ff320c1e25e7f6d676858f16ffd9b0493d2cc67c3d900c6f2ed027b747f43" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "auto_impl", "environmental", @@ -2522,8 +2521,7 @@ dependencies = [ [[package]] name = "evm-core" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4537041d3a3438d59b2d01bd950ce89fb1ccb3cf21d9331193c10be12e849f" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "parity-scale-codec", "primitive-types", @@ -2534,8 +2532,7 @@ dependencies = [ [[package]] name = "evm-gasometer" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6181da8734c86873ac9b3f9886d4e00105361039dcfb9f621be9a0ddb8f43961" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "environmental", "evm-core", @@ -2546,8 +2543,7 @@ dependencies = [ [[package]] name = "evm-runtime" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6157af91ca70fcf3581afaea1fa25974a71b9ef63d454c08dfba93ab0c7715d" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "auto_impl", "environmental", @@ -2669,7 +2665,7 @@ dependencies = [ [[package]] name = "fc-consensus" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "async-trait", "fc-db", @@ -2688,7 +2684,7 @@ dependencies = [ [[package]] name = "fc-db" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-storage", "kvdb-rocksdb", @@ -2704,7 +2700,7 @@ dependencies = [ [[package]] name = "fc-mapping-sync" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fc-db", "fp-consensus", @@ -2721,7 +2717,7 @@ dependencies = [ [[package]] name = "fc-rpc" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "ethereum-types", @@ -2763,7 +2759,7 @@ dependencies = [ [[package]] name = "fc-rpc-core" version = "1.1.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "ethereum-types", @@ -2893,7 +2889,7 @@ dependencies = [ [[package]] name = "fp-consensus" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "parity-scale-codec", @@ -2905,7 +2901,7 @@ dependencies = [ [[package]] name = "fp-evm" version = "3.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "evm", "frame-support", @@ -2918,7 +2914,7 @@ dependencies = [ [[package]] name = "fp-rpc" version = "3.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "ethereum-types", @@ -2935,7 +2931,7 @@ dependencies = [ [[package]] name = "fp-self-contained" version = "1.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "frame-support", @@ -2951,7 +2947,7 @@ dependencies = [ [[package]] name = "fp-storage" version = "2.0.0" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "parity-scale-codec", ] @@ -6976,7 +6972,7 @@ dependencies = [ [[package]] name = "pallet-base-fee" version = "1.0.0" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "frame-support", @@ -7250,7 +7246,7 @@ dependencies = [ [[package]] name = "pallet-ethereum" version = "4.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "ethereum", "ethereum-types", @@ -7290,7 +7286,7 @@ dependencies = [ [[package]] name = "pallet-evm" version = "6.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "evm", "fp-evm", @@ -7373,7 +7369,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-blake2" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", ] @@ -7381,7 +7377,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-bn128" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "sp-core", @@ -7391,7 +7387,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-dispatch" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "frame-support", @@ -7401,7 +7397,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-modexp" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "num", @@ -7410,7 +7406,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-sha3fips" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "tiny-keccak", @@ -7419,7 +7415,7 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-simple" version = "2.0.0-dev" -source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#ff71b5a8b940c716b1cfd4e9c1fe6b7c384a7fd0" +source = "git+https://github.com/purestake/frontier?branch=moonbeam-polkadot-v0.9.20#22d54c34e8487231187b3f6527a24af471c46cd9" dependencies = [ "fp-evm", "ripemd", diff --git a/Cargo.toml b/Cargo.toml index 927672fbdd8..7fa54720889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,15 @@ members = [ "pallets/migrations", "pallets/moonbeam-orbiters", "pallets/proxy-genesis-companion", + "precompiles/author-mapping", "precompiles/balances-erc20", + "precompiles/crowdloan-rewards", + "precompiles/pallet-democracy", + "precompiles/parachain-staking", + "precompiles/relay-encoder", "precompiles/utils/macro", + "precompiles/xcm_transactor", + "precompiles/xtokens", "runtime/moonbase", "runtime/moonbeam", "runtime/moonriver", diff --git a/precompiles/assets-erc20/src/eip2612.rs b/precompiles/assets-erc20/src/eip2612.rs index 786f1d8a30c..fa1286f2109 100644 --- a/precompiles/assets-erc20/src/eip2612.rs +++ b/precompiles/assets-erc20/src/eip2612.rs @@ -186,25 +186,26 @@ where // Translated from // https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2ERC20.sol#L81 pub(crate) fn permit( - address: H160, asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let mut input = handle.read_input()?; + let owner: H160 = input.read::
()?.into(); + let spender: H160 = input.read::
()?.into(); + let value: U256 = input.read()?; + let deadline: U256 = input.read()?; + let v: u8 = input.read()?; + let r: H256 = input.read()?; + let s: H256 = input.read()?; - let owner: H160 = input.read::
(gasometer)?.into(); - let spender: H160 = input.read::
(gasometer)?.into(); - let value: U256 = input.read(gasometer)?; - let deadline: U256 = input.read(gasometer)?; - let v: u8 = input.read(gasometer)?; - let r: H256 = input.read(gasometer)?; - let s: H256 = input.read(gasometer)?; + let address = handle.code_address(); // pallet_timestamp is in ms while Ethereum use second timestamps. let timestamp: U256 = (pallet_timestamp::Pallet::::get()).into() / 1000; - ensure!(deadline >= timestamp, gasometer.revert("permit expired")); + ensure!(deadline >= timestamp, revert("permit expired")); let nonce = NoncesStorage::::get(address, owner); @@ -217,68 +218,58 @@ where sig[64] = v; let signer = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &permit) - .map_err(|_| gasometer.revert("invalid permit"))?; + .map_err(|_| revert("invalid permit"))?; let signer = H160::from(H256::from_slice(keccak_256(&signer).as_slice())); ensure!( signer != H160::zero() && signer == owner, - gasometer.revert("invalid permit") + revert("invalid permit") ); NoncesStorage::::insert(address, owner, nonce + U256::one()); Erc20AssetsPrecompileSet::::approve_inner( - asset_id, gasometer, owner, spender, value, + asset_id, handle, owner, spender, value, )?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: vec![], - logs: LogsBuilder::new(address) - .log3( - SELECTOR_LOG_APPROVAL, - owner, - spender, - EvmDataWriter::new().write(value).build(), - ) - .build(), - }) + let log_builder = LogsBuilder::new(address); + log_builder + .log3( + SELECTOR_LOG_APPROVAL, + owner, + spender, + EvmDataWriter::new().write(value).build(), + ) + .record(handle)?; + + Ok(succeed([])) } pub(crate) fn nonces( - address: H160, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + _asset_id: AssetIdOf, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let owner: H160 = input.read::
(gasometer)?.into(); + let mut input = handle.read_input()?; + let owner: H160 = input.read::
()?.into(); - let nonce = NoncesStorage::::get(address, owner); + let nonce = NoncesStorage::::get(handle.code_address(), owner); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(nonce).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(nonce).build())) } pub(crate) fn domain_separator( - address: H160, asset_id: AssetIdOf, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let domain_separator: H256 = Self::compute_domain_separator(address, asset_id).into(); + let domain_separator: H256 = + Self::compute_domain_separator(handle.code_address(), asset_id).into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(domain_separator).build(), - logs: vec![], - }) + Ok(succeed( + EvmDataWriter::new().write(domain_separator).build(), + )) } } diff --git a/precompiles/assets-erc20/src/lib.rs b/precompiles/assets-erc20/src/lib.rs index ecafc6b9b9d..73aeb3b8a70 100644 --- a/precompiles/assets-erc20/src/lib.rs +++ b/precompiles/assets-erc20/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(test, feature(assert_matches))] use core::fmt::Display; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::traits::fungibles::approvals::Inspect as ApprovalInspect; use frame_support::traits::fungibles::metadata::Inspect as MetadataInspect; use frame_support::traits::fungibles::Inspect; @@ -29,8 +29,8 @@ use frame_support::{ }; use pallet_evm::{AddressMapping, PrecompileSet}; use precompile_utils::{ - keccak256, Address, Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, - Gasometer, LogsBuilder, RuntimeHelper, + keccak256, revert, succeed, Address, Bytes, EvmData, EvmDataWriter, EvmResult, + FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt, RuntimeHelper, }; use sp_runtime::traits::Bounded; use sp_std::vec::Vec; @@ -39,7 +39,6 @@ use sp_core::{H160, U256}; use sp_std::{ convert::{TryFrom, TryInto}, marker::PhantomData, - vec, }; mod eip2612; @@ -139,85 +138,59 @@ where ::Moment: Into, AssetIdOf: Display, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - if let Some((_, asset_id)) = - Runtime::account_to_asset_id(Runtime::AddressMapping::into_account_id(address)) - { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + if let Some((_, asset_id)) = Runtime::account_to_asset_id( + Runtime::AddressMapping::into_account_id(handle.code_address()), + ) { // We check maybe_total_supply. This function returns Some if the asset exists, // which is all we care about at this point if pallet_assets::Pallet::::maybe_total_supply(asset_id).is_some() { let result = { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; - - let (mut input, selector) = - match EvmDataReader::new_with_selector(gasometer, input) { - Ok((input, selector)) => (input, selector), - Err(e) => return Some(Err(e)), - }; - let input = &mut input; - - if let Err(err) = gasometer.check_function_modifier( - context, - is_static, - match selector { - Action::Approve | Action::Transfer | Action::TransferFrom => { - FunctionModifier::NonPayable - } - _ => FunctionModifier::View, - }, - ) { + let selector = match handle.read_selector() { + Ok(selector) => selector, + Err(e) => return Some(Err(e)), + }; + + if let Err(err) = handle.check_function_modifier(match selector { + Action::Approve | Action::Transfer | Action::TransferFrom => { + FunctionModifier::NonPayable + } + _ => FunctionModifier::View, + }) { return Some(Err(err)); } match selector { // Local and Foreign common - Action::TotalSupply => Self::total_supply(asset_id, input, gasometer), - Action::BalanceOf => Self::balance_of(asset_id, input, gasometer), - Action::Allowance => Self::allowance(asset_id, input, gasometer), - Action::Approve => Self::approve(asset_id, input, gasometer, context), - Action::Transfer => Self::transfer(asset_id, input, gasometer, context), - Action::TransferFrom => { - Self::transfer_from(asset_id, input, gasometer, context) - } - Action::Name => Self::name(asset_id, gasometer), - Action::Symbol => Self::symbol(asset_id, gasometer), - Action::Decimals => Self::decimals(asset_id, gasometer), + Action::TotalSupply => Self::total_supply(asset_id, handle), + Action::BalanceOf => Self::balance_of(asset_id, handle), + Action::Allowance => Self::allowance(asset_id, handle), + Action::Approve => Self::approve(asset_id, handle), + Action::Transfer => Self::transfer(asset_id, handle), + Action::TransferFrom => Self::transfer_from(asset_id, handle), + Action::Name => Self::name(asset_id, handle), + Action::Symbol => Self::symbol(asset_id, handle), + Action::Decimals => Self::decimals(asset_id, handle), // Only local - Action::Mint => Self::mint(asset_id, input, gasometer, context), - Action::Burn => Self::burn(asset_id, input, gasometer, context), - Action::Freeze => Self::freeze(asset_id, input, gasometer, context), - Action::Thaw => Self::thaw(asset_id, input, gasometer, context), - Action::FreezeAsset => Self::freeze_asset(asset_id, gasometer, context), - Action::ThawAsset => Self::thaw_asset(asset_id, gasometer, context), - Action::TransferOwnership => { - Self::transfer_ownership(asset_id, input, gasometer, context) - } - Action::SetTeam => Self::set_team(asset_id, input, gasometer, context), - Action::SetMetadata => { - Self::set_metadata(asset_id, input, gasometer, context) - } - Action::ClearMetadata => Self::clear_metadata(asset_id, gasometer, context), + Action::Mint => Self::mint(asset_id, handle), + Action::Burn => Self::burn(asset_id, handle), + Action::Freeze => Self::freeze(asset_id, handle), + Action::Thaw => Self::thaw(asset_id, handle), + Action::FreezeAsset => Self::freeze_asset(asset_id, handle), + Action::ThawAsset => Self::thaw_asset(asset_id, handle), + Action::TransferOwnership => Self::transfer_ownership(asset_id, handle), + Action::SetTeam => Self::set_team(asset_id, handle), + Action::SetMetadata => Self::set_metadata(asset_id, handle), + Action::ClearMetadata => Self::clear_metadata(asset_id, handle), Action::Eip2612Permit => { - eip2612::Eip2612::::permit( - address, asset_id, input, gasometer, - ) + eip2612::Eip2612::::permit(asset_id, handle) } Action::Eip2612Nonces => { - eip2612::Eip2612::::nonces( - address, input, gasometer, - ) + eip2612::Eip2612::::nonces(asset_id, handle) } Action::Eip2612DomainSeparator => { eip2612::Eip2612::::domain_separator( - address, asset_id, gasometer, + asset_id, handle, ) } } @@ -266,38 +239,33 @@ where { fn total_supply( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Parse input. - input.expect_arguments(gasometer, 0)?; + let input = handle.read_input()?; + input.expect_arguments(0)?; // Fetch info. let amount: U256 = pallet_assets::Pallet::::total_issuance(asset_id).into(); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } fn balance_of( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Read input. - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let owner: H160 = input.read::
(gasometer)?.into(); + let owner: H160 = input.read::
()?.into(); // Fetch info. let amount: U256 = { @@ -306,26 +274,21 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } fn allowance( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Read input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let owner: H160 = input.read::
(gasometer)?.into(); - let spender: H160 = input.read::
(gasometer)?.into(); + let owner: H160 = input.read::
()?.into(); + let spender: H160 = input.read::
()?.into(); // Fetch info. let amount: U256 = { @@ -337,49 +300,40 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } fn approve( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_log_costs_manual(3, 32)?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; + + let spender: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; - let spender: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; + Self::approve_inner(asset_id, handle, handle.context().caller, spender, amount)?; - Self::approve_inner(asset_id, gasometer, context.caller, spender, amount)?; + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_APPROVAL, + handle.context().caller, + spender, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_APPROVAL, - context.caller, - spender, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn approve_inner( asset_id: AssetIdOf, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, owner: H160, spender: H160, value: U256, @@ -391,97 +345,92 @@ where value.try_into().unwrap_or_else(|_| Bounded::max_value()); // Allowance read - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // If previous approval exists, we need to clean it if pallet_assets::Pallet::::allowance(asset_id, &owner, &spender) != 0u32.into() { RuntimeHelper::::try_dispatch( + handle, Some(owner.clone()).into(), pallet_assets::Call::::cancel_approval { id: asset_id, delegate: Runtime::Lookup::unlookup(spender.clone()), }, - gasometer, )?; } // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(owner).into(), pallet_assets::Call::::approve_transfer { id: asset_id, delegate: Runtime::Lookup::unlookup(spender), amount, }, - gasometer, ) } fn transfer( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_log_costs_manual(3, 32)?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let to: H160 = input.read::
(gasometer)?.into(); - let amount = input.read::>(gasometer)?; + let to: H160 = input.read::
()?.into(); + let amount = input.read::>()?; // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::transfer { id: asset_id, target: Runtime::Lookup::unlookup(to), amount, }, - gasometer, )?; } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + handle.context().caller, + to, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - context.caller, - to, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn transfer_from( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_log_costs_manual(3, 32)?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 3)?; - let from: H160 = input.read::
(gasometer)?.into(); - let to: H160 = input.read::
(gasometer)?.into(); - let amount = input.read::>(gasometer)?; + let mut input = handle.read_input()?; + input.expect_arguments(3)?; + let from: H160 = input.read::
()?.into(); + let to: H160 = input.read::
()?.into(); + let amount = input.read::>()?; { let caller: Runtime::AccountId = - Runtime::AddressMapping::into_account_id(context.caller); + Runtime::AddressMapping::into_account_id(handle.context().caller); let from: Runtime::AccountId = Runtime::AddressMapping::into_account_id(from.clone()); let to: Runtime::AccountId = Runtime::AddressMapping::into_account_id(to); @@ -489,6 +438,7 @@ where if caller != from { // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(caller).into(), pallet_assets::Call::::transfer_approved { id: asset_id, @@ -496,407 +446,357 @@ where destination: Runtime::Lookup::unlookup(to), amount, }, - gasometer, )?; } else { // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(from).into(), pallet_assets::Call::::transfer { id: asset_id, target: Runtime::Lookup::unlookup(to), amount, }, - gasometer, )?; } } + + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + from, + to, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - from, - to, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn name( asset_id: AssetIdOf, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write::( pallet_assets::Pallet::::name(asset_id) .as_slice() .into(), ) .build(), - logs: Default::default(), - }) + )) } fn symbol( asset_id: AssetIdOf, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write::( pallet_assets::Pallet::::symbol(asset_id) .as_slice() .into(), ) .build(), - logs: Default::default(), - }) + )) } fn decimals( asset_id: AssetIdOf, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write::(pallet_assets::Pallet::::decimals( asset_id, )) .build(), - logs: Default::default(), - }) + )) } // From here: only for locals, we need to check whether we are in local assets otherwise fail fn mint( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } - gasometer.record_log_costs_manual(3, 32)?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let to: H160 = input.read::
(gasometer)?.into(); - let amount = input.read::>(gasometer)?; + let to: H160 = input.read::
()?.into(); + let amount = input.read::>()?; // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::mint { id: asset_id, beneficiary: Runtime::Lookup::unlookup(to), amount, }, - gasometer, )?; } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + H160::default(), + to, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::default(), - to, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn burn( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } - gasometer.record_log_costs_manual(3, 32)?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let to: H160 = input.read::
(gasometer)?.into(); - let amount = input.read::>(gasometer)?; + let to: H160 = input.read::
()?.into(); + let amount = input.read::>()?; // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::burn { id: asset_id, who: Runtime::Lookup::unlookup(to), amount, }, - gasometer, )?; } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + to, + H160::default(), + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - to, - H160::default(), - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn freeze( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Parse input. - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let to: H160 = input.read::
(gasometer)?.into(); + let to: H160 = input.read::
()?.into(); // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::freeze { id: asset_id, who: Runtime::Lookup::unlookup(to), }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn thaw( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Parse input. - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let to: H160 = input.read::
(gasometer)?.into(); + let to: H160 = input.read::
()?.into(); // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::thaw { id: asset_id, who: Runtime::Lookup::unlookup(to), }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn freeze_asset( asset_id: AssetIdOf, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::freeze_asset { id: asset_id }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn thaw_asset( asset_id: AssetIdOf, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::thaw_asset { id: asset_id }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn transfer_ownership( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Parse input. - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let owner: H160 = input.read::
(gasometer)?.into(); + let owner: H160 = input.read::
()?.into(); // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let owner = Runtime::AddressMapping::into_account_id(owner); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::transfer_ownership { id: asset_id, owner: Runtime::Lookup::unlookup(owner), }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn set_team( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Parse input. - input.expect_arguments(gasometer, 3)?; + let mut input = handle.read_input()?; + input.expect_arguments(3)?; - let issuer: H160 = input.read::
(gasometer)?.into(); - let admin: H160 = input.read::
(gasometer)?.into(); - let freezer: H160 = input.read::
(gasometer)?.into(); + let issuer: H160 = input.read::
()?.into(); + let admin: H160 = input.read::
()?.into(); + let freezer: H160 = input.read::
()?.into(); // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let issuer = Runtime::AddressMapping::into_account_id(issuer); let admin = Runtime::AddressMapping::into_account_id(admin); let freezer = Runtime::AddressMapping::into_account_id(freezer); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::set_team { id: asset_id, @@ -904,42 +804,36 @@ where admin: Runtime::Lookup::unlookup(admin), freezer: Runtime::Lookup::unlookup(freezer), }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn set_metadata( asset_id: AssetIdOf, - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Parse input. - input.expect_arguments(gasometer, 3)?; + let mut input = handle.read_input()?; + input.expect_arguments(3)?; - let name: Bytes = input.read::(gasometer)?.into(); - let symbol: Bytes = input.read::(gasometer)?.into(); - let decimals: u8 = input.read::(gasometer)?.into(); + let name: Bytes = input.read::()?.into(); + let symbol: Bytes = input.read::()?.into(); + let decimals: u8 = input.read::()?.into(); // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::set_metadata { id: asset_id, @@ -947,46 +841,34 @@ where symbol: symbol.into(), decimals, }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } fn clear_metadata( asset_id: AssetIdOf, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { if !IsLocal::get() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_assets::Call::::clear_metadata { id: asset_id }, - gasometer, )?; } // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } } diff --git a/precompiles/assets-erc20/src/mock.rs b/precompiles/assets-erc20/src/mock.rs index 135a4f07d94..b632eac72c4 100644 --- a/precompiles/assets-erc20/src/mock.rs +++ b/precompiles/assets-erc20/src/mock.rs @@ -358,24 +358,17 @@ where Erc20AssetsPrecompileSet: PrecompileSet, Erc20AssetsPrecompileSet: PrecompileSet, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { // If the address matches asset prefix, the we route through the foreign asset precompile set a if &a.to_fixed_bytes()[0..4] == LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + .execute(handle) } // If the address matches asset prefix, the we route through the local asset precompile set a if &a.to_fixed_bytes()[0..4] == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + .execute(handle) } _ => None, } diff --git a/precompiles/assets-erc20/src/tests.rs b/precompiles/assets-erc20/src/tests.rs index 615b9903d80..497b05c4952 100644 --- a/precompiles/assets-erc20/src/tests.rs +++ b/precompiles/assets-erc20/src/tests.rs @@ -15,15 +15,13 @@ // along with Moonbeam. If not, see . use frame_support::assert_ok; -use std::{assert_matches::assert_matches, str::from_utf8}; +use std::str::from_utf8; use crate::{eip2612::Eip2612, mock::*, *}; -use fp_evm::{Context, PrecompileFailure}; use hex_literal::hex; use libsecp256k1::{sign, Message, SecretKey}; -use pallet_evm::PrecompileSet; -use precompile_utils::{EvmDataWriter, LogsBuilder}; +use precompile_utils::{testing::*, EvmDataWriter, LogsBuilder}; use sha3::{Digest, Keccak256}; use sp_core::H256; @@ -42,23 +40,13 @@ fn selector_less_than_four_bytes() { 1 )); // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &bogus_selector, - None, - &Context { - address: Account::ForeignAssetId(1u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, .. })) - if output == b"tried to parse selector out of bounds" - ); + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + vec![1u8, 2u8, 3u8], + ) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } @@ -72,23 +60,14 @@ fn no_selector_exists_but_length_is_right() { true, 1 )); - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &bogus_selector, - None, - &Context { - address: Account::ForeignAssetId(1u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, .. })) - if output == b"unknown selector" - ); + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + vec![1u8, 2u8, 3u8, 4u8], + ) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -137,25 +116,16 @@ fn get_total_supply() { Account::Alice.into(), 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TotalSupply).build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TotalSupply).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000u64)).build()); }); } @@ -178,27 +148,18 @@ fn get_balances_known_user() { Account::Alice.into(), 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000u64)).build()); }); } @@ -215,27 +176,18 @@ fn get_balances_unknown_user() { true, 1 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u64)).build()); }); } @@ -259,35 +211,25 @@ fn approve() { 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::from(500)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 30832756u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(30832756u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(500)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -311,58 +253,38 @@ fn approve_saturating() { 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::MAX) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 30832756u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::MAX).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(30832756u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::MAX).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(u128::MAX)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0u64) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(u128::MAX)).build()); }); } @@ -386,43 +308,29 @@ fn check_allowance_existing() { 1000 )); - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(500)) - .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(500)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(500u64)).build(), - cost: 0u64, - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(500u64)).build()); }); } @@ -439,28 +347,19 @@ fn check_allowance_not_existing() { true, 1 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u64)).build(), - cost: 0u64, - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u64)).build()); }); } @@ -484,79 +383,49 @@ fn transfer() { 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 44001756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(44001756u64) // 1 weight => 1 gas in mock + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); }); } @@ -580,26 +449,21 @@ fn transfer_not_enough_founds() { 1 )); - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(Account::Charlie.into())) .write(U256::from(50)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, ..})) - if from_utf8(&str).unwrap() + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("BalanceLow") - ); + && from_utf8(&output).unwrap().contains("BalanceLow") + }); }); } @@ -623,117 +487,84 @@ fn transfer_from() { 1000 )); - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(500)) - .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(500)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(500)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Bob, // Bob is the one sending transferFrom! + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Charlie.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Bob.into(), // Bob is the one sending transferFrom! - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 56268756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Charlie, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(56268756u64) // 1 weight => 1 gas in mock + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Charlie, + EvmDataWriter::new().write(U256::from(400)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Charlie.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -758,93 +589,68 @@ fn transfer_from_non_incremental_approval() { )); // We first approve 500 - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::from(500)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 30832756u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(30832756u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(500)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); // We then approve 300. Non-incremental, so this is // the approved new value // Additionally, the gas used in this approval is higher because we // need to clear the previous one - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::from(300)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 62796756u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(300)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(62796756u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(300)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); // This should fail, as now the new approved quantity is 300 - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Bob, // Bob is the one sending transferFrom! + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(500)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Bob.into(), // Bob is the one sending transferFrom! - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"Dispatched call failed with error: DispatchErrorWithPostInfo { \ + ) + .execute_reverts(|output| { + output + == b"Dispatched call failed with error: DispatchErrorWithPostInfo { \ post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, \ error: Module(ModuleError { index: 2, error: [10, 0, 0, 0], \ message: Some(\"Unapproved\") }) }" - ); + }); }); } @@ -868,43 +674,34 @@ fn transfer_from_above_allowance() { 1000 )); - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(300)) - .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(300)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Bob, // Bob is the one sending transferFrom! + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Bob.into(), // Bob is the one sending transferFrom! - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"Dispatched call failed with error: DispatchErrorWithPostInfo { \ + ) + .execute_reverts(|output| { + output + == b"Dispatched call failed with error: DispatchErrorWithPostInfo { \ post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes }, \ error: Module(ModuleError { index: 2, error: [10, 0, 0, 0], \ message: Some(\"Unapproved\") }) }" - ); + }); }); } @@ -928,81 +725,50 @@ fn transfer_from_self() { 1000 )); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, // Alice sending transferFrom herself, no need for allowance. + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 44001756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(44001756u64) // 1 weight => 1 gas in mock + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + ), + ) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -1027,72 +793,40 @@ fn get_metadata() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Name).build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() - .write::("TestToken".into()) - .build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Symbol).build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write::("Test".into()).build(), - cost: Default::default(), - logs: Default::default(), - })) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Name).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write::("TestToken".into()) + .build(), ); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Decimals).build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(12u8).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Symbol).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write::("Test".into()).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Decimals).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(12u8).build()); }); } @@ -1117,46 +851,28 @@ fn local_functions_cannot_be_accessed_by_foreign_assets() { 12, false )); - { - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Mint) - .write(Address(Account::Bob.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, .. })) - if output == b"unknown selector" - ); - }; - { - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Burn) - .write(Address(Account::Bob.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, .. })) - if output == b"unknown selector" - ); - }; + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Mint) + .write(Address(Account::Bob.into())) + .write(U256::from(400)) + .build(), + ) + .execute_reverts(|output| output == b"unknown selector"); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Burn) + .write(Address(Account::Bob.into())) + .write(U256::from(400)) + .build(), + ) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -1181,59 +897,36 @@ fn mint_local_assets() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Mint) - .write(Address(Account::Bob.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 26633756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::LocalAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Zero, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Mint) + .write(Address(Account::Bob.into())) + .write(U256::from(400)) + .build(), + ) + .expect_cost(26633756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::LocalAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Zero, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -1264,59 +957,36 @@ fn burn_local_assets() { Account::Alice.into(), 1000 )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Burn) - .write(Address(Account::Alice.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 30049756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::LocalAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Zero, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) - .write(Address(Account::Alice.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Burn) + .write(Address(Account::Alice.into())) + .write(U256::from(400)) + .build(), + ) + .expect_cost(30049756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::LocalAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Zero, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) + .write(Address(Account::Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); }); } @@ -1347,50 +1017,34 @@ fn freeze_local_assets() { Account::Bob.into(), 1000 )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Freeze) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 18309000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - assert_matches!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) - .write(Address(Account::Alice.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, ..})) - if from_utf8(&str).unwrap() - .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("Frozen") - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Freeze) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(18309000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) + .write(Address(Account::Alice.into())) + .write(U256::from(400)) + .build(), + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") + && from_utf8(&output).unwrap().contains("Frozen") + }); }); } @@ -1421,81 +1075,48 @@ fn thaw_local_assets() { Account::Bob.into(), 1000 )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Freeze) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 18309000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Thaw) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 18290000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) - .write(Address(Account::Alice.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 44001756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::LocalAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Bob, - Account::Alice, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Freeze) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(18309000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Thaw) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(18290000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) + .write(Address(Account::Alice.into())) + .write(U256::from(400)) + .build(), + ) + .expect_cost(44001756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::LocalAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Bob, + Account::Alice, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -1526,48 +1147,32 @@ fn freeze_asset_local_asset() { Account::Bob.into(), 1000 )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::FreezeAsset).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 14744000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - assert_matches!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) - .write(Address(Account::Alice.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, ..})) - if from_utf8(&str).unwrap() - .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("Frozen") - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::FreezeAsset).build(), + ) + .expect_cost(14744000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) + .write(Address(Account::Alice.into())) + .write(U256::from(400)) + .build(), + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") + && from_utf8(&output).unwrap().contains("Frozen") + }); }); } @@ -1598,77 +1203,44 @@ fn thaw_asset_local_assets() { Account::Bob.into(), 1000 )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::FreezeAsset).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 14744000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::ThawAsset).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 14833000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Transfer) - .write(Address(Account::Alice.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 44001756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::LocalAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Bob, - Account::Alice, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::FreezeAsset).build(), + ) + .expect_cost(14744000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::ThawAsset).build(), + ) + .expect_cost(14833000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Transfer) + .write(Address(Account::Alice.into())) + .write(U256::from(400)) + .build(), + ) + .expect_cost(44001756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::LocalAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Bob, + Account::Alice, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -1693,72 +1265,46 @@ fn transfer_ownership_local_assets() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferOwnership) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 16654000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - // Now Bob should be able to change ownership, and not Alice - assert_matches!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferOwnership) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, ..})) - if from_utf8(&str).unwrap() - .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("NoPermission") - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::TransferOwnership) - .write(Address(Account::Alice.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 16654000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferOwnership) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(16654000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Now Bob should be able to change ownership, and not Alice + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferOwnership) + .write(Address(Account::Bob.into())) + .build(), + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") + && from_utf8(&output).unwrap().contains("NoPermission") + }); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::TransferOwnership) + .write(Address(Account::Alice.into())) + .build(), + ) + .expect_cost(16654000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -1783,105 +1329,67 @@ fn set_team_local_assets() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::SetTeam) - .write(Address(Account::Bob.into())) - .write(Address(Account::Bob.into())) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 15351000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - // Now Bob should be able to mint, and not Alice - assert_matches!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Mint) - .write(Address(Account::Bob.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, ..})) - if from_utf8(&str).unwrap() - .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("NoPermission") - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Mint) - .write(Address(Account::Bob.into())) - .write(U256::from(400)) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 26633756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::LocalAssetId(0u128).into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Zero, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) - .write(Address(Account::Bob.into())) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Bob.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::SetTeam) + .write(Address(Account::Bob.into())) + .write(Address(Account::Bob.into())) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(15351000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Now Bob should be able to mint, and not Alice + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Mint) + .write(Address(Account::Bob.into())) + .write(U256::from(400)) + .build(), + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() + .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") + && from_utf8(&output).unwrap().contains("NoPermission") + }); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Mint) + .write(Address(Account::Bob.into())) + .write(U256::from(400)) + .build(), + ) + .expect_cost(26633756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::LocalAssetId(0u128).into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Zero, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Bob, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::BalanceOf) + .write(Address(Account::Bob.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -1906,96 +1414,54 @@ fn set_metadata() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::SetMetadata) - .write::("TestToken".into()) - .write::("Test".into()) - .write::(12) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 27654000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Name).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() - .write::("TestToken".into()) - .build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Symbol).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write::("Test".into()).build(), - cost: Default::default(), - logs: Default::default(), - })) + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::SetMetadata) + .write::("TestToken".into()) + .write::("Test".into()) + .write::(12) + .build(), + ) + .expect_cost(27654000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Name).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write::("TestToken".into()) + .build(), ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Decimals).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(12u8).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Symbol).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write::("Test".into()).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Decimals).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(12u8).build()); }); } @@ -2020,111 +1486,60 @@ fn clear_metadata() { 12, false )); - { - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::SetMetadata) - .write::("TestToken".into()) - .write::("Test".into()) - .write::(12) - .build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 27654000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::ClearMetadata).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 27710000u64, // 1 weight => 1 gas in mock - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Name).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write::("".into()).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Symbol).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write::("".into()).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::LocalAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Decimals).build(), - None, - &Context { - address: Account::LocalAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(0u8).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - }; + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::SetMetadata) + .write::("TestToken".into()) + .write::("Test".into()) + .write::(12) + .build(), + ) + .expect_cost(27654000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::ClearMetadata).build(), + ) + .expect_cost(27710000u64) // 1 weight => 1 gas in mock + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Name).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write::("".into()).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Symbol).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write::("".into()).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::LocalAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Decimals).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(0u8).build()); }); } @@ -2167,32 +1582,23 @@ fn permit_valid() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2201,73 +1607,42 @@ fn permit_valid() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - cost: 30831000u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(30831000u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(500)).build(), + ), + ) + .execute_returns(vec![]); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(500u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(500u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1u8)).build()); }); } @@ -2317,32 +1692,23 @@ fn permit_valid_named_asset() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2351,73 +1717,42 @@ fn permit_valid_named_asset() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - cost: 30831000u64, - logs: LogsBuilder::new(Account::ForeignAssetId(0u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(30831000u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(0u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(500)).build(), + ), + ) + .execute_returns(vec![]); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(500u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(500u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1u8)).build()); }); } @@ -2460,32 +1795,23 @@ fn permit_invalid_nonce() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2494,62 +1820,33 @@ fn permit_invalid_nonce() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"invalid permit" - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"invalid permit"); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -2578,32 +1875,23 @@ fn permit_invalid_signature() { let value: U256 = 500u16.into(); let deadline: U256 = 0u8.into(); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2612,62 +1900,33 @@ fn permit_invalid_signature() { .write(H256::random()) .write(H256::random()) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"invalid permit" - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"invalid permit"); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -2712,32 +1971,23 @@ fn permit_invalid_deadline() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2746,62 +1996,33 @@ fn permit_invalid_deadline() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"permit expired" - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"permit expired"); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(0u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::ForeignAssetId(0u128), + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::ForeignAssetId(0u128).into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -2962,10 +2183,11 @@ fn permit_valid_with_metamask_signed_data() { let r_real: [u8; 32] = r.try_into().unwrap(); let s_real: [u8; 32] = s.try_into().unwrap(); - assert_eq!( - precompiles().execute( - Account::ForeignAssetId(1u128).into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + precompiles() + .prepare_test( + Account::Charlie, + Account::ForeignAssetId(1u128), + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -2974,27 +2196,16 @@ fn permit_valid_with_metamask_signed_data() { .write(H256::from(r_real)) .write(H256::from(s_real)) .build(), - None, - &Context { - address: Account::ForeignAssetId(1u128).into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - cost: 30831000u64, - logs: LogsBuilder::new(Account::ForeignAssetId(1u128).into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(1000)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(30831000u64) + .expect_log( + LogsBuilder::new(Account::ForeignAssetId(1u128).into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(1000)).build(), + ), + ) + .execute_returns(vec![]); }); } diff --git a/precompiles/author-mapping/Cargo.toml b/precompiles/author-mapping/Cargo.toml index 1b713b871f6..3a1ea27e6db 100644 --- a/precompiles/author-mapping/Cargo.toml +++ b/precompiles/author-mapping/Cargo.toml @@ -15,7 +15,6 @@ precompile-utils = { path = "../utils", default-features = false } # Substrate codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } @@ -23,6 +22,7 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } # Frontier +fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } # Nimbus diff --git a/precompiles/author-mapping/src/lib.rs b/precompiles/author-mapping/src/lib.rs index 5ca26bc3bbd..11dfce9c965 100644 --- a/precompiles/author-mapping/src/lib.rs +++ b/precompiles/author-mapping/src/lib.rs @@ -19,13 +19,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(assert_matches)] -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; use nimbus_primitives::NimbusId; use pallet_author_mapping::Call as AuthorMappingCall; -use pallet_evm::AddressMapping; -use pallet_evm::Precompile; -use precompile_utils::{EvmDataReader, EvmResult, FunctionModifier, Gasometer, RuntimeHelper}; +use pallet_evm::{AddressMapping, Precompile}; +use precompile_utils::{succeed, EvmResult, FunctionModifier, PrecompileHandleExt, RuntimeHelper}; use sp_core::crypto::UncheckedFrom; use sp_core::H256; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -48,6 +47,7 @@ pub enum Action { /// A precompile to wrap the functionality from pallet author mapping. pub struct AuthorMappingWrapper(PhantomData); +// TODO: Migrate to precompile_utils::Precompile. impl Precompile for AuthorMappingWrapper where Runtime: pallet_author_mapping::Config + pallet_evm::Config + frame_system::Config, @@ -56,29 +56,20 @@ where Runtime::Call: From>, Runtime::Hash: From, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { log::trace!(target: "author-mapping-precompile", "In author mapping wrapper"); - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; + let selector = handle.read_selector()?; - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier(context, is_static, FunctionModifier::NonPayable)?; + handle.check_function_modifier(FunctionModifier::NonPayable)?; match selector { // Dispatchables - Action::AddAssociation => Self::add_association(input, gasometer, context), - Action::UpdateAssociation => Self::update_association(input, gasometer, context), - Action::ClearAssociation => Self::clear_association(input, gasometer, context), - Action::RegisterKeys => Self::register_keys(input, gasometer, context), - Action::SetKeys => Self::set_keys(input, gasometer, context), + Action::AddAssociation => Self::add_association(handle), + Action::UpdateAssociation => Self::update_association(handle), + Action::ClearAssociation => Self::clear_association(handle), + Action::RegisterKeys => Self::register_keys(handle), + Action::SetKeys => Self::set_keys(handle), } } } @@ -92,112 +83,81 @@ where Runtime::Hash: From, { // The dispatchable wrappers are next. They dispatch a Substrate inner Call. - fn add_association( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn add_association(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + // Bound check - input.expect_arguments(gasometer, 1)?; + input.expect_arguments(1)?; - let nimbus_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + let nimbus_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); log::trace!( target: "author-mapping-precompile", "Associating author id {:?}", nimbus_id ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = AuthorMappingCall::::add_association { author_id: nimbus_id, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn update_association( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn update_association(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 2)?; + input.expect_arguments(2)?; - let old_nimbus_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); - let new_nimbus_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + let old_nimbus_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); + let new_nimbus_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); log::trace!( target: "author-mapping-precompile", "Updating author id {:?} for {:?}", old_nimbus_id, new_nimbus_id ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = AuthorMappingCall::::update_association { old_author_id: old_nimbus_id, new_author_id: new_nimbus_id, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn clear_association( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn clear_association(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 1)?; - let nimbus_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + input.expect_arguments(1)?; + let nimbus_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); log::trace!( target: "author-mapping-precompile", "Clearing author id {:?}", nimbus_id ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = AuthorMappingCall::::clear_association { author_id: nimbus_id, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn register_keys( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn register_keys(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 2)?; - let nimbus_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + input.expect_arguments(2)?; + let nimbus_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); let keys_as_nimbus_id: NimbusId = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); let keys: ::Keys = keys_as_nimbus_id.into(); log::trace!( @@ -205,31 +165,22 @@ where "Adding full association with author id {:?} keys {:?}", nimbus_id, keys ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = AuthorMappingCall::::register_keys { keys: (nimbus_id, keys), }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn set_keys( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - input.expect_arguments(gasometer, 2)?; - let new_author_id = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + fn set_keys(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(2)?; + let new_author_id = sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); let new_keys_as_nimbus_id: NimbusId = - sp_core::sr25519::Public::unchecked_from(input.read::(gasometer)?).into(); + sp_core::sr25519::Public::unchecked_from(input.read::()?).into(); let new_keys: ::Keys = new_keys_as_nimbus_id.into(); @@ -239,18 +190,13 @@ where new_author_id, new_keys ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = AuthorMappingCall::::set_keys { keys: (new_author_id, new_keys), }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } } diff --git a/precompiles/author-mapping/src/mock.rs b/precompiles/author-mapping/src/mock.rs index 6429e314ab9..9081a51bbc6 100644 --- a/precompiles/author-mapping/src/mock.rs +++ b/precompiles/author-mapping/src/mock.rs @@ -34,7 +34,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, }; -pub type AccountId = TestAccount; +pub type AccountId = Account; pub type Balance = u128; pub type BlockNumber = u64; @@ -58,7 +58,7 @@ pub const PRECOMPILE_ADDRESS: u64 = 1; derive_more::Display, TypeInfo, )] -pub enum TestAccount { +pub enum Account { Alice, Bob, Charlie, @@ -66,14 +66,14 @@ pub enum TestAccount { Precompile, } -impl Default for TestAccount { +impl Default for Account { fn default() -> Self { Self::Bogus } } -impl AddressMapping for TestAccount { - fn into_account_id(h160_account: H160) -> TestAccount { +impl AddressMapping for Account { + fn into_account_id(h160_account: H160) -> Account { match h160_account { a if a == H160::repeat_byte(0xAA) => Self::Alice, a if a == H160::repeat_byte(0xBB) => Self::Bob, @@ -84,20 +84,20 @@ impl AddressMapping for TestAccount { } } -impl From for TestAccount { - fn from(x: H160) -> TestAccount { - TestAccount::into_account_id(x) +impl From for Account { + fn from(x: H160) -> Account { + Account::into_account_id(x) } } -impl From for H160 { - fn from(value: TestAccount) -> H160 { +impl From for H160 { + fn from(value: Account) -> H160 { match value { - TestAccount::Alice => H160::repeat_byte(0xAA), - TestAccount::Bob => H160::repeat_byte(0xBB), - TestAccount::Charlie => H160::repeat_byte(0xCC), - TestAccount::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), - TestAccount::Bogus => Default::default(), + Account::Alice => H160::repeat_byte(0xAA), + Account::Bob => H160::repeat_byte(0xBB), + Account::Charlie => H160::repeat_byte(0xCC), + Account::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), + Account::Bogus => Default::default(), } } } @@ -131,7 +131,7 @@ impl frame_system::Config for Runtime { type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = TestAccount; + type AccountId = Account; type Lookup = IdentityLookup; type Header = Header; type Event = Event; @@ -170,9 +170,9 @@ parameter_types! { impl pallet_evm::Config for Runtime { type FeeCalculator = (); type GasWeightMapping = (); - type CallOrigin = EnsureAddressRoot; - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = TestAccount; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = Account; type Currency = Balances; type Event = Event; type Runner = pallet_evm::runner::stack::Runner; @@ -214,7 +214,7 @@ impl pallet_scheduler::Config for Runtime { type PalletsOrigin = OriginCaller; type Call = Call; type MaximumWeight = (); - type ScheduleOrigin = EnsureRoot; + type ScheduleOrigin = EnsureRoot; type MaxScheduledPerBlock = (); type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; // TODO : Simplest type, maybe there is better ? @@ -229,18 +229,9 @@ impl PrecompileSet for Precompiles where AuthorMappingWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == hash(PRECOMPILE_ADDRESS) => Some(AuthorMappingWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == hash(PRECOMPILE_ADDRESS) => Some(AuthorMappingWrapper::::execute(handle)), _ => None, } } @@ -295,51 +286,31 @@ pub(crate) fn events() -> Vec { .collect::>() } -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} - #[test] fn test_account_id_mapping_works() { // Bidirectional conversions for normal accounts assert_eq!( - TestAccount::Alice, - TestAccount::into_account_id(TestAccount::Alice.into()) - ); - assert_eq!( - TestAccount::Bob, - TestAccount::into_account_id(TestAccount::Bob.into()) + Account::Alice, + Account::into_account_id(Account::Alice.into()) ); + assert_eq!(Account::Bob, Account::into_account_id(Account::Bob.into())); assert_eq!( - TestAccount::Charlie, - TestAccount::into_account_id(TestAccount::Charlie.into()) + Account::Charlie, + Account::into_account_id(Account::Charlie.into()) ); // Bidirectional conversion between bogus and default H160 - assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::default()) - ); - assert_eq!(H160::default(), TestAccount::Bogus.into()); + assert_eq!(Account::Bogus, Account::into_account_id(H160::default())); + assert_eq!(H160::default(), Account::Bogus.into()); // All other H160s map to bogus + assert_eq!(Account::Bogus, Account::into_account_id(H160::zero())); assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::zero()) - ); - assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::repeat_byte(0x12)) + Account::Bogus, + Account::into_account_id(H160::repeat_byte(0x12)) ); assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::repeat_byte(0xFF)) + Account::Bogus, + Account::into_account_id(H160::repeat_byte(0xFF)) ); } diff --git a/precompiles/author-mapping/src/tests.rs b/precompiles/author-mapping/src/tests.rs index ba98e8f9932..bed92094c40 100644 --- a/precompiles/author-mapping/src/tests.rs +++ b/precompiles/author-mapping/src/tests.rs @@ -16,21 +16,20 @@ use crate::{ mock::{ - events, evm_test_context, Call, ExtBuilder, Origin, Precompiles, PrecompilesValue, Runtime, - TestAccount::{Alice, Precompile}, + events, + Account::{Alice, Precompile}, + Call, ExtBuilder, Origin, Precompiles, PrecompilesValue, Runtime, }, Action, }; -use fp_evm::PrecompileFailure; use frame_support::{assert_ok, dispatch::Dispatchable}; use nimbus_primitives::NimbusId; use pallet_author_mapping::{Call as AuthorMappingCall, Event as AuthorMappingEvent}; use pallet_balances::Event as BalancesEvent; -use pallet_evm::{Call as EvmCall, Event as EvmEvent, PrecompileSet}; -use precompile_utils::EvmDataWriter; +use pallet_evm::{Call as EvmCall, Event as EvmEvent}; +use precompile_utils::{testing::*, EvmDataWriter}; use sp_core::crypto::UncheckedFrom; use sp_core::U256; -use std::assert_matches::assert_matches; fn precompiles() -> Precompiles { PrecompilesValue::get() @@ -54,38 +53,18 @@ fn evm_call(input: Vec) -> EvmCall { fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"tried to parse selector out of bounds", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"unknown selector", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } diff --git a/precompiles/balances-erc20/src/eip2612.rs b/precompiles/balances-erc20/src/eip2612.rs index f08899cd15f..b38e81f1021 100644 --- a/precompiles/balances-erc20/src/eip2612.rs +++ b/precompiles/balances-erc20/src/eip2612.rs @@ -90,29 +90,33 @@ where // Translated from // https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2ERC20.sol#L81 - pub(crate) fn permit( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; - - let owner: H160 = input.read::
(gasometer)?.into(); - let spender: H160 = input.read::
(gasometer)?.into(); - let value: U256 = input.read(gasometer)?; - let deadline: U256 = input.read(gasometer)?; - let v: u8 = input.read(gasometer)?; - let r: H256 = input.read(gasometer)?; - let s: H256 = input.read(gasometer)?; + pub(crate) fn permit(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let mut input = EvmDataReader::new_skip_selector(handle.input())?; + let owner: H160 = input.read::
()?.into(); + let spender: H160 = input.read::
()?.into(); + let value: U256 = input.read()?; + let deadline: U256 = input.read()?; + let v: u8 = input.read()?; + let r: H256 = input.read()?; + let s: H256 = input.read()?; // pallet_timestamp is in ms while Ethereum use second timestamps. let timestamp: U256 = (pallet_timestamp::Pallet::::get()).into() / 1000; - ensure!(deadline >= timestamp, gasometer.revert("permit expired")); + ensure!(deadline >= timestamp, revert("permit expired")); let nonce = NoncesStorage::::get(owner); - let permit = Self::generate_permit(context.address, owner, spender, value, nonce, deadline); + let permit = Self::generate_permit( + handle.context().address, + owner, + spender, + value, + nonce, + deadline, + ); let mut sig = [0u8; 65]; sig[0..32].copy_from_slice(&r.as_bytes()); @@ -120,74 +124,59 @@ where sig[64] = v; let signer = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &permit) - .map_err(|_| gasometer.revert("invalid permit"))?; + .map_err(|_| revert("invalid permit"))?; let signer = H160::from(H256::from_slice(keccak_256(&signer).as_slice())); ensure!( signer != H160::zero() && signer == owner, - gasometer.revert("invalid permit") + revert("invalid permit") ); NoncesStorage::::insert(owner, nonce + U256::one()); { - let amount = Erc20BalancesPrecompile::::u256_to_amount( - &mut gasometer.clone(), - value, - ) - .unwrap_or_else(|_| Bounded::max_value()); + let amount = + Erc20BalancesPrecompile::::u256_to_amount(value) + .unwrap_or_else(|_| Bounded::max_value()); let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner); let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender); ApprovesStorage::::insert(owner, spender, amount); } - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: vec![], - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_APPROVAL, - owner, - spender, - EvmDataWriter::new().write(value).build(), - ) - .build(), - }) + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_APPROVAL, + owner, + spender, + EvmDataWriter::new().write(value).build(), + ) + .record(handle)?; + + Ok(succeed([])) } - pub(crate) fn nonces( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + pub(crate) fn nonces(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let owner: H160 = input.read::
(gasometer)?.into(); + let mut input = EvmDataReader::new_skip_selector(handle.input())?; + let owner: H160 = input.read::
()?.into(); let nonce = NoncesStorage::::get(owner); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(nonce).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(nonce).build())) } pub(crate) fn domain_separator( - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let domain_separator: H256 = Self::compute_domain_separator(context.address).into(); + let domain_separator: H256 = + Self::compute_domain_separator(handle.context().address).into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(domain_separator).build(), - logs: vec![], - }) + Ok(succeed( + EvmDataWriter::new().write(domain_separator).build(), + )) } } diff --git a/precompiles/balances-erc20/src/lib.rs b/precompiles/balances-erc20/src/lib.rs index 198a632b1ab..2c13236705c 100644 --- a/precompiles/balances-erc20/src/lib.rs +++ b/precompiles/balances-erc20/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(test, feature(assert_matches))] -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, sp_runtime::traits::{Bounded, CheckedSub, StaticLookup}, @@ -31,16 +31,15 @@ use pallet_balances::pallet::{ Instance1, Instance10, Instance11, Instance12, Instance13, Instance14, Instance15, Instance16, Instance2, Instance3, Instance4, Instance5, Instance6, Instance7, Instance8, Instance9, }; -use pallet_evm::{AddressMapping, Precompile}; +use pallet_evm::AddressMapping; use precompile_utils::{ - keccak256, Address, Bytes, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, - Gasometer, LogsBuilder, RuntimeHelper, + keccak256, revert, succeed, Address, Bytes, EvmDataReader, EvmDataWriter, EvmResult, + FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt, RuntimeHelper, }; use sp_core::{H160, U256}; use sp_std::{ convert::{TryFrom, TryInto}, marker::PhantomData, - vec, }; mod eip2612; @@ -201,6 +200,7 @@ pub struct Erc20BalancesPrecompile, ); +// TODO: Migrate to precompile_utils::Precompile. impl Precompile for Erc20BalancesPrecompile where @@ -213,53 +213,37 @@ where BalanceOf: TryFrom + Into, ::Moment: Into, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; - - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input) - .unwrap_or_else(|_| (EvmDataReader::new(input), Action::Deposit)); - let input = &mut input; - - gasometer.check_function_modifier( - context, - is_static, - match selector { - Action::Approve | Action::Transfer | Action::TransferFrom | Action::Withdraw => { - FunctionModifier::NonPayable - } - Action::Deposit => FunctionModifier::Payable, - _ => FunctionModifier::View, - }, - )?; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector().unwrap_or_else(|_| Action::Deposit); + + handle.check_function_modifier(match selector { + Action::Approve | Action::Transfer | Action::TransferFrom | Action::Withdraw => { + FunctionModifier::NonPayable + } + Action::Deposit => FunctionModifier::Payable, + _ => FunctionModifier::View, + })?; match selector { - Action::TotalSupply => Self::total_supply(input, gasometer), - Action::BalanceOf => Self::balance_of(input, gasometer), - Action::Allowance => Self::allowance(input, gasometer), - Action::Approve => Self::approve(input, gasometer, context), - Action::Transfer => Self::transfer(input, gasometer, context), - Action::TransferFrom => Self::transfer_from(input, gasometer, context), - Action::Name => Self::name(input, gasometer, context), - Action::Symbol => Self::symbol(input, gasometer, context), - Action::Decimals => Self::decimals(input, gasometer, context), - Action::Deposit => Self::deposit(input, gasometer, context), - Action::Withdraw => Self::withdraw(input, gasometer, context), + Action::TotalSupply => Self::total_supply(handle), + Action::BalanceOf => Self::balance_of(handle), + Action::Allowance => Self::allowance(handle), + Action::Approve => Self::approve(handle), + Action::Transfer => Self::transfer(handle), + Action::TransferFrom => Self::transfer_from(handle), + Action::Name => Self::name(), + Action::Symbol => Self::symbol(), + Action::Decimals => Self::decimals(), + Action::Deposit => Self::deposit(handle), + Action::Withdraw => Self::withdraw(handle), Action::Eip2612Permit => { - eip2612::Eip2612::::permit(input, gasometer, context) + eip2612::Eip2612::::permit(handle) } Action::Eip2612Nonces => { - eip2612::Eip2612::::nonces(input, gasometer) + eip2612::Eip2612::::nonces(handle) } Action::Eip2612DomainSeparator => { - eip2612::Eip2612::::domain_separator( - gasometer, context, - ) + eip2612::Eip2612::::domain_separator(handle) } } } @@ -276,37 +260,28 @@ where BalanceOf: TryFrom + Into, ::Moment: Into, { - fn total_supply( - input: &EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn total_supply(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Parse input. - input.expect_arguments(gasometer, 0)?; + let input = handle.read_input()?; + input.expect_arguments(0)?; // Fetch info. let amount: U256 = pallet_balances::Pallet::::total_issuance().into(); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } - fn balance_of( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn balance_of(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Read input. - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let owner: H160 = input.read::
(gasometer)?.into(); + let owner: H160 = input.read::
()?.into(); // Fetch info. let amount: U256 = { @@ -315,25 +290,18 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } - fn allowance( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn allowance(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Read input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let owner: H160 = input.read::
(gasometer)?.into(); - let spender: H160 = input.read::
(gasometer)?.into(); + let owner: H160 = input.read::
()?.into(); + let spender: H160 = input.read::
()?.into(); // Fetch info. let amount: U256 = { @@ -346,304 +314,246 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(amount).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(amount).build())) } - fn approve( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_write_gas_cost())?; - gasometer.record_log_costs_manual(3, 32)?; + fn approve(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let spender: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; + let spender: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; // Write into storage. { let caller: Runtime::AccountId = - Runtime::AddressMapping::into_account_id(context.caller); + Runtime::AddressMapping::into_account_id(handle.context().caller); let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender); // Amount saturate if too high. - let amount = Self::u256_to_amount(&mut gasometer.clone(), amount) - .unwrap_or_else(|_| Bounded::max_value()); + let amount = Self::u256_to_amount(amount).unwrap_or_else(|_| Bounded::max_value()); ApprovesStorage::::insert(caller, spender, amount); } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_APPROVAL, + handle.context().caller, + spender, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_APPROVAL, - context.caller, - spender, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } - fn transfer( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - gasometer.record_log_costs_manual(3, 32)?; + fn transfer(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let to: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; + let to: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; // Build call with origin. { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to = Runtime::AddressMapping::into_account_id(to); - let amount = Self::u256_to_amount(gasometer, amount)?; + let amount = Self::u256_to_amount(amount)?; // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(origin).into(), pallet_balances::Call::::transfer { dest: Runtime::Lookup::unlookup(to), value: amount, }, - gasometer, )?; } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + handle.context().caller, + to, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - context.caller, - to, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } - fn transfer_from( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; - gasometer.record_cost(RuntimeHelper::::db_write_gas_cost())?; - gasometer.record_log_costs_manual(3, 32)?; + fn transfer_from(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_log_costs_manual(3, 32)?; // Parse input. - input.expect_arguments(gasometer, 3)?; - let from: H160 = input.read::
(gasometer)?.into(); - let to: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; + let mut input = handle.read_input()?; + input.expect_arguments(3)?; + let from: H160 = input.read::
()?.into(); + let to: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; { let caller: Runtime::AccountId = - Runtime::AddressMapping::into_account_id(context.caller); + Runtime::AddressMapping::into_account_id(handle.context().caller); let from: Runtime::AccountId = Runtime::AddressMapping::into_account_id(from); let to: Runtime::AccountId = Runtime::AddressMapping::into_account_id(to); - let amount = Self::u256_to_amount(gasometer, amount)?; + let amount = Self::u256_to_amount(amount)?; // If caller is "from", it can spend as much as it wants. if caller != from { ApprovesStorage::::mutate(from.clone(), caller, |entry| { // Get current value, exit if None. - let value = entry.ok_or(gasometer.revert("spender not allowed"))?; + let value = entry.ok_or(revert("spender not allowed"))?; // Remove "amount" from allowed, exit if underflow. let new_value = value .checked_sub(&amount) - .ok_or_else(|| gasometer.revert("trying to spend more than allowed"))?; + .ok_or_else(|| revert("trying to spend more than allowed"))?; // Update value. *entry = Some(new_value); - Ok(()) + EvmResult::Ok(()) })?; } // Build call with origin. Here origin is the "from"/owner field. // Dispatch call (if enough gas). RuntimeHelper::::try_dispatch( + handle, Some(from).into(), pallet_balances::Call::::transfer { dest: Runtime::Lookup::unlookup(to), value: amount, }, - gasometer, )?; } + LogsBuilder::new(handle.context().address) + .log3( + SELECTOR_LOG_TRANSFER, + from, + to, + EvmDataWriter::new().write(amount).build(), + ) + .record(handle)?; + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(true).build(), - logs: LogsBuilder::new(context.address) - .log3( - SELECTOR_LOG_TRANSFER, - from, - to, - EvmDataWriter::new().write(amount).build(), - ) - .build(), - }) + Ok(succeed(EvmDataWriter::new().write(true).build())) } - fn name( - _: &mut EvmDataReader, - gasometer: &mut Gasometer, - _: &Context, - ) -> EvmResult { + fn name() -> EvmResult { // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write::(Metadata::name().into()) .build(), - logs: Default::default(), - }) + )) } - fn symbol( - _: &mut EvmDataReader, - gasometer: &mut Gasometer, - _: &Context, - ) -> EvmResult { + fn symbol() -> EvmResult { // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write::(Metadata::symbol().into()) .build(), - logs: Default::default(), - }) + )) } - fn decimals( - _: &mut EvmDataReader, - gasometer: &mut Gasometer, - _: &Context, - ) -> EvmResult { + fn decimals() -> EvmResult { // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(Metadata::decimals()).build(), - logs: Default::default(), - }) + Ok(succeed( + EvmDataWriter::new().write(Metadata::decimals()).build(), + )) } - fn deposit( - _: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn deposit(handle: &mut impl PrecompileHandle) -> EvmResult { // Deposit only makes sense for the native currency. if !Metadata::is_native_currency() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } - let caller: Runtime::AccountId = Runtime::AddressMapping::into_account_id(context.caller); - let precompile = Runtime::AddressMapping::into_account_id(context.address); - let amount = Self::u256_to_amount(gasometer, context.apparent_value)?; + let caller: Runtime::AccountId = + Runtime::AddressMapping::into_account_id(handle.context().caller); + let precompile = Runtime::AddressMapping::into_account_id(handle.context().address); + let amount = Self::u256_to_amount(handle.context().apparent_value)?; if amount.into() == U256::from(0u32) { - return Err(gasometer.revert("deposited amount must be non-zero")); + return Err(revert("deposited amount must be non-zero")); } - gasometer.record_log_costs_manual(2, 32)?; + handle.record_log_costs_manual(2, 32)?; // Send back funds received by the precompile. RuntimeHelper::::try_dispatch( + handle, Some(precompile).into(), pallet_balances::Call::::transfer { dest: Runtime::Lookup::unlookup(caller), value: amount, }, - gasometer, )?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: LogsBuilder::new(context.address) - .log2( - SELECTOR_LOG_DEPOSIT, - context.caller, - EvmDataWriter::new().write(context.apparent_value).build(), - ) - .build(), - }) + LogsBuilder::new(handle.context().address) + .log2( + SELECTOR_LOG_DEPOSIT, + handle.context().caller, + EvmDataWriter::new() + .write(handle.context().apparent_value) + .build(), + ) + .record(handle)?; + + Ok(succeed([])) } - fn withdraw( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn withdraw(handle: &mut impl PrecompileHandle) -> EvmResult { // Withdraw only makes sense for the native currency. if !Metadata::is_native_currency() { - return Err(gasometer.revert("unknown selector")); + return Err(revert("unknown selector")); } - gasometer.record_log_costs_manual(2, 32)?; + handle.record_log_costs_manual(2, 32)?; - let withdrawn_amount: U256 = input.read(gasometer)?; + let mut input = handle.read_input()?; + let withdrawn_amount: U256 = input.read()?; let account_amount: U256 = { let owner: Runtime::AccountId = - Runtime::AddressMapping::into_account_id(context.caller); + Runtime::AddressMapping::into_account_id(handle.context().caller); pallet_balances::Pallet::::usable_balance(&owner).into() }; if withdrawn_amount > account_amount { - return Err(gasometer.revert("trying to withdraw more than owned")); + return Err(revert("trying to withdraw more than owned")); } - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: LogsBuilder::new(context.address) - .log2( - SELECTOR_LOG_WITHDRAWAL, - context.caller, - EvmDataWriter::new().write(withdrawn_amount).build(), - ) - .build(), - }) + LogsBuilder::new(handle.context().address) + .log2( + SELECTOR_LOG_WITHDRAWAL, + handle.context().caller, + EvmDataWriter::new().write(withdrawn_amount).build(), + ) + .record(handle)?; + + Ok(succeed([])) } - fn u256_to_amount( - gasometer: &mut Gasometer, - value: U256, - ) -> EvmResult> { + fn u256_to_amount(value: U256) -> EvmResult> { value .try_into() - .map_err(|_| gasometer.revert("amount is too large for provided balance type")) + .map_err(|_| revert("amount is too large for provided balance type")) } } diff --git a/precompiles/balances-erc20/src/mock.rs b/precompiles/balances-erc20/src/mock.rs index 05ee6b833c9..2425b543ddc 100644 --- a/precompiles/balances-erc20/src/mock.rs +++ b/precompiles/balances-erc20/src/mock.rs @@ -241,20 +241,12 @@ impl PrecompileSet for Precompiles where Erc20BalancesPrecompile: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == hash(PRECOMPILE_ADDRESS) => { - Some(Erc20BalancesPrecompile::::execute( - input, target_gas, context, is_static, - )) - } + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == hash(PRECOMPILE_ADDRESS) => Some(Erc20BalancesPrecompile::< + R, + NativeErc20Metadata, + >::execute(handle)), _ => None, } } diff --git a/precompiles/balances-erc20/src/tests.rs b/precompiles/balances-erc20/src/tests.rs index 95cb8de5fb7..67ea9cf2baf 100644 --- a/precompiles/balances-erc20/src/tests.rs +++ b/precompiles/balances-erc20/src/tests.rs @@ -14,14 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . -use std::{assert_matches::assert_matches, str::from_utf8}; +use std::str::from_utf8; use crate::{eip2612::Eip2612, mock::*, *}; -use fp_evm::{Context, PrecompileFailure}; use libsecp256k1::{sign, Message, SecretKey}; -use pallet_evm::PrecompileSet; -use precompile_utils::{Bytes, EvmDataWriter, LogsBuilder}; +use precompile_utils::{testing::*, Bytes, EvmDataWriter, LogsBuilder}; use sha3::{Digest, Keccak256}; use sp_core::{H256, U256}; @@ -73,25 +71,15 @@ fn get_total_supply() { .with_balances(vec![(Account::Alice, 1000), (Account::Bob, 2500)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TotalSupply).build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(3500u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::TotalSupply).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(3500u64)).build()); }); } @@ -101,27 +89,17 @@ fn get_balances_known_user() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000u64)).build()); }); } @@ -131,27 +109,17 @@ fn get_balances_unknown_user() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u64)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u64)).build()); }); } @@ -161,35 +129,23 @@ fn approve() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::from(500)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 1756u64, - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(1756) + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(500)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -199,58 +155,36 @@ fn approve_saturating() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Approve) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Approve) .write(Address(Account::Bob.into())) .write(U256::MAX) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 1756u64, - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::MAX).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(1756u64) + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::MAX).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(u128::MAX)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(u128::MAX)).build()); }); } @@ -260,43 +194,29 @@ fn check_allowance_existing() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(500)) - .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(500)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(500u64)).build(), - cost: 0u64, - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(500u64)).build()); }); } @@ -306,28 +226,18 @@ fn check_allowance_not_existing() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u64)).build(), - cost: 0u64, - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u64)).build()); }); } @@ -337,79 +247,47 @@ fn transfer() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 159201756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(159201756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -419,26 +297,21 @@ fn transfer_not_enough_funds() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(Account::Bob.into())) .write(U256::from(1400)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, .. })) - if from_utf8(&str).unwrap() + ) + .execute_reverts(|output| { + from_utf8(&output) + .unwrap() .contains("Dispatched call failed with error: DispatchErrorWithPostInfo") - && from_utf8(&str).unwrap().contains("InsufficientBalance") - ); + && from_utf8(&output).unwrap().contains("InsufficientBalance") + }); }); } @@ -448,118 +321,72 @@ fn transfer_from() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(500)) - .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(500)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Bob, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Bob.into(), // Bob is the one sending transferFrom! - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 159201756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(159201756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(100u64)).build(), - cost: 0u64, - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(100u64)).build()); }); } @@ -569,40 +396,28 @@ fn transfer_from_above_allowance() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Approve) - .write(Address(Account::Bob.into())) - .write(U256::from(300)) - .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ); - - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Approve) + .write(Address(Account::Bob.into())) + .write(U256::from(300)) + .build(), + ) + .execute_some(); + + precompiles() + .prepare_test( + Account::Bob, // Bob is the one sending transferFrom! + Account::Precompile, + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Bob.into(), // Bob is the one sending transferFrom! - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"trying to spend more than allowed", - ); + ) + .execute_reverts(|output| output == b"trying to spend more than allowed"); }); } @@ -612,81 +427,48 @@ fn transfer_from_self() { .with_balances(vec![(Account::Alice, 1000)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferFrom) + precompiles() + .prepare_test( + Account::Alice, // Alice sending transferFrom herself, no need for allowance. + Account::Precompile, + EvmDataWriter::new_with_selector(Action::TransferFrom) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .write(U256::from(400)) .build(), - None, - &Context { - address: Account::Precompile.into(), - // Alice sending transferFrom herself, no need for allowance. - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 159201756u64, // 1 weight => 1 gas in mock - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_TRANSFER, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(400)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(159201756u64) // 1 weight => 1 gas in mock + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_TRANSFER, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(400)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(600)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(600)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400)).build()); }); } @@ -696,27 +478,19 @@ fn get_metadata_name() { .with_balances(vec![(Account::Alice, 1000), (Account::Bob, 2500)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Name).build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Name).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() .write::("Mock token".into()) .build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ); }); } @@ -726,25 +500,15 @@ fn get_metadata_symbol() { .with_balances(vec![(Account::Alice, 1000), (Account::Bob, 2500)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Symbol).build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write::("MOCK".into()).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Symbol).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write::("MOCK".into()).build()); }); } @@ -754,25 +518,15 @@ fn get_metadata_decimals() { .with_balances(vec![(Account::Alice, 1000), (Account::Bob, 2500)]) .build() .execute_with(|| { - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Decimals).build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(18u8).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Decimals).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(18u8).build()); }); } @@ -782,27 +536,17 @@ fn deposit(data: Vec) { .build() .execute_with(|| { // Check precompile balance is 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Deposit // We need to call using EVM pallet so we can check the EVM correctly sends the amount @@ -845,64 +589,41 @@ fn deposit(data: Vec) { }), // Log is correctly emited. Event::Evm(pallet_evm::Event::Log( - LogsBuilder::new(Account::Precompile.into()) - .log2( - SELECTOR_LOG_DEPOSIT, - Account::Alice, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build()[0] - .clone() + LogsBuilder::new(Account::Precompile.into()).log2( + SELECTOR_LOG_DEPOSIT, + Account::Alice, + EvmDataWriter::new().write(U256::from(500)).build(), + ) )), Event::Evm(pallet_evm::Event::Executed(Account::Precompile.into())), ] ); // Check precompile balance is still 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Check Alice balance is still 1000. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000)).build()); }); } @@ -928,27 +649,17 @@ fn deposit_zero() { .build() .execute_with(|| { // Check precompile balance is 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Deposit // We need to call using EVM pallet so we can check the EVM correctly sends the amount @@ -975,50 +686,30 @@ fn deposit_zero() { ); // Check precompile balance is still 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Check Alice balance is still 1000. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000)).build()); }); } @@ -1029,79 +720,47 @@ fn withdraw() { .build() .execute_with(|| { // Check precompile balance is 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Withdraw - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Withdraw) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Withdraw) .write(U256::from(500)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().build(), - cost: 1381, - logs: LogsBuilder::new(Account::Precompile.into()) - .log2( - SELECTOR_LOG_WITHDRAWAL, - Account::Alice, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(1381) + .expect_log(LogsBuilder::new(Account::Precompile.into()).log2( + SELECTOR_LOG_WITHDRAWAL, + Account::Alice, + EvmDataWriter::new().write(U256::from(500)).build(), + )) + .execute_returns(vec![]); // Check Alice balance is still 1000. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000)).build()); }); } @@ -1112,69 +771,41 @@ fn withdraw_more_than_owned() { .build() .execute_with(|| { // Check precompile balance is 0. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Precompile.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0)).build()); // Withdraw - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Withdraw) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Withdraw) .write(U256::from(1001)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output: str, .. })) - if str == b"trying to withdraw more than owned" - ); + ) + .execute_reverts(|output| output == b"trying to withdraw more than owned"); // Check Alice balance is still 1000. - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::BalanceOf) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::BalanceOf) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000)).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000)).build()); }); } @@ -1202,32 +833,23 @@ fn permit_valid() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, // can be anyone + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -1236,73 +858,40 @@ fn permit_valid() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - cost: 0u64, - logs: LogsBuilder::new(Account::Precompile.into()) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(500)).build(), - ) - .build(), - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(value)).build(), + )) + .execute_returns(vec![]); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(500u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(500u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1u8)).build()); }); } @@ -1330,32 +919,23 @@ fn permit_invalid_nonce() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, // can be anyone + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -1364,62 +944,33 @@ fn permit_invalid_nonce() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"invalid permit" - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"invalid permit"); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -1434,96 +985,58 @@ fn permit_invalid_signature() { let value: U256 = 500u16.into(); let deadline: U256 = 0u8.into(); - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, // can be anyone + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) .write(deadline) .write(0u8) - .write(H256::random()) - .write(H256::random()) + .write(H256::repeat_byte(0x11)) + .write(H256::repeat_byte(0x11)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"invalid permit" - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"invalid permit"); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -1553,32 +1066,23 @@ fn permit_invalid_deadline() { let message = Message::parse(&permit); let (rs, v) = sign(&message, &secret_key); - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_matches!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); + + precompiles() + .prepare_test( + Account::Charlie, // can be anyone + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -1587,62 +1091,33 @@ fn permit_invalid_deadline() { .write(H256::from(rs.r.b32())) .write(H256::from(rs.s.b32())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"permit expired" - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Allowance) + ) + .execute_reverts(|output| output == b"permit expired"); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Allowance) .write(Address(Account::Alice.into())) .write(Address(Account::Bob.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u16)).build(), - cost: 0u64, - logs: vec![], - })) - ); - - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Nonces) + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u16)).build()); + + precompiles() + .prepare_test( + Account::Alice, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Nonces) .write(Address(Account::Alice.into())) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(0u8)).build(), - cost: 0u64, - logs: vec![], - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(0u8)).build()); }); } @@ -1788,10 +1263,11 @@ fn permit_valid_with_metamask_signed_data() { let r_real: [u8; 32] = r.try_into().unwrap(); let s_real: [u8; 32] = s.try_into().unwrap(); - assert_eq!( - precompiles().execute( - Account::Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Eip2612Permit) + precompiles() + .prepare_test( + Account::Charlie, // can be anyone, + Account::Precompile, + EvmDataWriter::new_with_selector(Action::Eip2612Permit) .write(Address(owner)) .write(Address(spender)) .write(value) @@ -1800,27 +1276,14 @@ fn permit_valid_with_metamask_signed_data() { .write(H256::from(r_real)) .write(H256::from(s_real)) .build(), - None, - &Context { - address: Account::Precompile.into(), - caller: Account::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - cost: 0u64, - logs: LogsBuilder::new(Account::Precompile.into(),) - .log3( - SELECTOR_LOG_APPROVAL, - Account::Alice, - Account::Bob, - EvmDataWriter::new().write(U256::from(1000)).build(), - ) - .build(), - })) - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_log(LogsBuilder::new(Account::Precompile.into()).log3( + SELECTOR_LOG_APPROVAL, + Account::Alice, + Account::Bob, + EvmDataWriter::new().write(U256::from(1000)).build(), + )) + .execute_returns(vec![]); }); } diff --git a/precompiles/crowdloan-rewards/Cargo.toml b/precompiles/crowdloan-rewards/Cargo.toml index 3415d89e29a..c2fddad3588 100644 --- a/precompiles/crowdloan-rewards/Cargo.toml +++ b/precompiles/crowdloan-rewards/Cargo.toml @@ -11,17 +11,19 @@ num_enum = { version = "0.5.3", default-features = false } rustc-hex = { version = "2.0.1", default-features = false } # Moonbeam +pallet-crowdloan-rewards = { git = "https://github.com/purestake/crowdloan-rewards", branch = "moonbeam-polkadot-v0.9.20", default-features = false } precompile-utils = { path = "../utils", default-features = false } # Substrate -fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } -pallet-crowdloan-rewards = { git = "https://github.com/purestake/crowdloan-rewards", branch = "moonbeam-polkadot-v0.9.20", default-features = false } -pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } +# Frontier +fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } +pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } + [dev-dependencies] derive_more = "0.99" serde = "1.0.100" diff --git a/precompiles/crowdloan-rewards/src/lib.rs b/precompiles/crowdloan-rewards/src/lib.rs index 2ff49b5e9e5..1295836363e 100644 --- a/precompiles/crowdloan-rewards/src/lib.rs +++ b/precompiles/crowdloan-rewards/src/lib.rs @@ -19,14 +19,15 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(assert_matches)] -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, traits::Currency, }; -use pallet_evm::{AddressMapping, Precompile}; +use pallet_evm::AddressMapping; use precompile_utils::{ - Address, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, RuntimeHelper, + revert, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, + RuntimeHelper, }; use sp_core::{H160, U256}; @@ -48,7 +49,7 @@ pub type BalanceOf = #[precompile_utils::generate_function_selector] #[derive(Debug, PartialEq)] -enum Action { +pub enum Action { IsContributor = "is_contributor(address)", RewardInfo = "reward_info(address)", Claim = "claim()", @@ -66,32 +67,20 @@ where ::Origin: From>, Runtime::Call: From>, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let (mut input, selector) = EvmDataReader::new_with_selector(&mut gasometer, input)?; - - gasometer.check_function_modifier( - context, - is_static, - match selector { - Action::Claim | Action::UpdateRewardAddress => FunctionModifier::NonPayable, - _ => FunctionModifier::View, - }, - )?; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; + + handle.check_function_modifier(match selector { + Action::Claim | Action::UpdateRewardAddress => FunctionModifier::NonPayable, + _ => FunctionModifier::View, + })?; match selector { // Check for accessor methods first. These return results immediately - Action::IsContributor => Self::is_contributor(&mut input, &mut gasometer), - Action::RewardInfo => Self::reward_info(&mut input, &mut gasometer), - Action::Claim => Self::claim(&mut gasometer, context), - Action::UpdateRewardAddress => { - Self::update_reward_address(&mut input, &mut gasometer, context) - } + Action::IsContributor => Self::is_contributor(handle), + Action::RewardInfo => Self::reward_info(handle), + Action::Claim => Self::claim(handle), + Action::UpdateRewardAddress => Self::update_reward_address(handle), } } } @@ -104,18 +93,16 @@ where ::Origin: From>, Runtime::Call: From>, { - // The accessors are first. They directly return their result. - fn is_contributor( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; // accounts_payable + // The accessors are first. + fn is_contributor(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // accounts_payable // Bound check - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; // parse the address - let contributor: H160 = input.read::
(gasometer)?.into(); + let contributor: H160 = input.read::
()?.into(); let account = Runtime::AddressMapping::into_account_id(contributor); @@ -131,25 +118,18 @@ where log::trace!(target: "crowldoan-rewards-precompile", "Result from pallet is {:?}", is_contributor); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(is_contributor).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(is_contributor).build())) } - fn reward_info( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; // accounts_payable + fn reward_info(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // accounts_payable // Bound check - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; // parse the address - let contributor: H160 = input.read::
(gasometer)?.into(); + let contributor: H160 = input.read::
()?.into(); let account = Runtime::AddressMapping::into_account_id(contributor); @@ -162,78 +142,63 @@ where // fetch data from pallet let reward_info = pallet_crowdloan_rewards::Pallet::::accounts_payable(account); - let (total, claimed): (U256, U256) = - if let Some(reward_info) = reward_info { - let total_reward: u128 = reward_info.total_reward.try_into().map_err(|_| { - gasometer.revert("Amount is too large for provided balance type") - })?; - let claimed_reward: u128 = reward_info.claimed_reward.try_into().map_err(|_| { - gasometer.revert("Amount is too large for provided balance type") - })?; - - (total_reward.into(), claimed_reward.into()) - } else { - (0u128.into(), 0u128.into()) - }; + let (total, claimed): (U256, U256) = if let Some(reward_info) = reward_info { + let total_reward: u128 = reward_info + .total_reward + .try_into() + .map_err(|_| revert("Amount is too large for provided balance type"))?; + let claimed_reward: u128 = reward_info + .claimed_reward + .try_into() + .map_err(|_| revert("Amount is too large for provided balance type"))?; + + (total_reward.into(), claimed_reward.into()) + } else { + (0u128.into(), 0u128.into()) + }; log::trace!( target: "crowldoan-rewards-precompile", "Result from pallet is {:?} {:?}", total, claimed ); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(total).write(claimed).build(), - logs: Default::default(), - }) + Ok(succeed( + EvmDataWriter::new().write(total).write(claimed).build(), + )) } - fn claim(gasometer: &mut Gasometer, context: &Context) -> EvmResult { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + fn claim(handle: &mut impl PrecompileHandle) -> EvmResult { + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = pallet_crowdloan_rewards::Call::::claim {}; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn update_reward_address( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn update_reward_address(handle: &mut impl PrecompileHandle) -> EvmResult { log::trace!( target: "crowdloan-rewards-precompile", "In update_reward_address dispatchable wrapper" ); // Bound check - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; // parse the address - let new_address: H160 = input.read::
(gasometer)?.into(); + let new_address: H160 = input.read::
()?.into(); let new_reward_account = Runtime::AddressMapping::into_account_id(new_address); log::trace!(target: "crowdloan-rewards-precompile", "New account is {:?}", new_address); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = pallet_crowdloan_rewards::Call::::update_reward_address { new_reward_account }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } } diff --git a/precompiles/crowdloan-rewards/src/mock.rs b/precompiles/crowdloan-rewards/src/mock.rs index c7ce69fa768..d492f402e0d 100644 --- a/precompiles/crowdloan-rewards/src/mock.rs +++ b/precompiles/crowdloan-rewards/src/mock.rs @@ -39,6 +39,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, Perbill, }; + pub type AccountId = H160; pub type Balance = u128; pub type BlockNumber = u64; @@ -80,7 +81,7 @@ construct_runtime!( derive_more::Display, scale_info::TypeInfo, )] -pub enum TestAccount { +pub enum Account { Alice, Bob, Charlie, @@ -99,20 +100,20 @@ impl> AddressMapping for IntoAddressMapping { } } -impl Default for TestAccount { +impl Default for Account { fn default() -> Self { Self::Bogus } } -impl From for H160 { - fn from(value: TestAccount) -> H160 { +impl From for H160 { + fn from(value: Account) -> H160 { match value { - TestAccount::Alice => H160::repeat_byte(0xAA), - TestAccount::Bob => H160::repeat_byte(0xBB), - TestAccount::Charlie => H160::repeat_byte(0xCC), - TestAccount::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), - TestAccount::Bogus => Default::default(), + Account::Alice => H160::repeat_byte(0xAA), + Account::Bob => H160::repeat_byte(0xBB), + Account::Charlie => H160::repeat_byte(0xCC), + Account::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), + Account::Bogus => Default::default(), } } } @@ -177,11 +178,6 @@ impl pallet_balances::Config for Runtime { type WeightInfo = (); } -/// The crowdloan precompile is available at address one in the mock runtime. -pub fn precompile_address() -> H160 { - H160::from_low_u64_be(1) -} - #[derive(Debug, Clone, Copy)] pub struct TestPrecompiles(PhantomData); @@ -189,24 +185,17 @@ impl PrecompileSet for TestPrecompiles where CrowdloanRewardsWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == precompile_address() => Some(CrowdloanRewardsWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == Account::Precompile.into() => { + Some(CrowdloanRewardsWrapper::::execute(handle)) + } _ => None, } } fn is_precompile(&self, address: H160) -> bool { - address == precompile_address() + address == Account::Precompile.into() } } @@ -367,14 +356,3 @@ pub(crate) fn events() -> Vec { .map(|r| r.event) .collect::>() } - -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} diff --git a/precompiles/crowdloan-rewards/src/tests.rs b/precompiles/crowdloan-rewards/src/tests.rs index c1a9d7dc846..ef4e3972bc6 100644 --- a/precompiles/crowdloan-rewards/src/tests.rs +++ b/precompiles/crowdloan-rewards/src/tests.rs @@ -14,20 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . -use std::assert_matches::assert_matches; - use crate::mock::{ - events, evm_test_context, precompile_address, roll_to, Call, Crowdloan, ExtBuilder, Origin, - PrecompilesValue, Runtime, TestAccount::Alice, TestAccount::Bob, TestAccount::Charlie, - TestPrecompiles, + events, roll_to, + Account::{Alice, Bob, Charlie, Precompile}, + Call, Crowdloan, ExtBuilder, Origin, PrecompilesValue, Runtime, TestPrecompiles, }; -use crate::{Action, PrecompileOutput}; -use fp_evm::PrecompileFailure; +use crate::Action; use frame_support::{assert_ok, dispatch::Dispatchable}; -use num_enum::TryFromPrimitive; use pallet_crowdloan_rewards::{Call as CrowdloanCall, Event as CrowdloanEvent}; -use pallet_evm::{Call as EvmCall, ExitSucceed, PrecompileSet}; -use precompile_utils::{Address, EvmDataWriter}; +use pallet_evm::Call as EvmCall; +use precompile_utils::{testing::*, Address, EvmDataWriter}; use sha3::{Digest, Keccak256}; use sp_core::{H160, U256}; @@ -38,7 +34,7 @@ fn precompiles() -> TestPrecompiles { fn evm_call(input: Vec) -> EvmCall { EvmCall::call { source: Alice.into(), - target: precompile_address(), + target: Precompile.into(), input, value: U256::zero(), // No value sent in EVM gas_limit: u64::max_value(), @@ -51,65 +47,28 @@ fn evm_call(input: Vec) -> EvmCall { #[test] fn test_selector_enum() { - let mut buffer = [0u8; 4]; - buffer.copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::IsContributor, - ); - buffer.copy_from_slice(&Keccak256::digest(b"claim()")[0..4]); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::Claim, - ); - buffer.copy_from_slice(&Keccak256::digest(b"reward_info(address)")[0..4]); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::RewardInfo, - ); - buffer.copy_from_slice(&Keccak256::digest(b"update_reward_address(address)")[0..4]); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::UpdateRewardAddress, - ); + assert_eq!(Action::IsContributor as u32, 0x53440c90); + assert_eq!(Action::RewardInfo as u32, 0x76f70249); + assert_eq!(Action::Claim as u32, 0x4e71d92d); + assert_eq!(Action::UpdateRewardAddress as u32, 0xaaac61d6); } #[test] fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"tried to parse selector out of bounds" - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"unknown selector", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -119,29 +78,17 @@ fn is_contributor_returns_false() { .with_balances(vec![(Alice.into(), 1000)]) .build() .execute_with(|| { - let input = EvmDataWriter::new_with_selector(Action::IsContributor) - .write(Address(H160::from(Alice))) - .build(); - - // Expected result is one - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that no props have been opened. - assert_eq!( - precompiles().execute( - precompile_address(), - &input, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsContributor) + .write(Address(H160::from(Alice))) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }); } @@ -170,26 +117,18 @@ fn is_contributor_returns_true() { init_block + VESTING )); - let input = EvmDataWriter::new_with_selector(Action::IsContributor) - .write(Address(H160::from(Alice))) - .build(); - // Assert that no props have been opened. - assert_eq!( - precompiles().execute( - precompile_address(), - &input, - None, - &evm_test_context(), - false - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsContributor) + .write(Address(H160::from(Alice))) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -258,29 +197,23 @@ fn reward_info_works() { roll_to(5); - let input = EvmDataWriter::new_with_selector(Action::RewardInfo) - .write(Address(H160::from(Alice))) - .build(); - // Assert that no props have been opened. - assert_eq!( - precompiles().execute( - precompile_address(), - &input, - None, - &evm_test_context(), - false - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::RewardInfo) + .write(Address(H160::from(Alice))) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() .write(U256::from(50u64)) .write(U256::from(10u64)) .build(), - cost: Default::default(), - logs: Default::default(), - })) - ); + ); }); } @@ -338,16 +271,8 @@ fn test_bound_checks_for_address_parsing() { let mut input = Keccak256::digest(b"update_reward_address(address)")[0..4].to_vec(); input.extend_from_slice(&[1u8; 4]); // incomplete data - assert_matches!( - precompiles().execute( - precompile_address(), - &input, - None, - &evm_test_context(), - false - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"input doesn't match expected length", - ); + precompiles() + .prepare_test(Alice, Precompile, input) + .execute_reverts(|output| output == b"input doesn't match expected length") }) } diff --git a/precompiles/pallet-democracy/src/lib.rs b/precompiles/pallet-democracy/src/lib.rs index 301e6d19400..09f2147b699 100644 --- a/precompiles/pallet-democracy/src/lib.rs +++ b/precompiles/pallet-democracy/src/lib.rs @@ -19,15 +19,14 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(assert_matches)] -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; use frame_support::traits::Currency; use pallet_democracy::{AccountVote, Call as DemocracyCall, Vote}; -use pallet_evm::AddressMapping; -use pallet_evm::Precompile; +use pallet_evm::{AddressMapping, Precompile}; use precompile_utils::{ - Address, Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, - RuntimeHelper, + revert, succeed, Address, Bytes, EvmData, EvmDataWriter, EvmResult, FunctionModifier, + PrecompileHandleExt, RuntimeHelper, }; use sp_core::{H160, H256, U256}; use sp_std::{ @@ -74,6 +73,7 @@ enum Action { /// For an example of a political party that operates as a DAO, see PoliticalPartyDao.sol pub struct DemocracyWrapper(PhantomData); +// TODO: Migrate to precompile_utils::Precompile. impl Precompile for DemocracyWrapper where Runtime: pallet_democracy::Config + pallet_evm::Config + frame_system::Config, @@ -83,55 +83,42 @@ where Runtime::Call: From>, Runtime::Hash: From, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { log::trace!(target: "democracy-precompile", "In democracy wrapper"); - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; - - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier( - context, - is_static, - match selector { - Action::Propose - | Action::Second - | Action::StandardVote - | Action::RemoveVote - | Action::Delegate - | Action::UnDelegate - | Action::Unlock - | Action::NotePreimage - | Action::NoteImminentPreimage => FunctionModifier::NonPayable, - _ => FunctionModifier::View, - }, - )?; + let selector = handle.read_selector()?; + + handle.check_function_modifier(match selector { + Action::Propose + | Action::Second + | Action::StandardVote + | Action::RemoveVote + | Action::Delegate + | Action::UnDelegate + | Action::Unlock + | Action::NotePreimage + | Action::NoteImminentPreimage => FunctionModifier::NonPayable, + _ => FunctionModifier::View, + })?; match selector { // Storage Accessors - Action::PublicPropCount => Self::public_prop_count(gasometer), - Action::DepositOf => Self::deposit_of(input, gasometer), - Action::LowestUnbaked => Self::lowest_unbaked(gasometer), - Action::OngoingReferendumInfo => Self::ongoing_referendum_info(input, gasometer), - Action::FinishedReferendumInfo => Self::finished_referendum_info(input, gasometer), + Action::PublicPropCount => Self::public_prop_count(handle), + Action::DepositOf => Self::deposit_of(handle), + Action::LowestUnbaked => Self::lowest_unbaked(handle), + Action::OngoingReferendumInfo => Self::ongoing_referendum_info(handle), + Action::FinishedReferendumInfo => Self::finished_referendum_info(handle), // Dispatchables - Action::Propose => Self::propose(input, gasometer, context), - Action::Second => Self::second(input, gasometer, context), - Action::StandardVote => Self::standard_vote(input, gasometer, context), - Action::RemoveVote => Self::remove_vote(input, gasometer, context), - Action::Delegate => Self::delegate(input, gasometer, context), - Action::UnDelegate => Self::un_delegate(gasometer, context), - Action::Unlock => Self::unlock(input, gasometer, context), - Action::NotePreimage => Self::note_preimage(input, gasometer, context), - Action::NoteImminentPreimage => Self::note_imminent_preimage(input, gasometer, context), + Action::Propose => Self::propose(handle), + Action::Second => Self::second(handle), + Action::StandardVote => Self::standard_vote(handle), + Action::RemoveVote => Self::remove_vote(handle), + Action::Delegate => Self::delegate(handle), + Action::UnDelegate => Self::un_delegate(handle), + Action::Unlock => Self::unlock(handle), + Action::NotePreimage => Self::note_preimage(handle), + Action::NoteImminentPreimage => Self::note_imminent_preimage(handle), } } } @@ -146,33 +133,25 @@ where Runtime::Hash: From, { // The accessors are first. They directly return their result. - - fn public_prop_count(gasometer: &mut Gasometer) -> EvmResult { + fn public_prop_count(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch data from pallet - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let prop_count = DemocracyOf::::public_prop_count(); log::trace!(target: "democracy-precompile", "Prop count from pallet is {:?}", prop_count); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(prop_count).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(prop_count).build())) } - fn deposit_of( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { + fn deposit_of(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 1)?; - let prop_index: u32 = input.read(gasometer)?; + input.expect_arguments(1)?; + let prop_index: u32 = input.read()?; // Fetch data from pallet - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let deposit = DemocracyOf::::deposit_of(prop_index) - .ok_or_else(|| gasometer.revert("No such proposal in pallet democracy"))? + .ok_or_else(|| revert("No such proposal in pallet democracy"))? .1; log::trace!( @@ -180,40 +159,28 @@ where "Deposit of proposal {:?} is {:?}", prop_index, deposit ); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(deposit).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(deposit).build())) } - fn lowest_unbaked(gasometer: &mut Gasometer) -> EvmResult { + fn lowest_unbaked(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch data from pallet - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let lowest_unbaked = DemocracyOf::::lowest_unbaked(); log::trace!( target: "democracy-precompile", "lowest unbaked referendum is {:?}", lowest_unbaked ); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(lowest_unbaked).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(lowest_unbaked).build())) } // This method is not yet implemented because it depends on // https://github.com/paritytech/substrate/pull/9565 which has been merged into Substrate // master, but is not on the release branches that we are following - fn ongoing_referendum_info( - _input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - Err(gasometer - .revert("This method depends on https://github.com/paritytech/substrate/pull/9565")) + fn ongoing_referendum_info(_handle: &mut impl PrecompileHandle) -> EvmResult { + Err(revert( + "This method depends on https://github.com/paritytech/substrate/pull/9565", + )) // let mut gasometer = Gasometer::new(target_gas); // // Bound check @@ -261,93 +228,74 @@ where // https://github.com/paritytech/substrate/pull/9565 which has been merged into Substrate // master, but is not on the release branches that we are following fn finished_referendum_info( - _input: &mut EvmDataReader, - gasometer: &mut Gasometer, + _handle: &mut impl PrecompileHandle, ) -> EvmResult { - Err(gasometer - .revert("This method depends on https://github.com/paritytech/substrate/pull/9565")) + Err(revert( + "This method depends on https://github.com/paritytech/substrate/pull/9565", + )) } // The dispatchable wrappers are next. They dispatch a Substrate inner Call. - fn propose( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn propose(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 2)?; + input.expect_arguments(2)?; - let proposal_hash = input.read::(gasometer)?.into(); - let amount = input.read::>(gasometer)?; + let proposal_hash = input.read::()?.into(); + let amount = input.read::>()?; log::trace!( target: "democracy-precompile", "Proposing with hash {:?}, and amount {:?}", proposal_hash, amount ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::propose { proposal_hash, value: amount, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn second( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn second(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 2)?; + input.expect_arguments(2)?; - let proposal = input.read(gasometer)?; - let seconds_upper_bound = input.read(gasometer)?; + let proposal = input.read()?; + let seconds_upper_bound = input.read()?; log::trace!( target: "democracy-precompile", "Seconding proposal {:?}, with bound {:?}", proposal, seconds_upper_bound ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::second { proposal, seconds_upper_bound, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn standard_vote( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn standard_vote(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 4)?; + input.expect_arguments(4)?; - let ref_index = input.read(gasometer)?; - let aye = input.read(gasometer)?; - let balance = input.read(gasometer)?; + let ref_index = input.read()?; + let aye = input.read()?; + let balance = input.read()?; let conviction = input - .read::(gasometer)? + .read::()? .try_into() - .map_err(|_| gasometer.revert("Conviction must be an integer in the range 0-6"))?; + .map_err(|_| revert("Conviction must be an integer in the range 0-6"))?; let vote = AccountVote::Standard { vote: Vote { aye, conviction }, balance, @@ -358,28 +306,20 @@ where aye, ref_index, conviction ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::vote { ref_index, vote }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn remove_vote( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn remove_vote(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 1)?; + input.expect_arguments(1)?; - let referendum_index = input.read(gasometer)?; + let referendum_index = input.read()?; log::trace!( target: "democracy-precompile", @@ -387,82 +327,61 @@ where referendum_index ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::remove_vote { index: referendum_index, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn delegate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn delegate(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 3)?; + input.expect_arguments(3)?; - let to: H160 = input.read::
(gasometer)?.into(); + let to: H160 = input.read::
()?.into(); let to = Runtime::AddressMapping::into_account_id(to); let conviction = input - .read::(gasometer)? + .read::()? .try_into() - .map_err(|_| gasometer.revert("Conviction must be an integer in the range 0-6"))?; - let balance = input.read(gasometer)?; + .map_err(|_| revert("Conviction must be an integer in the range 0-6"))?; + let balance = input.read()?; log::trace!(target: "democracy-precompile", "Delegating vote to {:?} with balance {:?} and {:?}", to, conviction, balance ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::delegate { to, conviction, balance, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn un_delegate(gasometer: &mut Gasometer, context: &Context) -> EvmResult { - let origin = Runtime::AddressMapping::into_account_id(context.caller); + fn un_delegate(handle: &mut impl PrecompileHandle) -> EvmResult { + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::undelegate {}; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn unlock( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn unlock(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 1)?; + input.expect_arguments(1)?; - let target: H160 = input.read::
(gasometer)?.into(); + let target: H160 = input.read::
()?.into(); let target = Runtime::AddressMapping::into_account_id(target); log::trace!( @@ -470,66 +389,45 @@ where "Unlocking democracy tokens for {:?}", target ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::unlock { target }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn note_preimage( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - let encoded_proposal: Vec = input.read::(gasometer)?.into(); + fn note_preimage(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + let encoded_proposal: Vec = input.read::()?.into(); log::trace!( target: "democracy-precompile", "Noting preimage {:?}", encoded_proposal ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::note_preimage { encoded_proposal }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn note_imminent_preimage( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - let encoded_proposal: Vec = input.read::(gasometer)?.into(); + fn note_imminent_preimage(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + let encoded_proposal: Vec = input.read::()?.into(); log::trace!( target: "democracy-precompile", "Noting imminent preimage {:?}", encoded_proposal ); - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = DemocracyCall::::note_imminent_preimage { encoded_proposal }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Stopped, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } } diff --git a/precompiles/pallet-democracy/src/mock.rs b/precompiles/pallet-democracy/src/mock.rs index a2ed70e3879..49c7dcc4529 100644 --- a/precompiles/pallet-democracy/src/mock.rs +++ b/precompiles/pallet-democracy/src/mock.rs @@ -35,7 +35,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, }; -pub type AccountId = TestAccount; +pub type AccountId = Account; pub type Balance = u128; pub type BlockNumber = u64; @@ -44,11 +44,6 @@ type Block = frame_system::mocking::MockBlock; pub const PRECOMPILE_ADDRESS: u64 = 1; -/// The democracy precompile is available at address one in the mock runtime. -pub fn precompile_address() -> H160 { - H160::from_low_u64_be(1) -} - #[derive( Eq, PartialEq, @@ -64,7 +59,7 @@ pub fn precompile_address() -> H160 { derive_more::Display, TypeInfo, )] -pub enum TestAccount { +pub enum Account { Alice, Bob, Charlie, @@ -72,14 +67,14 @@ pub enum TestAccount { Precompile, } -impl Default for TestAccount { +impl Default for Account { fn default() -> Self { Self::Bogus } } -impl AddressMapping for TestAccount { - fn into_account_id(h160_account: H160) -> TestAccount { +impl AddressMapping for Account { + fn into_account_id(h160_account: H160) -> Account { match h160_account { a if a == H160::repeat_byte(0xAA) => Self::Alice, a if a == H160::repeat_byte(0xBB) => Self::Bob, @@ -90,20 +85,20 @@ impl AddressMapping for TestAccount { } } -impl From for TestAccount { - fn from(x: H160) -> TestAccount { - TestAccount::into_account_id(x) +impl From for Account { + fn from(x: H160) -> Account { + Account::into_account_id(x) } } -impl From for H160 { - fn from(value: TestAccount) -> H160 { +impl From for H160 { + fn from(value: Account) -> H160 { match value { - TestAccount::Alice => H160::repeat_byte(0xAA), - TestAccount::Bob => H160::repeat_byte(0xBB), - TestAccount::Charlie => H160::repeat_byte(0xCC), - TestAccount::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), - TestAccount::Bogus => Default::default(), + Account::Alice => H160::repeat_byte(0xAA), + Account::Bob => H160::repeat_byte(0xBB), + Account::Charlie => H160::repeat_byte(0xCC), + Account::Precompile => H160::from_low_u64_be(PRECOMPILE_ADDRESS), + Account::Bogus => Default::default(), } } } @@ -137,7 +132,7 @@ impl frame_system::Config for Runtime { type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = TestAccount; + type AccountId = Account; type Lookup = IdentityLookup; type Header = Header; type Event = Event; @@ -176,9 +171,9 @@ parameter_types! { impl pallet_evm::Config for Runtime { type FeeCalculator = (); type GasWeightMapping = (); - type CallOrigin = EnsureAddressRoot; - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = TestAccount; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = Account; type Currency = Balances; type Event = Event; type Runner = pallet_evm::runner::stack::Runner; @@ -252,7 +247,7 @@ impl pallet_scheduler::Config for Runtime { type PalletsOrigin = OriginCaller; type Call = Call; type MaximumWeight = (); - type ScheduleOrigin = EnsureRoot; + type ScheduleOrigin = EnsureRoot; type MaxScheduledPerBlock = (); type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; // TODO : Simplest type, maybe there is better ? @@ -267,18 +262,9 @@ impl PrecompileSet for Precompiles where DemocracyWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == hash(PRECOMPILE_ADDRESS) => Some(DemocracyWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == hash(PRECOMPILE_ADDRESS) => Some(DemocracyWrapper::::execute(handle)), _ => None, } } @@ -381,51 +367,31 @@ pub(crate) fn events() -> Vec { .collect::>() } -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} - #[test] fn test_account_id_mapping_works() { // Bidirectional conversions for normal accounts assert_eq!( - TestAccount::Alice, - TestAccount::into_account_id(TestAccount::Alice.into()) + Account::Alice, + Account::into_account_id(Account::Alice.into()) ); + assert_eq!(Account::Bob, Account::into_account_id(Account::Bob.into())); assert_eq!( - TestAccount::Bob, - TestAccount::into_account_id(TestAccount::Bob.into()) - ); - assert_eq!( - TestAccount::Charlie, - TestAccount::into_account_id(TestAccount::Charlie.into()) + Account::Charlie, + Account::into_account_id(Account::Charlie.into()) ); // Bidirectional conversion between bogus and default H160 - assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::default()) - ); - assert_eq!(H160::default(), TestAccount::Bogus.into()); + assert_eq!(Account::Bogus, Account::into_account_id(H160::default())); + assert_eq!(H160::default(), Account::Bogus.into()); // All other H160s map to bogus + assert_eq!(Account::Bogus, Account::into_account_id(H160::zero())); assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::zero()) - ); - assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::repeat_byte(0x12)) + Account::Bogus, + Account::into_account_id(H160::repeat_byte(0x12)) ); assert_eq!( - TestAccount::Bogus, - TestAccount::into_account_id(H160::repeat_byte(0xFF)) + Account::Bogus, + Account::into_account_id(H160::repeat_byte(0xFF)) ); } diff --git a/precompiles/pallet-democracy/src/tests.rs b/precompiles/pallet-democracy/src/tests.rs index addab438074..26014a52012 100644 --- a/precompiles/pallet-democracy/src/tests.rs +++ b/precompiles/pallet-democracy/src/tests.rs @@ -16,23 +16,22 @@ use crate::{ mock::{ - events, evm_test_context, precompile_address, roll_to, Balances, Call, Democracy, - ExtBuilder, Origin, Precompiles, PrecompilesValue, Runtime, - TestAccount::{self, Alice, Bob, Precompile}, + events, roll_to, + Account::{self, Alice, Bob, Precompile}, + Balances, Call, Democracy, ExtBuilder, Origin, Precompiles, PrecompilesValue, Runtime, }, Action, }; -use fp_evm::{PrecompileFailure, PrecompileOutput}; use frame_support::{assert_ok, dispatch::Dispatchable, traits::Currency}; use pallet_balances::Event as BalancesEvent; use pallet_democracy::{ AccountVote, Call as DemocracyCall, Config as DemocracyConfig, Event as DemocracyEvent, PreimageStatus, Vote, VoteThreshold, Voting, }; -use pallet_evm::{Call as EvmCall, Event as EvmEvent, ExitSucceed, PrecompileSet}; -use precompile_utils::{Address, Bytes, EvmDataWriter}; +use pallet_evm::{Call as EvmCall, Event as EvmEvent}; +use precompile_utils::{testing::*, Address, Bytes, EvmDataWriter}; use sp_core::{H160, U256}; -use std::{assert_matches::assert_matches, convert::TryInto, str::from_utf8}; +use std::{convert::TryInto, str::from_utf8}; fn precompiles() -> Precompiles { PrecompilesValue::get() @@ -56,38 +55,18 @@ fn evm_call(input: Vec) -> EvmCall { fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"tried to parse selector out of bounds", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"unknown selector", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -112,22 +91,16 @@ fn selectors() { #[test] fn prop_count_zero() { ExtBuilder::default().build().execute_with(|| { - // Construct data to read prop count - let input = EvmDataWriter::new_with_selector(Action::PublicPropCount).build(); - - // Expected result is zero. because no props are open yet. - let expected_zero_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Vec::from([0u8; 32]), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that no props have been opened. - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_zero_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::PublicPropCount).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns([0u8; 32].into()) }); } @@ -144,22 +117,15 @@ fn prop_count_non_zero() { }) .dispatch(Origin::signed(Alice))); - // Construct data to read prop count - let input = EvmDataWriter::new_with_selector(Action::PublicPropCount).build(); - - // Expected result is one - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(1u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that no props have been opened. - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::PublicPropCount).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(1u32).build()); }); } @@ -178,60 +144,47 @@ fn deposit_of_non_zero() { }) .dispatch(Origin::signed(Alice))); - // Construct data to read prop count - let input = EvmDataWriter::new_with_selector(Action::DepositOf) - .write(0u32) - .build(); - - // Expected result is Alice's deposit of 1000 - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(1000u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_result - ) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DepositOf) + .write(0u32) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(1000u32).build()); }); } #[test] fn deposit_of_bad_index() { ExtBuilder::default().build().execute_with(|| { - // Construct data to read prop count - let input = EvmDataWriter::new_with_selector(Action::DepositOf) - .write(10u32) - .build(); - - assert_matches!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"No such proposal in pallet democracy", - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DepositOf) + .write(10u32) + .build(), + ) + .execute_reverts(|output| output == b"No such proposal in pallet democracy"); }); } #[test] fn lowest_unbaked_zero() { ExtBuilder::default().build().execute_with(|| { - // Construct data to read lowest unbaked referendum index - let input = EvmDataWriter::new_with_selector(Action::LowestUnbaked).build(); - - // Expected result is zero - let expected_zero_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(0u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_zero_result - ) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::LowestUnbaked).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(0u32).build()); }); } @@ -287,21 +240,15 @@ fn lowest_unbaked_non_zero() { + 1000, ); - // Construct data to read lowest unbaked referendum index - let input = EvmDataWriter::new_with_selector(Action::LowestUnbaked).build(); - - // Expected result is one - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(1u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_one_result - ) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::LowestUnbaked).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(1u32).build()); }); } @@ -675,19 +622,15 @@ fn remove_vote_dne() { // Wait until it becomes a referendum roll_to(::LaunchPeriod::get()); - // Construct input data to remove a non-existant vote - let input = EvmDataWriter::new_with_selector(Action::RemoveVote) - .write(0u32) // Referendum index 0 - .build(); - - // Expected result is an error from the pallet - if let Some(Err(PrecompileFailure::Revert { output: e, .. })) = - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false) - { - assert!(from_utf8(&e).unwrap().contains("NotVoter")); - } else { - panic!("Expected an ExitError, but didn't get one.") - } + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::RemoveVote) + .write(0u32) // Referendum index 0 + .build(), + ) + .execute_reverts(|output| from_utf8(&output).unwrap().contains("NotVoter")); }) } @@ -798,17 +741,13 @@ fn undelegate_works() { #[test] fn undelegate_dne() { ExtBuilder::default().build().execute_with(|| { - // Construct input data to un-delegate Alice - let input = EvmDataWriter::new_with_selector(Action::UnDelegate).build(); - - // Expected result is an error from the pallet - if let Some(Err(PrecompileFailure::Revert { output: e, .. })) = - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false) - { - assert!(from_utf8(&e).unwrap().contains("NotDelegating")); - } else { - panic!("Expected an ExitError, but didn't get one.") - } + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::UnDelegate).build(), + ) + .execute_reverts(|output| from_utf8(&output).unwrap().contains("NotDelegating")); }) } @@ -843,10 +782,7 @@ fn unlock_works() { // One possible way to look further: I just noticed there is a `Locks` storage item in // the pallet. // And also, maybe write a test in the pallet to ensure the locks work as expected. - assert_eq!( - >::free_balance(&Alice), - 900 - ); + assert_eq!(>::free_balance(&Alice), 900); // Let time elapse until she wins the vote and gets her tokens locked roll_to(11); @@ -1010,7 +946,7 @@ fn note_preimage_works_with_real_data() { // Make sure the call goes through successfully assert_ok!(Call::Evm(EvmCall::call { source: Alice.into(), - target: precompile_address(), + target: Precompile.into(), input, value: U256::zero(), // No value sent in EVM gas_limit: u64::max_value(), @@ -1036,7 +972,7 @@ fn note_preimage_works_with_real_data() { deposit: expected_deposit } .into(), - EvmEvent::Executed(precompile_address()).into(), + EvmEvent::Executed(Precompile.into()).into(), ] ); diff --git a/precompiles/parachain-staking/src/lib.rs b/precompiles/parachain-staking/src/lib.rs index 320932c81fb..7d14058e0ff 100644 --- a/precompiles/parachain-staking/src/lib.rs +++ b/precompiles/parachain-staking/src/lib.rs @@ -24,17 +24,16 @@ mod mock; #[cfg(test)] mod tests; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; use frame_support::traits::{Currency, Get}; use pallet_evm::AddressMapping; -use pallet_evm::Precompile; use precompile_utils::{ - Address, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, - RuntimeHelper, + revert, succeed, Address, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, + PrecompileHandleExt, RuntimeHelper, }; use sp_core::H160; -use sp_std::{convert::TryInto, fmt::Debug, marker::PhantomData, vec, vec::Vec}; +use sp_std::{convert::TryInto, fmt::Debug, marker::PhantomData, vec::Vec}; type BalanceOf = <::Currency as Currency< ::AccountId, @@ -108,7 +107,8 @@ enum Action { /// supporters who want to donate toward a perpetual nomination fund. pub struct ParachainStakingWrapper(PhantomData); -impl Precompile for ParachainStakingWrapper +// TODO: Migrate to precompile_utils::Precompile. +impl pallet_evm::Precompile for ParachainStakingWrapper where Runtime: parachain_staking::Config + pallet_evm::Config, BalanceOf: EvmData, @@ -117,178 +117,122 @@ where ::Origin: From>, Runtime::Call: From>, { - fn execute( - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; - - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier( - context, - is_static, - match selector { - // Views - Action::IsNominator - | Action::IsDelegator - | Action::IsCandidate - | Action::IsSelectedCandidate - | Action::MinNomination - | Action::MinDelegation - | Action::Points - | Action::CandidateCount - | Action::Round - | Action::CollatorNominationCount - | Action::NominatorNominationCount - | Action::CandidateDelegationCount - | Action::DelegatorDelegationCount - | Action::SelectedCandidates - | Action::DelegationRequestIsPending - | Action::CandidateExitIsPending - | Action::CandidateRequestIsPending => FunctionModifier::View, - // Non-payables - Action::JoinCandidates - | Action::LeaveCandidates - | Action::ScheduleLeaveCandidates - | Action::ExecuteLeaveCandidates - | Action::CancelLeaveCandidates - | Action::GoOffline - | Action::GoOnline - | Action::CandidateBondLess - | Action::ScheduleCandidateBondLess - | Action::CandidateBondMore - | Action::ExecuteCandidateBondLess - | Action::CancelCandidateBondLess - | Action::Nominate - | Action::Delegate - | Action::LeaveNominators - | Action::ScheduleLeaveDelegators - | Action::ExecuteLeaveDelegators - | Action::CancelLeaveDelegators - | Action::RevokeNomination - | Action::ScheduleRevokeDelegation - | Action::NominatorBondLess - | Action::ScheduleDelegatorBondLess - | Action::NominatorBondMore - | Action::DelegatorBondMore - | Action::ExecuteDelegationRequest - | Action::CancelDelegationRequest => FunctionModifier::NonPayable, - }, - )?; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; + + handle.check_function_modifier(match selector { + // Views + Action::IsNominator + | Action::IsDelegator + | Action::IsCandidate + | Action::IsSelectedCandidate + | Action::MinNomination + | Action::MinDelegation + | Action::Points + | Action::CandidateCount + | Action::Round + | Action::CollatorNominationCount + | Action::NominatorNominationCount + | Action::CandidateDelegationCount + | Action::DelegatorDelegationCount + | Action::SelectedCandidates + | Action::DelegationRequestIsPending + | Action::CandidateExitIsPending + | Action::CandidateRequestIsPending => FunctionModifier::View, + // Non-payables + Action::JoinCandidates + | Action::LeaveCandidates + | Action::ScheduleLeaveCandidates + | Action::ExecuteLeaveCandidates + | Action::CancelLeaveCandidates + | Action::GoOffline + | Action::GoOnline + | Action::CandidateBondLess + | Action::ScheduleCandidateBondLess + | Action::CandidateBondMore + | Action::ExecuteCandidateBondLess + | Action::CancelCandidateBondLess + | Action::Nominate + | Action::Delegate + | Action::LeaveNominators + | Action::ScheduleLeaveDelegators + | Action::ExecuteLeaveDelegators + | Action::CancelLeaveDelegators + | Action::RevokeNomination + | Action::ScheduleRevokeDelegation + | Action::NominatorBondLess + | Action::ScheduleDelegatorBondLess + | Action::NominatorBondMore + | Action::DelegatorBondMore + | Action::ExecuteDelegationRequest + | Action::CancelDelegationRequest => FunctionModifier::NonPayable, + })?; // Return early if storage getter; return (origin, call) if dispatchable let (origin, call) = match selector { // DEPRECATED - Action::MinNomination => return Self::min_delegation(gasometer), - Action::MinDelegation => return Self::min_delegation(gasometer), - Action::Points => return Self::points(input, gasometer), - Action::CandidateCount => return Self::candidate_count(gasometer), - Action::Round => return Self::round(gasometer), + Action::MinNomination => return Self::min_delegation(handle), + Action::MinDelegation => return Self::min_delegation(handle), + Action::Points => return Self::points(handle), + Action::CandidateCount => return Self::candidate_count(handle), + Action::Round => return Self::round(handle), // DEPRECATED - Action::CollatorNominationCount => { - return Self::candidate_delegation_count(input, gasometer) - } + Action::CollatorNominationCount => return Self::candidate_delegation_count(handle), // DEPRECATED - Action::NominatorNominationCount => { - return Self::delegator_delegation_count(input, gasometer) - } - Action::CandidateDelegationCount => { - return Self::candidate_delegation_count(input, gasometer) - } - Action::DelegatorDelegationCount => { - return Self::delegator_delegation_count(input, gasometer) - } - Action::SelectedCandidates => return Self::selected_candidates(gasometer), + Action::NominatorNominationCount => return Self::delegator_delegation_count(handle), + Action::CandidateDelegationCount => return Self::candidate_delegation_count(handle), + Action::DelegatorDelegationCount => return Self::delegator_delegation_count(handle), + Action::SelectedCandidates => return Self::selected_candidates(handle), // DEPRECATED - Action::IsNominator => return Self::is_delegator(input, gasometer), - Action::IsDelegator => return Self::is_delegator(input, gasometer), - Action::IsCandidate => return Self::is_candidate(input, gasometer), - Action::IsSelectedCandidate => return Self::is_selected_candidate(input, gasometer), + Action::IsNominator => return Self::is_delegator(handle), + Action::IsDelegator => return Self::is_delegator(handle), + Action::IsCandidate => return Self::is_candidate(handle), + Action::IsSelectedCandidate => return Self::is_selected_candidate(handle), Action::DelegationRequestIsPending => { - return Self::delegation_request_is_pending(input, gasometer) - } - Action::CandidateExitIsPending => { - return Self::candidate_exit_is_pending(input, gasometer) - } - Action::CandidateRequestIsPending => { - return Self::candidate_request_is_pending(input, gasometer) + return Self::delegation_request_is_pending(handle) } + Action::CandidateExitIsPending => return Self::candidate_exit_is_pending(handle), + Action::CandidateRequestIsPending => return Self::candidate_request_is_pending(handle), // runtime methods (dispatchables) - Action::JoinCandidates => Self::join_candidates(input, gasometer, context)?, + Action::JoinCandidates => Self::join_candidates(handle)?, // DEPRECATED - Action::LeaveCandidates => Self::schedule_leave_candidates(input, gasometer, context)?, - Action::ScheduleLeaveCandidates => { - Self::schedule_leave_candidates(input, gasometer, context)? - } - Action::ExecuteLeaveCandidates => { - Self::execute_leave_candidates(input, gasometer, context)? - } - Action::CancelLeaveCandidates => { - Self::cancel_leave_candidates(input, gasometer, context)? - } - Action::GoOffline => Self::go_offline(context)?, - Action::GoOnline => Self::go_online(context)?, + Action::LeaveCandidates => Self::schedule_leave_candidates(handle)?, + Action::ScheduleLeaveCandidates => Self::schedule_leave_candidates(handle)?, + Action::ExecuteLeaveCandidates => Self::execute_leave_candidates(handle)?, + Action::CancelLeaveCandidates => Self::cancel_leave_candidates(handle)?, + Action::GoOffline => Self::go_offline(handle)?, + Action::GoOnline => Self::go_online(handle)?, // DEPRECATED - Action::CandidateBondLess => { - Self::schedule_candidate_bond_less(input, gasometer, context)? - } - Action::ScheduleCandidateBondLess => { - Self::schedule_candidate_bond_less(input, gasometer, context)? - } - Action::CandidateBondMore => Self::candidate_bond_more(input, gasometer, context)?, - Action::ExecuteCandidateBondLess => { - Self::execute_candidate_bond_less(input, gasometer, context)? - } - Action::CancelCandidateBondLess => Self::cancel_candidate_bond_less(context)?, + Action::CandidateBondLess => Self::schedule_candidate_bond_less(handle)?, + Action::ScheduleCandidateBondLess => Self::schedule_candidate_bond_less(handle)?, + Action::CandidateBondMore => Self::candidate_bond_more(handle)?, + Action::ExecuteCandidateBondLess => Self::execute_candidate_bond_less(handle)?, + Action::CancelCandidateBondLess => Self::cancel_candidate_bond_less(handle)?, // DEPRECATED - Action::Nominate => Self::delegate(input, gasometer, context)?, - Action::Delegate => Self::delegate(input, gasometer, context)?, + Action::Nominate => Self::delegate(handle)?, + Action::Delegate => Self::delegate(handle)?, // DEPRECATED - Action::LeaveNominators => Self::schedule_leave_delegators(context)?, - Action::ScheduleLeaveDelegators => Self::schedule_leave_delegators(context)?, - Action::ExecuteLeaveDelegators => { - Self::execute_leave_delegators(input, gasometer, context)? - } - Action::CancelLeaveDelegators => Self::cancel_leave_delegators(context)?, + Action::LeaveNominators => Self::schedule_leave_delegators(handle)?, + Action::ScheduleLeaveDelegators => Self::schedule_leave_delegators(handle)?, + Action::ExecuteLeaveDelegators => Self::execute_leave_delegators(handle)?, + Action::CancelLeaveDelegators => Self::cancel_leave_delegators(handle)?, // DEPRECATED - Action::RevokeNomination => { - Self::schedule_revoke_delegation(input, gasometer, context)? - } - Action::ScheduleRevokeDelegation => { - Self::schedule_revoke_delegation(input, gasometer, context)? - } + Action::RevokeNomination => Self::schedule_revoke_delegation(handle)?, + Action::ScheduleRevokeDelegation => Self::schedule_revoke_delegation(handle)?, // DEPRECATED - Action::NominatorBondLess => { - Self::schedule_delegator_bond_less(input, gasometer, context)? - } - Action::ScheduleDelegatorBondLess => { - Self::schedule_delegator_bond_less(input, gasometer, context)? - } + Action::NominatorBondLess => Self::schedule_delegator_bond_less(handle)?, + Action::ScheduleDelegatorBondLess => Self::schedule_delegator_bond_less(handle)?, // DEPRECATED - Action::NominatorBondMore => Self::delegator_bond_more(input, gasometer, context)?, - Action::DelegatorBondMore => Self::delegator_bond_more(input, gasometer, context)?, - Action::ExecuteDelegationRequest => { - Self::execute_delegation_request(input, gasometer, context)? - } - Action::CancelDelegationRequest => { - Self::cancel_delegation_request(input, gasometer, context)? - } + Action::NominatorBondMore => Self::delegator_bond_more(handle)?, + Action::DelegatorBondMore => Self::delegator_bond_more(handle)?, + Action::ExecuteDelegationRequest => Self::execute_delegation_request(handle)?, + Action::CancelDelegationRequest => Self::cancel_delegation_request(handle)?, }; // Dispatch call (if enough gas). - RuntimeHelper::::try_dispatch(origin, call, gasometer)?; - - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: vec![], - logs: vec![], - }) + RuntimeHelper::::try_dispatch(handle, origin, call)?; + + Ok(succeed([])) } } @@ -303,85 +247,66 @@ where { // Constants - fn min_delegation(gasometer: &mut Gasometer) -> EvmResult { + fn min_delegation(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let min_nomination: u128 = <::MinDelegation as Get< BalanceOf, >>::get() .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(min_nomination).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(min_nomination).build())) } // Storage Getters - fn points(input: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { + fn points(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let round = input.read::(gasometer)?; + input.expect_arguments(1)?; + let round = input.read::()?; // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let points: u32 = parachain_staking::Pallet::::points(round); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(points).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(points).build())) } - fn candidate_count(gasometer: &mut Gasometer) -> EvmResult { + fn candidate_count(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let candidate_count: u32 = >::candidate_pool() .0 .len() as u32; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(candidate_count).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(candidate_count).build())) } - fn round(gasometer: &mut Gasometer) -> EvmResult { + fn round(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let round: u32 = >::round().current; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(round).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(round).build())) } fn candidate_delegation_count( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let address = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let address = input.read::
()?.0; let address = Runtime::AddressMapping::into_account_id(address); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let result = if let Some(state) = >::candidate_info(&address) { let candidate_delegation_count: u32 = state.delegation_count; @@ -402,25 +327,20 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(result).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(result).build())) } fn delegator_delegation_count( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let address = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let address = input.read::
()?.0; let address = Runtime::AddressMapping::into_account_id(address); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let result = if let Some(state) = >::delegator_state(&address) { let delegator_delegation_count: u32 = state.delegations.0.len() as u32; @@ -442,116 +362,86 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(result).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(result).build())) } - fn selected_candidates(gasometer: &mut Gasometer) -> EvmResult { + fn selected_candidates(handle: &mut impl PrecompileHandle) -> EvmResult { // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let selected_candidates: Vec
= parachain_staking::Pallet::::selected_candidates() .into_iter() .map(|address| Address(address.into())) .collect(); + // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(selected_candidates).build(), - logs: vec![], - }) + Ok(succeed( + EvmDataWriter::new().write(selected_candidates).build(), + )) } // Role Verifiers - fn is_delegator( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { + fn is_delegator(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let address = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let address = input.read::
()?.0; let address = Runtime::AddressMapping::into_account_id(address); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let is_delegator = parachain_staking::Pallet::::is_delegator(&address); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(is_delegator).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(is_delegator).build())) } - fn is_candidate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { + fn is_candidate(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let address = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let address = input.read::
()?.0; let address = Runtime::AddressMapping::into_account_id(address); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let is_candidate = parachain_staking::Pallet::::is_candidate(&address); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(is_candidate).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(is_candidate).build())) } - fn is_selected_candidate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { + fn is_selected_candidate(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let address = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let address = input.read::
()?.0; let address = Runtime::AddressMapping::into_account_id(address); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let is_selected = parachain_staking::Pallet::::is_selected_candidate(&address); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(is_selected).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(is_selected).build())) } fn delegation_request_is_pending( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; + input.expect_arguments(2)?; // First argument is delegator - let delegator = - Runtime::AddressMapping::into_account_id(input.read::
(gasometer)?.0); + let delegator = Runtime::AddressMapping::into_account_id(input.read::
()?.0); // Second argument is candidate - let candidate = - Runtime::AddressMapping::into_account_id(input.read::
(gasometer)?.0); + let candidate = Runtime::AddressMapping::into_account_id(input.read::
()?.0); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // If we are not able to get delegator state, we return false // Users can call `is_delegator` to determine when this happens @@ -559,27 +449,21 @@ where >::delegation_request_exists(&candidate, &delegator); // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(pending).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(pending).build())) } fn candidate_exit_is_pending( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; + input.expect_arguments(1)?; // Only argument is candidate - let candidate = - Runtime::AddressMapping::into_account_id(input.read::
(gasometer)?.0); + let candidate = Runtime::AddressMapping::into_account_id(input.read::
()?.0); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // If we are not able to get delegator state, we return false // Users can call `is_candidate` to determine when this happens @@ -596,27 +480,21 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(pending).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(pending).build())) } fn candidate_request_is_pending( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; + input.expect_arguments(1)?; // Only argument is candidate - let candidate = - Runtime::AddressMapping::into_account_id(input.read::
(gasometer)?.0); + let candidate = Runtime::AddressMapping::into_account_id(input.read::
()?.0); // Fetch info. - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // If we are not able to get candidate metadata, we return false // Users can call `is_candidate` to determine when this happens @@ -633,31 +511,25 @@ where }; // Build output. - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(pending).build(), - logs: vec![], - }) + Ok(succeed(EvmDataWriter::new().write(pending).build())) } // Runtime Methods (dispatchables) fn join_candidates( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; - let bond: BalanceOf = input.read(gasometer)?; - let candidate_count = input.read(gasometer)?; + input.expect_arguments(2)?; + let bond: BalanceOf = input.read()?; + let candidate_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::join_candidates { bond, candidate_count, @@ -668,19 +540,18 @@ where } fn schedule_leave_candidates( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let candidate_count = input.read(gasometer)?; + input.expect_arguments(1)?; + let candidate_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::schedule_leave_candidates { candidate_count }; @@ -689,21 +560,20 @@ where } fn execute_leave_candidates( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let candidate = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); - let candidate_delegation_count = input.read(gasometer)?; + let candidate_delegation_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::execute_leave_candidates { candidate, candidate_delegation_count, @@ -714,19 +584,18 @@ where } fn cancel_leave_candidates( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let candidate_count = input.read(gasometer)?; + input.expect_arguments(1)?; + let candidate_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::cancel_leave_candidates { candidate_count }; // Return call information @@ -734,13 +603,13 @@ where } fn go_offline( - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::go_offline {}; // Return call information @@ -748,13 +617,13 @@ where } fn go_online( - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::go_online {}; // Return call information @@ -762,19 +631,18 @@ where } fn candidate_bond_more( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let more: BalanceOf = input.read(gasometer)?; + input.expect_arguments(1)?; + let more: BalanceOf = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::candidate_bond_more { more }; // Return call information @@ -782,19 +650,18 @@ where } fn schedule_candidate_bond_less( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let less: BalanceOf = input.read(gasometer)?; + input.expect_arguments(1)?; + let less: BalanceOf = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::schedule_candidate_bond_less { less }; // Return call information @@ -802,20 +669,19 @@ where } fn execute_candidate_bond_less( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let candidate = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::execute_candidate_bond_less { candidate }; // Return call information @@ -823,13 +689,13 @@ where } fn cancel_candidate_bond_less( - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::cancel_candidate_bond_less {}; // Return call information @@ -837,23 +703,21 @@ where } fn delegate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 4)?; - let candidate = - Runtime::AddressMapping::into_account_id(input.read::
(gasometer)?.0); - let amount: BalanceOf = input.read(gasometer)?; - let candidate_delegation_count = input.read(gasometer)?; - let delegation_count = input.read(gasometer)?; + input.expect_arguments(4)?; + let candidate = Runtime::AddressMapping::into_account_id(input.read::
()?.0); + let amount: BalanceOf = input.read()?; + let candidate_delegation_count = input.read()?; + let delegation_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::delegate { candidate, amount, @@ -866,13 +730,13 @@ where } fn schedule_leave_delegators( - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::schedule_leave_delegators {}; // Return call information @@ -880,21 +744,20 @@ where } fn execute_leave_delegators( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; - let delegator = input.read::
(gasometer)?.0; + input.expect_arguments(2)?; + let delegator = input.read::
()?.0; let delegator = Runtime::AddressMapping::into_account_id(delegator); - let delegation_count = input.read(gasometer)?; + let delegation_count = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::execute_leave_delegators { delegator, delegation_count, @@ -905,13 +768,13 @@ where } fn cancel_leave_delegators( - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::cancel_leave_delegators {}; // Return call information @@ -919,20 +782,19 @@ where } fn schedule_revoke_delegation( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let collator = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let collator = input.read::
()?.0; let collator = Runtime::AddressMapping::into_account_id(collator); // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::schedule_revoke_delegation { collator }; // Return call information @@ -940,21 +802,20 @@ where } fn delegator_bond_more( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; - let candidate = input.read::
(gasometer)?.0; + input.expect_arguments(2)?; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); - let more: BalanceOf = input.read(gasometer)?; + let more: BalanceOf = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::delegator_bond_more { candidate, more }; // Return call information @@ -962,21 +823,20 @@ where } fn schedule_delegator_bond_less( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; - let candidate = input.read::
(gasometer)?.0; + input.expect_arguments(2)?; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); - let less: BalanceOf = input.read(gasometer)?; + let less: BalanceOf = input.read()?; // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::schedule_delegator_bond_less { candidate, less }; @@ -985,22 +845,21 @@ where } fn execute_delegation_request( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 2)?; - let delegator = input.read::
(gasometer)?.0; + input.expect_arguments(2)?; + let delegator = input.read::
()?.0; let delegator = Runtime::AddressMapping::into_account_id(delegator); - let candidate = input.read::
(gasometer)?.0; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::execute_delegation_request { delegator, candidate, @@ -1011,20 +870,19 @@ where } fn cancel_delegation_request( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult<( ::Origin, parachain_staking::Call, )> { + let mut input = EvmDataReader::new_skip_selector(handle.input())?; // Read input. - input.expect_arguments(gasometer, 1)?; - let candidate = input.read::
(gasometer)?.0; + input.expect_arguments(1)?; + let candidate = input.read::
()?.0; let candidate = Runtime::AddressMapping::into_account_id(candidate); // Build call with origin. - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = parachain_staking::Call::::cancel_delegation_request { candidate }; // Return call information diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index a4ac28cc906..a5046aa999e 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -22,7 +22,9 @@ use frame_support::{ traits::{Everything, GenesisBuild, OnFinalize, OnInitialize}, weights::Weight, }; -use pallet_evm::{AddressMapping, EnsureAddressNever, EnsureAddressRoot, PrecompileSet}; +use pallet_evm::{ + AddressMapping, EnsureAddressNever, EnsureAddressRoot, Precompile, PrecompileSet, +}; use parachain_staking::{AwardedPts, InflationInfo, Points, Range}; use serde::{Deserialize, Serialize}; use sp_core::{H160, H256}; @@ -33,7 +35,7 @@ use sp_runtime::{ Perbill, Percent, }; -pub type AccountId = TestAccount; +pub type AccountId = Account; pub type Balance = u128; pub type BlockNumber = u64; @@ -70,55 +72,47 @@ construct_runtime!( derive_more::Display, scale_info::TypeInfo, )] -pub enum TestAccount { +pub enum Account { Alice, Bob, Charlie, Bogus, + Precompile, } -impl Default for TestAccount { +impl Default for Account { fn default() -> Self { Self::Bogus } } -impl Into for TestAccount { +impl Into for Account { fn into(self) -> H160 { match self { - TestAccount::Alice => H160::repeat_byte(0xAA), - TestAccount::Bob => H160::repeat_byte(0xBB), - TestAccount::Charlie => H160::repeat_byte(0xCC), - TestAccount::Bogus => H160::repeat_byte(0xDD), + Account::Alice => H160::repeat_byte(0xAA), + Account::Bob => H160::repeat_byte(0xBB), + Account::Charlie => H160::repeat_byte(0xCC), + Account::Bogus => H160::repeat_byte(0xDD), + Account::Precompile => H160::from_low_u64_be(1), } } } -impl AddressMapping for TestAccount { - fn into_account_id(h160_account: H160) -> TestAccount { +impl AddressMapping for Account { + fn into_account_id(h160_account: H160) -> Account { match h160_account { a if a == H160::repeat_byte(0xAA) => Self::Alice, a if a == H160::repeat_byte(0xBB) => Self::Bob, a if a == H160::repeat_byte(0xCC) => Self::Charlie, + a if a == H160::from_low_u64_be(1) => Self::Precompile, _ => Self::Bogus, } } } -impl TestAccount { - pub(crate) fn to_h160(&self) -> H160 { - match self { - Self::Alice => H160::repeat_byte(0xAA), - Self::Bob => H160::repeat_byte(0xBB), - Self::Charlie => H160::repeat_byte(0xCC), - Self::Bogus => Default::default(), - } - } -} - -impl From for TestAccount { - fn from(x: H160) -> TestAccount { - TestAccount::into_account_id(x) +impl From for Account { + fn from(x: H160) -> Account { + Account::into_account_id(x) } } @@ -182,18 +176,9 @@ impl PrecompileSet for TestPrecompiles where ParachainStakingWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == precompile_address() => Some(ParachainStakingWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == precompile_address() => Some(ParachainStakingWrapper::::execute(handle)), _ => None, } } @@ -372,7 +357,7 @@ impl ExtBuilder { } // Sets the same storage changes as EventHandler::note_author impl -pub(crate) fn set_points(round: u32, acc: TestAccount, pts: u32) { +pub(crate) fn set_points(round: u32, acc: Account, pts: u32) { >::mutate(round, |p| *p += pts); >::mutate(round, acc, |p| *p += pts); } @@ -402,14 +387,3 @@ pub(crate) fn events() -> Vec { .map(|r| r.event) .collect::>() } - -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} diff --git a/precompiles/parachain-staking/src/tests.rs b/precompiles/parachain-staking/src/tests.rs index ee9dc0db725..99ee89676df 100644 --- a/precompiles/parachain-staking/src/tests.rs +++ b/precompiles/parachain-staking/src/tests.rs @@ -15,27 +15,25 @@ // along with Moonbeam. If not, see . use crate::mock::{ - events, evm_test_context, precompile_address, roll_to, roll_to_round_begin, set_points, Call, - ExtBuilder, Origin, ParachainStaking, PrecompilesValue, Runtime, TestAccount, TestPrecompiles, + events, roll_to, roll_to_round_begin, set_points, + Account::{self, Alice, Bob, Bogus, Charlie, Precompile}, + Call, ExtBuilder, Origin, ParachainStaking, PrecompilesValue, Runtime, TestPrecompiles, }; -use crate::{Action, PrecompileOutput}; -use fp_evm::{Context, PrecompileFailure}; +use crate::Action; use frame_support::{assert_ok, dispatch::Dispatchable}; -use pallet_evm::{Call as EvmCall, ExitSucceed, PrecompileSet}; +use pallet_evm::Call as EvmCall; use parachain_staking::Event as StakingEvent; -use precompile_utils::{Address, EvmDataWriter}; -use sha3::{Digest, Keccak256}; +use precompile_utils::{testing::*, Address, EvmDataWriter}; use sp_core::U256; -use std::assert_matches::assert_matches; fn precompiles() -> TestPrecompiles { PrecompilesValue::get() } -fn evm_call(source: TestAccount, input: Vec) -> EvmCall { +fn evm_call(source: Account, input: Vec) -> EvmCall { EvmCall::call { - source: source.to_h160(), - target: precompile_address(), + source: source.into(), + target: Precompile.into(), input, value: U256::zero(), // No value sent in EVM gas_limit: u64::max_value(), @@ -110,39 +108,18 @@ fn selectors() { #[test] fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { - // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if &output == b"tried to parse selector out of bounds" - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if &output == b"unknown selector" - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -150,181 +127,109 @@ fn no_selector_exists_but_length_is_right() { #[test] fn min_nomination_works() { ExtBuilder::default().build().execute_with(|| { - let selector = &Keccak256::digest(b"min_nomination()")[0..4]; - - // Construct data to read minimum nomination constant - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); - - // Expected result is 3 - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(3u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::MinNomination).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(3u32).build()) }); } #[test] fn min_delegation_works() { ExtBuilder::default().build().execute_with(|| { - let selector = &Keccak256::digest(b"min_delegation()")[0..4]; - - // Construct data to read minimum nomination constant - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); - - // Expected result is 3 - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(3u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::MinDelegation).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(3u32).build()) }); } #[test] fn points_zero() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"points(uint256)")[0..4]; - - // Construct data to read points for round 1 - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - U256::one().to_big_endian(&mut input_data[4..36]); - - // Expected result is 0 points - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(0u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that there are total 0 points in round 1 - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + // Assert that there are total 0 points in round 1 + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Points) + .write(U256::one()) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(0u32).build()); }); } #[test] fn points_non_zero() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"points(uint256)")[0..4]; - - // Construct data to read points for round 1 - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - U256::one().to_big_endian(&mut input_data[4..36]); - - // Expected result is 100 points - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(100u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - - set_points(1u32, TestAccount::Alice, 100); + set_points(1u32, Alice, 100); // Assert that there are total 100 points in round 1 - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Points) + .write(U256::one()) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(100u32).build()); }); } #[test] fn round_works() { ExtBuilder::default().build().execute_with(|| { - // Expected starts at round 1 - let mut expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(1u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that round is 1 - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::Round).build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Round).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(1u32).build()); + // test next `ROUNDS_TO_TEST` rounds const ROUNDS_TO_TEST: u64 = 10; let mut current_round: u64 = 1; while current_round < ROUNDS_TO_TEST { current_round += 1; roll_to_round_begin(current_round); - expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(current_round).build(), - cost: Default::default(), - logs: Default::default(), - })); + // Assert that round is equal to expectation - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::Round).build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Round).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(current_round).build()); } }); } @@ -333,92 +238,54 @@ fn round_works() { #[test] fn collator_nomination_count_works() { ExtBuilder::default() - .with_balances(vec![ - (TestAccount::Alice, 1_000), - (TestAccount::Bob, 50), - (TestAccount::Charlie, 50), - (TestAccount::Bogus, 50), - ]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 50), (Charlie, 50), (Bogus, 50)]) + .with_candidates(vec![(Alice, 1_000)]) .with_delegations(vec![ - (TestAccount::Bob, TestAccount::Alice, 50), - (TestAccount::Charlie, TestAccount::Alice, 50), - (TestAccount::Bogus, TestAccount::Alice, 50), + (Bob, Alice, 50), + (Charlie, Alice, 50), + (Bogus, Alice, 50), ]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"collator_nomination_count(address)")[0..4]; - - // Construct data to read collator nomination count - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result 3 - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(3u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that there 3 nominations for Alice - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CollatorNominationCount) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(3u32).build()); }); } #[test] fn candidate_delegation_count_works() { ExtBuilder::default() - .with_balances(vec![ - (TestAccount::Alice, 1_000), - (TestAccount::Bob, 50), - (TestAccount::Charlie, 50), - (TestAccount::Bogus, 50), - ]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 50), (Charlie, 50), (Bogus, 50)]) + .with_candidates(vec![(Alice, 1_000)]) .with_delegations(vec![ - (TestAccount::Bob, TestAccount::Alice, 50), - (TestAccount::Charlie, TestAccount::Alice, 50), - (TestAccount::Bogus, TestAccount::Alice, 50), + (Bob, Alice, 50), + (Charlie, Alice, 50), + (Bogus, Alice, 50), ]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"candidate_delegation_count(address)")[0..4]; - - // Construct data to read candidate delegation count - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result 3 - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(3u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that there 3 delegations to Alice - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateDelegationCount) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(3u32).build()); }); } @@ -426,88 +293,46 @@ fn candidate_delegation_count_works() { #[test] fn nominator_nomination_count_works() { ExtBuilder::default() - .with_balances(vec![ - (TestAccount::Alice, 1_000), - (TestAccount::Bob, 1_000), - (TestAccount::Charlie, 200), - ]) - .with_candidates(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_delegations(vec![ - (TestAccount::Charlie, TestAccount::Alice, 100), - (TestAccount::Charlie, TestAccount::Bob, 100), - ]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000), (Charlie, 200)]) + .with_candidates(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_delegations(vec![(Charlie, Alice, 100), (Charlie, Bob, 100)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"nominator_nomination_count(address)")[0..4]; - - // Construct data to read delegator delegation count - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Charlie.to_h160().0); - - // Expected result is 2 - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(2u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Charlie has 2 outstanding delegations - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::NominatorNominationCount) + .write(Address(Charlie.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(2u32).build()); }); } #[test] fn delegator_delegation_count_works() { ExtBuilder::default() - .with_balances(vec![ - (TestAccount::Alice, 1_000), - (TestAccount::Bob, 1_000), - (TestAccount::Charlie, 200), - ]) - .with_candidates(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_delegations(vec![ - (TestAccount::Charlie, TestAccount::Alice, 100), - (TestAccount::Charlie, TestAccount::Bob, 100), - ]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000), (Charlie, 200)]) + .with_candidates(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_delegations(vec![(Charlie, Alice, 100), (Charlie, Bob, 100)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"delegator_delegation_count(address)")[0..4]; - - // Construct data to read delegator delegation count - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Charlie.to_h160().0); - - // Expected result is 2 - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(2u32).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Charlie has 2 outstanding nominations - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DelegatorDelegationCount) + .write(Address(Charlie.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(2u32).build()); }); } @@ -515,386 +340,231 @@ fn delegator_delegation_count_works() { #[test] fn is_nominator_true_false() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 50)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 50)]) + .with_balances(vec![(Alice, 1_000), (Bob, 50)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 50)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"is_nominator(address)")[0..4]; - - // Construct data to read is_nominator for Charlie - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4].copy_from_slice(&selector); - charlie_input_data[16..36].copy_from_slice(&TestAccount::Charlie.to_h160().0); - - // Expected result is false - let expected_false_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Charlie is not a delegator - assert_eq!( - precompiles().execute( - precompile_address(), - &charlie_input_data, - None, - &evm_test_context(), - false, - ), - expected_false_result - ); - - // Construct data to read is_nominator for Bob - let mut bob_input_data = Vec::::from([0u8; 36]); - bob_input_data[0..4].copy_from_slice(&selector); - bob_input_data[16..36].copy_from_slice(&TestAccount::Bob.to_h160().0); - - // Expected result is true - let expected_true_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsNominator) + .write(Address(Charlie.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); // Assert that Bob is a delegator - assert_eq!( - precompiles().execute( - precompile_address(), - &bob_input_data, - None, - &evm_test_context(), - false - ), - expected_true_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsNominator) + .write(Address(Bob.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } #[test] fn is_delegator_false() { ExtBuilder::default().build().execute_with(|| { - let selector = &Keccak256::digest(b"is_delegator(address)")[0..4]; - - // Construct data to read is_delegator - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Charlie.to_h160().0); - - // Expected result is false - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Charlie is not a delegator - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsDelegator) + .write(Address(Charlie.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }); } #[test] fn is_delegator_true() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 50)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 50)]) + .with_balances(vec![(Alice, 1_000), (Bob, 50)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 50)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"is_delegator(address)")[0..4]; - - // Construct data to read is_delegator - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Bob.to_h160().0); - - // Expected result is true - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Bob is a delegator - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsDelegator) + .write(Address(Bob.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } #[test] fn is_candidate_false() { ExtBuilder::default().build().execute_with(|| { - let selector = &Keccak256::digest(b"is_candidate(address)")[0..4]; - - // Construct data to read is_candidate - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result is false - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Alice is not a candidate - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsCandidate) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }); } #[test] fn is_candidate_true() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"is_candidate(address)")[0..4]; - - // Construct data to read is_candidate - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result is true - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Alice is a candidate - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsCandidate) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } #[test] fn is_selected_candidate_false() { ExtBuilder::default().build().execute_with(|| { - let selector = &Keccak256::digest(b"is_selected_candidate(address)")[0..4]; - - // Construct data to read is_selected_candidate - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result is false - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that Alice is not a selected candidate - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsSelectedCandidate) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }); } #[test] fn is_selected_candidate_true() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"is_selected_candidate(address)")[0..4]; - - // Construct data to read is_selected_candidate - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - - // Expected result is true - let expected_one_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that Alice is a selected candidate - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_one_result - ); + // Assert that Alice is not a selected candidate + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::IsSelectedCandidate) + .write(Address(Alice.into())) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } #[test] fn selected_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let input_data = EvmDataWriter::new_with_selector(Action::SelectedCandidates).build(); - // Alice is only selected candidates - let expected_selected_candidate = vec![TestAccount::Alice]; - let evm_expected_selected_candidate: Vec
= expected_selected_candidate - .into_iter() - .map(|x| Address(x.into())) - .collect(); - // Expected result is bogus selected repeated candidates - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() - .write(evm_expected_selected_candidate) - .build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that Alice is a selected candidate - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::SelectedCandidates).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(vec![Address(Alice.into())]) + .build(), + ); }); } #[test] fn delegation_request_is_pending_works() { ExtBuilder::default() - .with_balances(vec![ - (TestAccount::Alice, 1_000), - (TestAccount::Charlie, 50), - (TestAccount::Bogus, 50), - ]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Charlie, TestAccount::Alice, 50)]) + .with_balances(vec![(Alice, 1_000), (Charlie, 50), (Bogus, 50)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Charlie, Alice, 50)]) .build() .execute_with(|| { - // Expected false because we dont have pending requests yet - let mut expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); // Assert that we dont have pending requests - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) - .write(Address(TestAccount::Charlie.into())) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) + .write(Address(Charlie.into())) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); // Schedule Revoke request - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::ScheduleRevokeDelegation) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Charlie, + Precompile, + EvmDataWriter::new_with_selector(Action::ScheduleRevokeDelegation) + .write(Address(Alice.into())) .build(), - None, - &Context { - address: precompile_address(), - caller: TestAccount::Charlie.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 281793000, - logs: Default::default(), - })) - ); - - // Expected true because we scheduled a revoke request - expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); + ) + .expect_cost(281793000) + .expect_no_logs() + .execute_returns(vec![]); + // Assert that we have pending requests - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) - .write(Address(TestAccount::Charlie.into())) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) + .write(Address(Charlie.into())) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -902,100 +572,66 @@ fn delegation_request_is_pending_works() { fn delegation_request_is_pending_returns_false_for_non_existing_delegator() { ExtBuilder::default().build().execute_with(|| { // Expected false because delegator Bob does not exist - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that we return false - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) - .write(Address(TestAccount::Bob.into())) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::DelegationRequestIsPending) + .write(Address(Bob.into())) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }) } #[test] fn candidate_exit_is_pending_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - // Expected false because we dont have pending requests yet - let mut expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); // Assert that we don't have pending requests - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); // Schedule exit request - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::ScheduleLeaveCandidates) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::ScheduleLeaveCandidates) .write(U256::one()) .build(), - None, - &Context { - address: precompile_address(), - caller: TestAccount::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 303417000, - logs: Default::default(), - })) - ); - - // Expected true because we scheduled exit - expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); + ) + .expect_cost(303417000) + .expect_no_logs() + .execute_returns(vec![]); + // Assert that we have pending exit - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -1003,99 +639,65 @@ fn candidate_exit_is_pending_works() { fn candidate_exit_is_pending_returns_false_for_non_existing_delegator() { ExtBuilder::default().build().execute_with(|| { // Expected false because candidate Bob does not exist - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that we return false - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) - .write(Address(TestAccount::Bob.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateExitIsPending) + .write(Address(Bob.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }) } #[test] fn candidate_request_is_pending_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_050)]) - .with_candidates(vec![(TestAccount::Alice, 1_050)]) + .with_balances(vec![(Alice, 1_050)]) + .with_candidates(vec![(Alice, 1_050)]) .build() .execute_with(|| { - // Expected false because we dont have pending requests yet - let mut expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); // Assert that we dont have pending requests - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); // Schedule bond less request - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::ScheduleCandidateBondLess) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::ScheduleCandidateBondLess) .write(U256::zero()) .build(), - None, - &Context { - address: precompile_address(), - caller: TestAccount::Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 151710000, - logs: Default::default(), - })) - ); - - // Expected true because we scheduled a bond less request - expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: Default::default(), - logs: Default::default(), - })); + ) + .expect_cost(151710000) + .expect_no_logs() + .execute_returns(vec![]); + // Assert that we have pending requests - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) - .write(Address(TestAccount::Alice.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) + .write(Address(Alice.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -1103,49 +705,36 @@ fn candidate_request_is_pending_works() { fn candidate_request_is_pending_returns_false_for_non_existing_candidate() { ExtBuilder::default().build().execute_with(|| { // Expected false because candidate Bob does not exist - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(false).build(), - cost: Default::default(), - logs: Default::default(), - })); - // Assert that we return false - assert_eq!( - precompiles().execute( - precompile_address(), - &EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) - .write(Address(TestAccount::Bob.into())) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::CandidateRequestIsPending) + .write(Address(Bob.into())) .build(), - None, - &evm_test_context(), - false - ), - expected_result - ); + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); }) } #[test] fn join_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"join_candidates(uint256,uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice(&selector); - let amount: U256 = 1000.into(); - amount.to_big_endian(&mut input_data[4..36]); - let candidate_count = U256::zero(); - candidate_count.to_big_endian(&mut input_data[36..]); + let input_data = EvmDataWriter::new_with_selector(Action::JoinCandidates) + .write(U256::from(1000u32)) + .write(U256::zero()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::JoinedCollatorCandidates { - account: TestAccount::Alice, + account: Alice, amount_locked: 1000, new_total_amt_locked: 1000, } @@ -1160,24 +749,20 @@ fn join_candidates_works() { #[test] fn leave_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"leave_candidates(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let candidate_count = U256::one(); - candidate_count.to_big_endian(&mut input_data[4..]); + let input_data = EvmDataWriter::new_with_selector(Action::LeaveCandidates) + .write(U256::one()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateScheduledExit { exit_allowed_round: 1, - candidate: TestAccount::Alice, + candidate: Alice, scheduled_exit: 3, } .into(); @@ -1189,24 +774,20 @@ fn leave_candidates_works() { #[test] fn schedule_leave_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"schedule_leave_candidates(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let candidate_count = U256::one(); - candidate_count.to_big_endian(&mut input_data[4..]); + let input_data = EvmDataWriter::new_with_selector(Action::ScheduleLeaveCandidates) + .write(U256::one()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateScheduledExit { exit_allowed_round: 1, - candidate: TestAccount::Alice, + candidate: Alice, scheduled_exit: 3, } .into(); @@ -1218,29 +799,26 @@ fn schedule_leave_candidates_works() { #[test] fn execute_leave_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_leave_candidates( - Origin::signed(TestAccount::Alice), + Origin::signed(Alice), 1 )); roll_to(10); - let selector = &Keccak256::digest(b"execute_leave_candidates(address,uint256)")[0..4]; - // Construct data - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let candidate_delegation_count = U256::zero(); - candidate_delegation_count.to_big_endian(&mut input_data[36..]); + let input_data = EvmDataWriter::new_with_selector(Action::ExecuteLeaveCandidates) + .write(Address(Alice.into())) + .write(U256::zero()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateLeft { - ex_candidate: TestAccount::Alice, + ex_candidate: Alice, unlocked_amount: 1_000, new_total_amt_locked: 0, } @@ -1253,29 +831,24 @@ fn execute_leave_candidates_works() { #[test] fn cancel_leave_candidates_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_leave_candidates( - Origin::signed(TestAccount::Alice), + Origin::signed(Alice), 1 )); - let selector = &Keccak256::digest(b"cancel_leave_candidates(uint256)")[0..4]; - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let candidate_count = U256::zero(); - candidate_count.to_big_endian(&mut input_data[4..]); + let input_data = EvmDataWriter::new_with_selector(Action::CancelLeaveCandidates) + .write(U256::zero()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); - let expected: crate::mock::Event = StakingEvent::CancelledCandidateExit { - candidate: TestAccount::Alice, - } - .into(); + let expected: crate::mock::Event = + StakingEvent::CancelledCandidateExit { candidate: Alice }.into(); // Assert that the events vector contains the one expected assert!(events().contains(&expected)); }); @@ -1284,26 +857,19 @@ fn cancel_leave_candidates_works() { #[test] fn go_online_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - assert_ok!(ParachainStaking::go_offline(Origin::signed( - TestAccount::Alice - ))); - let selector = &Keccak256::digest(b"go_online()")[0..4]; + assert_ok!(ParachainStaking::go_offline(Origin::signed(Alice))); - // Construct data - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); + let input_data = EvmDataWriter::new_with_selector(Action::GoOnline).build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); - let expected: crate::mock::Event = StakingEvent::CandidateBackOnline { - candidate: TestAccount::Alice, - } - .into(); + let expected: crate::mock::Event = + StakingEvent::CandidateBackOnline { candidate: Alice }.into(); // Assert that the events vector contains the one expected assert!(events().contains(&expected)); }); @@ -1312,23 +878,16 @@ fn go_online_works() { #[test] fn go_offline_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"go_offline()")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); - + let input_data = EvmDataWriter::new_with_selector(Action::GoOffline).build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); - let expected: crate::mock::Event = StakingEvent::CandidateWentOffline { - candidate: TestAccount::Alice, - } - .into(); + let expected: crate::mock::Event = + StakingEvent::CandidateWentOffline { candidate: Alice }.into(); // Assert that the events vector contains the one expected assert!(events().contains(&expected)); }); @@ -1337,23 +896,19 @@ fn go_offline_works() { #[test] fn candidate_bond_more_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_500)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"candidate_bond_more(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let bond_more_amount: U256 = 500.into(); - bond_more_amount.to_big_endian(&mut input_data[4..36]); + let input_data = EvmDataWriter::new_with_selector(Action::CandidateBondMore) + .write(U256::from(500)) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateBondedMore { - candidate: TestAccount::Alice, + candidate: Alice, amount: 500, new_total_bond: 1500, } @@ -1367,23 +922,19 @@ fn candidate_bond_more_works() { #[test] fn candidate_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"candidate_bond_less(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let bond_less_amount: U256 = 500.into(); - bond_less_amount.to_big_endian(&mut input_data[4..36]); + let input_data = EvmDataWriter::new_with_selector(Action::CandidateBondLess) + .write(U256::from(500)) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateBondLessRequested { - candidate: TestAccount::Alice, + candidate: Alice, amount_to_decrease: 500, execute_round: 3, } @@ -1396,23 +947,19 @@ fn candidate_bond_less_works() { #[test] fn schedule_candidate_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"schedule_candidate_bond_less(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let bond_less_amount: U256 = 500.into(); - bond_less_amount.to_big_endian(&mut input_data[4..36]); + let input_data = EvmDataWriter::new_with_selector(Action::ScheduleCandidateBondLess) + .write(U256::from(500)) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateBondLessRequested { - candidate: TestAccount::Alice, + candidate: Alice, amount_to_decrease: 500, execute_round: 3, } @@ -1425,28 +972,25 @@ fn schedule_candidate_bond_less_works() { #[test] fn execute_candidate_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_500)]) + .with_balances(vec![(Alice, 1_500)]) + .with_candidates(vec![(Alice, 1_500)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"execute_candidate_bond_less(address)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - assert_ok!(ParachainStaking::schedule_candidate_bond_less( - Origin::signed(TestAccount::Alice), + Origin::signed(Alice), 500 )); roll_to(10); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + let input_data = EvmDataWriter::new_with_selector(Action::ExecuteCandidateBondLess) + .write(Address(Alice.into())) + .build(); + + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CandidateBondedLess { - candidate: TestAccount::Alice, + candidate: Alice, amount: 500, new_bond: 1000, } @@ -1459,26 +1003,23 @@ fn execute_candidate_bond_less_works() { #[test] fn cancel_candidate_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_200)]) - .with_candidates(vec![(TestAccount::Alice, 1_200)]) + .with_balances(vec![(Alice, 1_200)]) + .with_candidates(vec![(Alice, 1_200)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"cancel_candidate_bond_less()")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - assert_ok!(ParachainStaking::schedule_candidate_bond_less( - Origin::signed(TestAccount::Alice), + Origin::signed(Alice), 200 )); + let input_data = + EvmDataWriter::new_with_selector(Action::CancelCandidateBondLess).build(); + // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CancelledCandidateBondLess { - candidate: TestAccount::Alice, + candidate: Alice, amount: 200, execute_round: 3, } @@ -1492,32 +1033,26 @@ fn cancel_candidate_bond_less_works() { #[test] fn nominate_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"nominate(address,uint256,uint256,uint256)")[0..4]; - - // Construct selector for nominate - let mut input_data = Vec::::from([0u8; 132]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let delegation_amount: U256 = 1_000.into(); - delegation_amount.to_big_endian(&mut input_data[36..68]); - let collator_delegation_count = U256::zero(); - collator_delegation_count.to_big_endian(&mut input_data[68..100]); - let delegation_count = U256::zero(); - delegation_count.to_big_endian(&mut input_data[100..]); + let input_data = EvmDataWriter::new_with_selector(Action::Nominate) + .write(Address(Alice.into())) + .write(U256::from(1_000)) + .write(U256::zero()) + .write(U256::zero()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); - assert!(ParachainStaking::is_delegator(&TestAccount::Bob)); + assert!(ParachainStaking::is_delegator(&Bob)); let expected: crate::mock::Event = StakingEvent::Delegation { - delegator: TestAccount::Bob, + delegator: Bob, locked_amount: 1_000, - candidate: TestAccount::Alice, + candidate: Alice, delegator_position: parachain_staking::DelegatorAdded::AddedToTop { new_total: 2_000, }, @@ -1531,32 +1066,26 @@ fn nominate_works() { #[test] fn delegate_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"delegate(address,uint256,uint256,uint256)")[0..4]; - - // Construct selector for nominate - let mut input_data = Vec::::from([0u8; 132]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let delegation_amount: U256 = 1_000.into(); - delegation_amount.to_big_endian(&mut input_data[36..68]); - let collator_delegation_count = U256::zero(); - collator_delegation_count.to_big_endian(&mut input_data[68..100]); - let delegation_count = U256::zero(); - delegation_count.to_big_endian(&mut input_data[100..]); + let input_data = EvmDataWriter::new_with_selector(Action::Delegate) + .write(Address(Alice.into())) + .write(U256::from(1_000)) + .write(U256::zero()) + .write(U256::zero()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); - assert!(ParachainStaking::is_delegator(&TestAccount::Bob)); + assert!(ParachainStaking::is_delegator(&Bob)); let expected: crate::mock::Event = StakingEvent::Delegation { - delegator: TestAccount::Bob, + delegator: Bob, locked_amount: 1_000, - candidate: TestAccount::Alice, + candidate: Alice, delegator_position: parachain_staking::DelegatorAdded::AddedToTop { new_total: 2_000, }, @@ -1571,25 +1100,21 @@ fn delegate_works() { #[test] fn leave_nominators_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"leave_nominators(uint256)")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - let delegation_count = U256::one(); - delegation_count.to_big_endian(&mut input_data[4..]); + let input_data = EvmDataWriter::new_with_selector(Action::LeaveNominators) + .write(U256::one()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegatorExitScheduled { round: 1, - delegator: TestAccount::Bob, + delegator: Bob, scheduled_exit: 3, } .into(); @@ -1601,23 +1126,20 @@ fn leave_nominators_works() { #[test] fn schedule_leave_delegators_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"schedule_leave_delegators()")[0..4]; - - // Construct data - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); + let input_data = + EvmDataWriter::new_with_selector(Action::ScheduleLeaveDelegators).build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegatorExitScheduled { round: 1, - delegator: TestAccount::Bob, + delegator: Bob, scheduled_exit: 3, } .into(); @@ -1629,29 +1151,26 @@ fn schedule_leave_delegators_works() { #[test] fn execute_leave_delegators_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 500)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_leave_delegators(Origin::signed( - TestAccount::Bob + Bob ))); roll_to(10); - let selector = &Keccak256::digest(b"execute_leave_delegators(address,uint256)")[0..4]; - // Construct data - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Bob.to_h160().0); - let delegation_count = U256::one(); - delegation_count.to_big_endian(&mut input_data[36..]); + let input_data = EvmDataWriter::new_with_selector(Action::ExecuteLeaveDelegators) + .write(Address(Bob.into())) + .write(U256::one()) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegatorLeft { - delegator: TestAccount::Bob, + delegator: Bob, unstaked_amount: 500, } .into(); @@ -1663,27 +1182,23 @@ fn execute_leave_delegators_works() { #[test] fn cancel_leave_delegators_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 500)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_leave_delegators(Origin::signed( - TestAccount::Bob + Bob ))); - let selector = &Keccak256::digest(b"cancel_leave_delegators()")[0..4]; - // Construct data - let mut input_data = Vec::::from([0u8; 4]); - input_data[0..4].copy_from_slice(&selector); + let input_data = + EvmDataWriter::new_with_selector(Action::CancelLeaveDelegators).build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); - let expected: crate::mock::Event = StakingEvent::DelegatorExitCancelled { - delegator: TestAccount::Bob, - } - .into(); + let expected: crate::mock::Event = + StakingEvent::DelegatorExitCancelled { delegator: Bob }.into(); // Assert that the events vector contains the one expected assert!(events().contains(&expected)); }); @@ -1693,25 +1208,22 @@ fn cancel_leave_delegators_works() { #[test] fn revoke_nomination_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"revoke_nomination(address)")[0..4]; - - // Construct selector for revoke_nomination - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::RevokeNomination) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationRevocationScheduled { round: 1, - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, scheduled_exit: 3, } .into(); @@ -1723,25 +1235,22 @@ fn revoke_nomination_works() { #[test] fn schedule_revoke_delegation_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { - let selector = &Keccak256::digest(b"schedule_revoke_delegation(address)")[0..4]; - - // Construct selector for schedule_revoke_delegation - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::ScheduleRevokeDelegation) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationRevocationScheduled { round: 1, - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, scheduled_exit: 3, } .into(); @@ -1754,24 +1263,21 @@ fn schedule_revoke_delegation_works() { #[test] fn nominator_bond_more_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 500)]) .build() .execute_with(|| { - // Construct the delegator_bond_more call - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"nominator_bond_more(address,uint256)")[0..4]); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let bond_more_amount: U256 = 500.into(); - bond_more_amount.to_big_endian(&mut input_data[36..68]); + let input_data = EvmDataWriter::new_with_selector(Action::NominatorBondMore) + .write(Address(Alice.into())) + .write(U256::from(500)) + .build(); - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationIncreased { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, amount: 500, in_top: true, } @@ -1784,24 +1290,21 @@ fn nominator_bond_more_works() { #[test] fn delegator_bond_more_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 500)]) .build() .execute_with(|| { - // Construct the delegator_bond_more call - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"delegator_bond_more(address,uint256)")[0..4]); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let bond_more_amount: U256 = 500.into(); - bond_more_amount.to_big_endian(&mut input_data[36..68]); + let input_data = EvmDataWriter::new_with_selector(Action::DelegatorBondMore) + .write(Address(Alice.into())) + .write(U256::from(500)) + .build(); - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationIncreased { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, amount: 500, in_top: true, } @@ -1815,25 +1318,22 @@ fn delegator_bond_more_works() { #[test] fn nominator_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_500)]) .build() .execute_with(|| { - // Construct the delegator_bond_less call - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"nominator_bond_less(address,uint256)")[0..4]); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let bond_less_amount: U256 = 500.into(); - bond_less_amount.to_big_endian(&mut input_data[36..68]); + let input_data = EvmDataWriter::new_with_selector(Action::NominatorBondLess) + .write(Address(Alice.into())) + .write(U256::from(500)) + .build(); - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); // Check for the right events. let expected_event: crate::mock::Event = StakingEvent::DelegationDecreaseScheduled { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, amount_to_decrease: 500, execute_round: 3, } @@ -1846,26 +1346,22 @@ fn nominator_bond_less_works() { #[test] fn schedule_delegator_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_500)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_500)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_500)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_500)]) .build() .execute_with(|| { - // Construct the delegator_bond_less call - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice( - &Keccak256::digest(b"schedule_delegator_bond_less(address,uint256)")[0..4], - ); - input_data[16..36].copy_from_slice(&TestAccount::Alice.to_h160().0); - let bond_less_amount: U256 = 500.into(); - bond_less_amount.to_big_endian(&mut input_data[36..68]); + let input_data = EvmDataWriter::new_with_selector(Action::ScheduleDelegatorBondLess) + .write(Address(Alice.into())) + .write(U256::from(500)) + .build(); - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); // Check for the right events. let expected_event: crate::mock::Event = StakingEvent::DelegationDecreaseScheduled { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, amount_to_decrease: 500, execute_round: 3, } @@ -1878,30 +1374,28 @@ fn schedule_delegator_bond_less_works() { #[test] fn execute_revoke_delegation_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_revoke_delegation( - Origin::signed(TestAccount::Bob), - TestAccount::Alice + Origin::signed(Bob), + Alice )); roll_to(10); - let selector = &Keccak256::digest(b"execute_delegation_request(address,address)")[0..4]; - // Construct selector - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Bob.to_h160().0); - input_data[48..].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::ExecuteDelegationRequest) + .write(Address(Bob.into())) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationRevoked { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, unstaked_amount: 1_000, } .into(); @@ -1913,31 +1407,29 @@ fn execute_revoke_delegation_works() { #[test] fn execute_delegator_bond_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_delegator_bond_less( - Origin::signed(TestAccount::Bob), - TestAccount::Alice, + Origin::signed(Bob), + Alice, 500 )); roll_to(10); - let selector = &Keccak256::digest(b"execute_delegation_request(address,address)")[0..4]; - // Construct selector - let mut input_data = Vec::::from([0u8; 68]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..36].copy_from_slice(&TestAccount::Bob.to_h160().0); - input_data[48..].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::ExecuteDelegationRequest) + .write(Address(Bob.into())) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Alice, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Alice, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::DelegationDecreased { - delegator: TestAccount::Bob, - candidate: TestAccount::Alice, + delegator: Bob, + candidate: Alice, amount: 500, in_top: true, } @@ -1950,28 +1442,26 @@ fn execute_delegator_bond_less_works() { #[test] fn cancel_revoke_delegation_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_revoke_delegation( - Origin::signed(TestAccount::Bob), - TestAccount::Alice + Origin::signed(Bob), + Alice )); - let selector = &Keccak256::digest(b"cancel_delegation_request(address)")[0..4]; - // Construct selector - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::CancelDelegationRequest) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CancelledDelegationRequest { - delegator: TestAccount::Bob, - collator: TestAccount::Alice, + delegator: Bob, + collator: Alice, cancelled_request: parachain_staking::CancelledScheduledRequest { when_executable: 3, action: parachain_staking::DelegationAction::Revoke(1_000), @@ -1986,29 +1476,27 @@ fn cancel_revoke_delegation_works() { #[test] fn cancel_delegator_bonded_less_works() { ExtBuilder::default() - .with_balances(vec![(TestAccount::Alice, 1_000), (TestAccount::Bob, 1_000)]) - .with_candidates(vec![(TestAccount::Alice, 1_000)]) - .with_delegations(vec![(TestAccount::Bob, TestAccount::Alice, 1_000)]) + .with_balances(vec![(Alice, 1_000), (Bob, 1_000)]) + .with_candidates(vec![(Alice, 1_000)]) + .with_delegations(vec![(Bob, Alice, 1_000)]) .build() .execute_with(|| { assert_ok!(ParachainStaking::schedule_delegator_bond_less( - Origin::signed(TestAccount::Bob), - TestAccount::Alice, + Origin::signed(Bob), + Alice, 500 )); - let selector = &Keccak256::digest(b"cancel_delegation_request(address)")[0..4]; - // Construct selector - let mut input_data = Vec::::from([0u8; 36]); - input_data[0..4].copy_from_slice(&selector); - input_data[16..].copy_from_slice(&TestAccount::Alice.to_h160().0); + let input_data = EvmDataWriter::new_with_selector(Action::CancelDelegationRequest) + .write(Address(Alice.into())) + .build(); // Make sure the call goes through successfully - assert_ok!(Call::Evm(evm_call(TestAccount::Bob, input_data)).dispatch(Origin::root())); + assert_ok!(Call::Evm(evm_call(Bob, input_data)).dispatch(Origin::root())); let expected: crate::mock::Event = StakingEvent::CancelledDelegationRequest { - delegator: TestAccount::Bob, - collator: TestAccount::Alice, + delegator: Bob, + collator: Alice, cancelled_request: parachain_staking::CancelledScheduledRequest { when_executable: 3, action: parachain_staking::DelegationAction::Decrease(500), diff --git a/precompiles/relay-encoder/RelayEncoder.sol b/precompiles/relay-encoder/RelayEncoder.sol index ef14e080da2..53905fc1cb3 100644 --- a/precompiles/relay-encoder/RelayEncoder.sol +++ b/precompiles/relay-encoder/RelayEncoder.sol @@ -10,32 +10,44 @@ pragma solidity >=0.8.0; */ interface RelayEncoder { - // dev Encode 'bond' relay call // Selector: 31627376 // @param controller_address: Address of the controller // @param amount: The amount to bond // @param reward_destination: the account that should receive the reward // @returns The bytes associated with the encoded call - function encode_bond(uint256 controller_address, uint256 amount, bytes memory reward_destination) external pure returns (bytes memory result); + function encode_bond( + uint256 controller_address, + uint256 amount, + bytes memory reward_destination + ) external pure returns (bytes memory result); // dev Encode 'bond_extra' relay call // Selector: 49def326 // @param amount: The extra amount to bond // @returns The bytes associated with the encoded call - function encode_bond_extra(uint256 amount) external pure returns (bytes memory result); + function encode_bond_extra(uint256 amount) + external + pure + returns (bytes memory result); // dev Encode 'unbond' relay call - // Selector: bc4b2187 + // Selector: 2cd61217 // @param amount: The amount to unbond // @returns The bytes associated with the encoded call - function encode_unbond(uint256 amount) external pure returns (bytes memory result); + function encode_unbond(uint256 amount) + external + pure + returns (bytes memory result); // dev Encode 'withdraw_unbonded' relay call // Selector: 2d220331 // @param slashes: Weight hint, number of slashing spans // @returns The bytes associated with the encoded call - function encode_withdraw_unbonded(uint32 slashes) external pure returns (bytes memory result); + function encode_withdraw_unbonded(uint32 slashes) + external + pure + returns (bytes memory result); // dev Encode 'validate' relay call // Selector: 3a0d803a @@ -43,14 +55,20 @@ interface RelayEncoder { // @param blocked: Whether or not the validator is accepting more nominations // @returns The bytes associated with the encoded call // selector: 3a0d803a - function encode_validate(uint256 comission, bool blocked) external pure returns (bytes memory result); + function encode_validate(uint256 comission, bool blocked) + external + pure + returns (bytes memory result); // dev Encode 'nominate' relay call // Selector: a7cb124b // @param nominees: An array of AccountIds corresponding to the accounts we will nominate // @param blocked: Whether or not the validator is accepting more nominations // @returns The bytes associated with the encoded call - function encode_nominate(uint256 [] memory nominees) external pure returns (bytes memory result); + function encode_nominate(uint256[] memory nominees) + external + pure + returns (bytes memory result); // dev Encode 'chill' relay call // Selector: bc4b2187 @@ -61,18 +79,26 @@ interface RelayEncoder { // Selector: 9801b147 // @param reward_destination: the account that should receive the reward // @returns The bytes associated with the encoded call - function encode_set_payee(bytes memory reward_destination) external pure returns (bytes memory result); + function encode_set_payee(bytes memory reward_destination) + external + pure + returns (bytes memory result); // dev Encode 'set_controller' relay call // Selector: 7a8f48c2 // @param controller: The controller address // @returns The bytes associated with the encoded call - function encode_set_controller(uint256 controller) external pure returns (bytes memory result); + function encode_set_controller(uint256 controller) + external + pure + returns (bytes memory result); // dev Encode 'rebond' relay call // Selector: add6b3bf // @param amount: The amount to rebond // @returns The bytes associated with the encoded call - function encode_rebond(uint256 amount) external pure returns (bytes memory result); - -} \ No newline at end of file + function encode_rebond(uint256 amount) + external + pure + returns (bytes memory result); +} diff --git a/precompiles/relay-encoder/src/lib.rs b/precompiles/relay-encoder/src/lib.rs index 1afacd1d5d7..ae814281b7d 100644 --- a/precompiles/relay-encoder/src/lib.rs +++ b/precompiles/relay-encoder/src/lib.rs @@ -20,16 +20,15 @@ #![cfg_attr(test, feature(assert_matches))] use cumulus_primitives_core::relay_chain; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, ensure, }; -use pallet_evm::Precompile; use pallet_staking::RewardDestination; use precompile_utils::{ - Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, - RuntimeHelper, + revert, succeed, Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, + PrecompileHandleExt, RuntimeHelper, }; use sp_core::{H256, U256}; use sp_runtime::AccountId32; @@ -83,25 +82,17 @@ enum Action { /// A precompile to provide relay stake calls encoding through evm pub struct RelayEncoderWrapper(PhantomData<(Runtime, RelayRuntime)>); +// TODO: Migrate to precompile_utils::Precompile. impl Precompile for RelayEncoderWrapper where RelayRuntime: StakeEncodeCall, Runtime: pallet_evm::Config, Runtime::Call: Dispatchable + GetDispatchInfo, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier(context, is_static, FunctionModifier::View)?; + handle.check_function_modifier(FunctionModifier::View)?; // Parse the function selector // These are the four-byte function selectors calculated from the RelayEncoder.sol @@ -109,16 +100,16 @@ where // https://docs.soliditylang.org/en/v0.8.0/abi-spec.html#function-selector match selector { // Storage Accessors - Action::EncodeBond => Self::encode_bond(input, gasometer), - Action::EncodeBondExtra => Self::encode_bond_extra(input, gasometer), - Action::EncodeUnbond => Self::encode_unbond(input, gasometer), - Action::EncodeWithdrawUnbonded => Self::encode_withdraw_unbonded(input, gasometer), - Action::EncodeValidate => Self::encode_validate(input, gasometer), - Action::EncodeNominate => Self::encode_nominate(input, gasometer), - Action::EncodeChill => Self::encode_chill(input, gasometer), - Action::EncodeSetPayee => Self::encode_set_payee(input, gasometer), - Action::EncodeSetController => Self::encode_set_controller(input, gasometer), - Action::EncodeRebond => Self::encode_rebond(input, gasometer), + Action::EncodeBond => Self::encode_bond(handle), + Action::EncodeBondExtra => Self::encode_bond_extra(handle), + Action::EncodeUnbond => Self::encode_unbond(handle), + Action::EncodeWithdrawUnbonded => Self::encode_withdraw_unbonded(handle), + Action::EncodeValidate => Self::encode_validate(handle), + Action::EncodeNominate => Self::encode_nominate(handle), + Action::EncodeChill => Self::encode_chill(handle), + Action::EncodeSetPayee => Self::encode_set_payee(handle), + Action::EncodeSetController => Self::encode_set_controller(handle), + Action::EncodeRebond => Self::encode_rebond(handle), } } } @@ -129,19 +120,17 @@ where Runtime: pallet_evm::Config, Runtime::Call: Dispatchable + GetDispatchInfo, { - fn encode_bond( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_bond(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 4)?; + let mut input = handle.read_input()?; + input.expect_arguments(4)?; - let address: [u8; 32] = input.read::(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; - let relay_amount = u256_to_relay_amount(gasometer, amount)?; + let address: [u8; 32] = input.read::()?.into(); + let amount: U256 = input.read()?; + let relay_amount = u256_to_relay_amount(amount)?; - let reward_destination = input.read::(gasometer)?.into(); + let reward_destination = input.read::()?.into(); let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::Bond( address.into(), relay_amount, @@ -150,91 +139,63 @@ where .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_bond_extra( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_bond_extra(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 1)?; - let amount: U256 = input.read(gasometer)?; - let relay_amount = u256_to_relay_amount(gasometer, amount)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; + let amount: U256 = input.read()?; + let relay_amount = u256_to_relay_amount(amount)?; let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::BondExtra(relay_amount)) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_unbond( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_unbond(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let amount: U256 = input.read(gasometer)?; - let relay_amount = u256_to_relay_amount(gasometer, amount)?; + let amount: U256 = input.read()?; + let relay_amount = u256_to_relay_amount(amount)?; let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::Unbond(relay_amount)) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_withdraw_unbonded( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_withdraw_unbonded(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let num_slashing_spans: u32 = input.read(gasometer)?; + let num_slashing_spans: u32 = input.read()?; let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::WithdrawUnbonded(num_slashing_spans)) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_validate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_validate(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let parst_per_billion: u32 = input.read(gasometer)?; - let blocked: bool = input.read(gasometer)?; + let parst_per_billion: u32 = input.read()?; + let blocked: bool = input.read()?; let fraction = Perbill::from_parts(parst_per_billion); let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::Validate( pallet_staking::ValidatorPrefs { @@ -245,21 +206,14 @@ where .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_nominate( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_nominate(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let nominated_as_h256: Vec = input.read(gasometer)?; + let mut input = handle.read_input()?; + let nominated_as_h256: Vec = input.read()?; let nominated: Vec = nominated_as_h256 .iter() @@ -272,108 +226,72 @@ where .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_chill( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_chill(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 0)?; + let input = handle.read_input()?; + input.expect_arguments(0)?; let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::Chill) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_set_payee( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_set_payee(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 2)?; + let mut input = handle.read_input()?; + input.expect_arguments(2)?; - let reward_destination = input.read::(gasometer)?.into(); + let reward_destination = input.read::()?.into(); let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::SetPayee(reward_destination)) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_set_controller( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_set_controller(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let controller: [u8; 32] = input.read::(gasometer)?.into(); + let mut input = handle.read_input()?; + let controller: [u8; 32] = input.read::()?.into(); let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::SetController(controller.into())) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } - fn encode_rebond( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn encode_rebond(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - input.expect_arguments(gasometer, 1)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; - let amount: U256 = input.read(gasometer)?; - let relay_amount = u256_to_relay_amount(gasometer, amount)?; + let amount: U256 = input.read()?; + let relay_amount = u256_to_relay_amount(amount)?; let encoded: Bytes = RelayRuntime::encode_call(AvailableStakeCalls::Rebond(relay_amount)) .as_slice() .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(encoded).build(), - logs: Default::default(), - }) + Ok(succeed(EvmDataWriter::new().write(encoded).build())) } } -pub fn u256_to_relay_amount( - gasometer: &mut Gasometer, - value: U256, -) -> EvmResult { +pub fn u256_to_relay_amount(value: U256) -> EvmResult { value .try_into() - .map_err(|_| gasometer.revert("amount is too large for provided balance type")) + .map_err(|_| revert("amount is too large for provided balance type")) } // A wrapper to be able to implement here the EvmData reader @@ -393,31 +311,31 @@ impl Into> for RewardDestinationWrapper { } impl EvmData for RewardDestinationWrapper { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let reward_destination = reader.read::(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let reward_destination = reader.read::()?; let reward_destination_bytes = reward_destination.as_bytes(); ensure!( reward_destination_bytes.len() > 0, - gasometer.revert("Reward destinations cannot be empty") + revert("Reward destinations cannot be empty") ); // For simplicity we use an EvmReader here let mut encoded_reward_destination = EvmDataReader::new(&reward_destination_bytes); // We take the first byte - let enum_selector = encoded_reward_destination.read_raw_bytes(gasometer, 1)?; + let enum_selector = encoded_reward_destination.read_raw_bytes(1)?; // The firs byte selects the enum variant match enum_selector[0] { 0u8 => Ok(RewardDestinationWrapper(RewardDestination::Staked)), 1u8 => Ok(RewardDestinationWrapper(RewardDestination::Stash)), 2u8 => Ok(RewardDestinationWrapper(RewardDestination::Controller)), 3u8 => { - let address = encoded_reward_destination.read::(gasometer)?; + let address = encoded_reward_destination.read::()?; Ok(RewardDestinationWrapper(RewardDestination::Account( address.as_fixed_bytes().clone().into(), ))) } 4u8 => Ok(RewardDestinationWrapper(RewardDestination::None)), - _ => Err(gasometer.revert("Not available enum")), + _ => Err(revert("Not available enum")), } } diff --git a/precompiles/relay-encoder/src/mock.rs b/precompiles/relay-encoder/src/mock.rs index 1e41db87d6f..82149acc4c0 100644 --- a/precompiles/relay-encoder/src/mock.rs +++ b/precompiles/relay-encoder/src/mock.rs @@ -32,7 +32,7 @@ use sp_runtime::{ Perbill, }; -pub type AccountId = TestAccount; +pub type AccountId = Account; pub type Balance = u128; pub type BlockNumber = u64; @@ -70,41 +70,50 @@ construct_runtime!( derive_more::Display, scale_info::TypeInfo, )] -pub enum TestAccount { +pub enum Account { Alice, Bob, Charlie, Bogus, + Precompile, } -impl Default for TestAccount { +impl Default for Account { fn default() -> Self { Self::Bogus } } -impl AddressMapping for TestAccount { - fn into_account_id(h160_account: H160) -> TestAccount { +impl Into for Account { + fn into(self) -> H160 { + match self { + Account::Alice => H160::repeat_byte(0xAA), + Account::Bob => H160::repeat_byte(0xBB), + Account::Charlie => H160::repeat_byte(0xCC), + Account::Bogus => H160::repeat_byte(0xDD), + Account::Precompile => H160::from_low_u64_be(1), + } + } +} + +impl AddressMapping for Account { + fn into_account_id(h160_account: H160) -> Account { match h160_account { a if a == H160::repeat_byte(0xAA) => Self::Alice, a if a == H160::repeat_byte(0xBB) => Self::Bob, a if a == H160::repeat_byte(0xCC) => Self::Charlie, + a if a == H160::from_low_u64_be(1) => Self::Precompile, _ => Self::Bogus, } } } -impl From for TestAccount { - fn from(x: H160) -> TestAccount { - TestAccount::into_account_id(x) +impl From for Account { + fn from(x: H160) -> Account { + Account::into_account_id(x) } } -/// The relay encoder precompile is available at address one in the mock runtime. -pub fn precompile_address() -> H160 { - H160::from_low_u64_be(1) -} - parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; @@ -118,7 +127,7 @@ impl frame_system::Config for Runtime { type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = TestAccount; + type AccountId = Account; type Lookup = IdentityLookup; type Header = Header; type Event = Event; @@ -173,27 +182,17 @@ impl PrecompileSet for TestPrecompiles where RelayEncoderWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == precompile_address() => Some(RelayEncoderWrapper::< - R, - test_relay_runtime::TestEncoder, - >::execute( - input, target_gas, context, is_static - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == H160::from_low_u64_be(1) => { + Some(RelayEncoderWrapper::::execute(handle)) + } _ => None, } } fn is_precompile(&self, address: H160) -> bool { - address == precompile_address() + address == H160::from_low_u64_be(1) } } @@ -204,9 +203,9 @@ parameter_types! { impl pallet_evm::Config for Runtime { type FeeCalculator = (); type GasWeightMapping = (); - type CallOrigin = EnsureAddressRoot; - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = TestAccount; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = Account; type Currency = Balances; type Event = Event; type Runner = pallet_evm::runner::stack::Runner; @@ -270,14 +269,3 @@ impl ExtBuilder { ext } } - -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} diff --git a/precompiles/relay-encoder/src/tests.rs b/precompiles/relay-encoder/src/tests.rs index cb66b308e3e..4634cc7b627 100644 --- a/precompiles/relay-encoder/src/tests.rs +++ b/precompiles/relay-encoder/src/tests.rs @@ -13,24 +13,17 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . - -use std::assert_matches::assert_matches; - use crate::mock::{ - evm_test_context, precompile_address, ExtBuilder, PrecompilesValue, Runtime, - TestAccount::Alice, TestPrecompiles, + Account::{Alice, Precompile}, + ExtBuilder, PrecompilesValue, Runtime, TestPrecompiles, }; use crate::test_relay_runtime::TestEncoder; +use crate::AvailableStakeCalls; use crate::StakeEncodeCall; use crate::*; -use crate::{AvailableStakeCalls, PrecompileOutput}; -use fp_evm::PrecompileFailure; -use num_enum::TryFromPrimitive; -use pallet_evm::{ExitSucceed, PrecompileSet}; use pallet_staking::RewardDestination; use pallet_staking::ValidatorPrefs; -use precompile_utils::{Bytes, EvmDataWriter}; -use sha3::{Digest, Keccak256}; +use precompile_utils::{testing::*, Bytes, EvmDataWriter}; use sp_core::{H256, U256}; use sp_runtime::Perbill; @@ -40,113 +33,33 @@ fn precompiles() -> TestPrecompiles { #[test] fn test_selector_enum() { - let mut buffer = [0u8; 4]; - buffer.copy_from_slice(&Keccak256::digest(b"encode_bond(uint256,uint256,bytes)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeBond, - ); - buffer.copy_from_slice(&Keccak256::digest(b"encode_bond_extra(uint256)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeBondExtra, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_chill()")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeChill, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_nominate(uint256[])")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeNominate, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_rebond(uint256)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeRebond, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_set_controller(uint256)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeSetController, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_set_payee(bytes)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeSetPayee, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_unbond(uint256)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeUnbond, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_validate(uint256,bool)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeValidate, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"encode_withdraw_unbonded(uint32)")[0..4]); - - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::EncodeWithdrawUnbonded, - ); + assert_eq!(Action::EncodeBond as u32, 0x31627376); + assert_eq!(Action::EncodeBondExtra as u32, 0x49def326); + assert_eq!(Action::EncodeUnbond as u32, 0x2cd61217); + assert_eq!(Action::EncodeWithdrawUnbonded as u32, 0x2d220331); + assert_eq!(Action::EncodeValidate as u32, 0x3a0d803a); + assert_eq!(Action::EncodeNominate as u32, 0xa7cb124b); + assert_eq!(Action::EncodeChill as u32, 0xbc4b2187); + assert_eq!(Action::EncodeSetPayee as u32, 0x9801b147); + assert_eq!(Action::EncodeSetController as u32, 0x7a8f48c2); + assert_eq!(Action::EncodeRebond as u32, 0xadd6b3bf); } #[test] fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { - // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, .. })) - if &output == b"tried to parse selector out of bounds" - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if &output == b"unknown selector" - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -156,40 +69,30 @@ fn test_encode_bond() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let controller_address: H256 = [1u8; 32].into(); - let amount: U256 = 100u32.into(); - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeBond) - .write(controller_address) - .write(amount) - .write(RewardDestinationWrapper(RewardDestination::Controller)) - .build(); - - let expected_bytes: Bytes = TestEncoder::encode_call(AvailableStakeCalls::Bond( - [1u8; 32].into(), - 100u32.into(), - RewardDestination::Controller, - )) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeBond) + .write(H256::from([1u8; 32])) + .write(U256::from(100)) + .write(RewardDestinationWrapper(RewardDestination::Controller)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Bond( + [1u8; 32].into(), + 100u32.into(), + RewardDestination::Controller, + )) + .as_slice(), + )) + .build(), + ); }); } @@ -199,33 +102,24 @@ fn test_encode_bond_more() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let amount: U256 = 100u32.into(); - let input_data = EvmDataWriter::new_with_selector(Action::EncodeBondExtra) - .write(amount) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::BondExtra(100u32.into())) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeBondExtra) + .write(U256::from(100)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::BondExtra(100u32.into())) + .as_slice(), + )) + .build(), + ); }); } @@ -235,29 +129,21 @@ fn test_encode_chill() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let input_data = EvmDataWriter::new_with_selector(Action::EncodeChill).build(); - - let expected_bytes: Bytes = TestEncoder::encode_call(AvailableStakeCalls::Chill) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeChill).build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Chill).as_slice(), + )) + .build(), + ); }); } @@ -267,37 +153,27 @@ fn test_encode_nominate() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let array: Vec = vec![[1u8; 32].into(), [2u8; 32].into()]; - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeNominate) - .write(array) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::Nominate(vec![ - [1u8; 32].into(), - [2u8; 32].into(), - ])) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeNominate) + .write(vec![H256::from([1u8; 32]), H256::from([2u8; 32])]) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Nominate(vec![ + [1u8; 32].into(), + [2u8; 32].into(), + ])) + .as_slice(), + )) + .build(), + ); }); } @@ -307,34 +183,24 @@ fn test_encode_rebond() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let amount: U256 = 100u32.into(); - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeRebond) - .write(amount) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::Rebond(100u128)) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeRebond) + .write(U256::from(100)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Rebond(100u128)) + .as_slice(), + )) + .build(), + ); }); } @@ -344,34 +210,26 @@ fn test_encode_set_controller() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let controller: H256 = [1u8; 32].into(); - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeSetController) - .write(controller) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::SetController([1u8; 32].into())) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeSetController) + .write(H256::from([1u8; 32])) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::SetController( + [1u8; 32].into(), + )) + .as_slice(), + )) + .build(), + ) }); } @@ -381,33 +239,26 @@ fn test_encode_set_payee() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let input_data = EvmDataWriter::new_with_selector(Action::EncodeSetPayee) - .write(RewardDestinationWrapper(RewardDestination::Controller)) - .build(); - - let expected_bytes: Bytes = TestEncoder::encode_call(AvailableStakeCalls::SetPayee( - RewardDestination::Controller, - )) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeSetPayee) + .write(RewardDestinationWrapper(RewardDestination::Controller)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::SetPayee( + RewardDestination::Controller, + )) + .as_slice(), + )) + .build(), + ); }); } @@ -417,36 +268,24 @@ fn test_encode_unbond() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let amount: U256 = 100u32.into(); - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeUnbond) - .write(amount) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::Unbond(100u32.into())) - .as_slice() - .into(); - - // Expected result is one - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that no props have been opened. - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeUnbond) + .write(U256::from(100)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Unbond(100u32.into())) + .as_slice(), + )) + .build(), + ); }); } @@ -456,41 +295,30 @@ fn test_encode_validate() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let amount: U256 = 100u32.into(); - let blocked = true; - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeValidate) - .write(amount) - .write(blocked) - .build(); - - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::Validate(ValidatorPrefs { - commission: Perbill::from_parts(100u32.into()), - blocked: true, - })) - .as_slice() - .into(); - - // Expected result is one - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - // Assert that no props have been opened. - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeValidate) + .write(U256::from(100)) + .write(true) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::Validate( + ValidatorPrefs { + commission: Perbill::from_parts(100u32.into()), + blocked: true, + }, + )) + .as_slice(), + )) + .build(), + ); }); } @@ -500,34 +328,25 @@ fn test_encode_withdraw_unbonded() { .with_balances(vec![(Alice, 1000)]) .build() .execute_with(|| { - let amount: U256 = 100u32.into(); - - let input_data = EvmDataWriter::new_with_selector(Action::EncodeWithdrawUnbonded) - .write(amount) - .build(); - - // Ethereum style - let expected_bytes: Bytes = - TestEncoder::encode_call(AvailableStakeCalls::WithdrawUnbonded(100u32.into())) - .as_slice() - .into(); - - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(expected_bytes).build(), - cost: Default::default(), - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute( - precompile_address(), - &input_data, - None, - &evm_test_context(), - false - ), - expected_result - ); + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::EncodeWithdrawUnbonded) + .write(U256::from(100)) + .build(), + ) + .expect_cost(0) // TODO: Test db read/write costs + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Bytes::from( + TestEncoder::encode_call(AvailableStakeCalls::WithdrawUnbonded( + 100u32.into(), + )) + .as_slice(), + )) + .build(), + ); }); } diff --git a/precompiles/utils/src/data.rs b/precompiles/utils/src/data.rs index b136f1e7ac7..954227a3f28 100644 --- a/precompiles/utils/src/data.rs +++ b/precompiles/utils/src/data.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . -use crate::{EvmResult, Gasometer}; +use crate::{revert, EvmResult}; use alloc::borrow::ToOwned; use core::{any::type_name, ops::Range}; @@ -94,12 +94,12 @@ impl<'a> EvmDataReader<'a> { } /// Create a new input parser from a selector-initial input. - pub fn new_with_selector(gasometer: &mut Gasometer, input: &'a [u8]) -> EvmResult<(Self, T)> + pub fn read_selector(input: &'a [u8]) -> EvmResult where T: num_enum::TryFromPrimitive, { if input.len() < 4 { - return Err(gasometer.revert("tried to parse selector out of bounds")); + return Err(revert("tried to parse selector out of bounds")); } let mut buffer = [0u8; 4]; @@ -110,52 +110,59 @@ impl<'a> EvmDataReader<'a> { "Failed to match function selector for {}", type_name::() ); - gasometer.revert("unknown selector") + revert("unknown selector") })?; - Ok((Self::new(&input[4..]), selector)) + Ok(selector) + } + + /// Create a new input parser from a selector-initial input. + pub fn new_skip_selector(input: &'a [u8]) -> EvmResult { + if input.len() < 4 { + return Err(revert("input is too short")); + } + + Ok(Self::new(&input[4..])) } /// Check the input has at least the correct amount of arguments before the end (32 bytes values). - pub fn expect_arguments(&self, gasometer: &mut Gasometer, args: usize) -> EvmResult { + pub fn expect_arguments(&self, args: usize) -> EvmResult { if self.input.len() >= self.cursor + args * 32 { Ok(()) } else { - Err(gasometer.revert("input doesn't match expected length")) + Err(revert("input doesn't match expected length")) } } /// Read data from the input. - /// Must be provided a gasometer to generate correct Revert errors. - /// TODO : Benchmark and add cost of parsing to gasometer ? - pub fn read(&mut self, gasometer: &mut Gasometer) -> EvmResult { - T::read(self, gasometer) + pub fn read(&mut self) -> EvmResult { + T::read(self) } /// Read raw bytes from the input. /// Doesn't handle any alignment checks, prefer using `read` instead of possible. /// Returns an error if trying to parse out of bounds. - pub fn read_raw_bytes(&mut self, gasometer: &mut Gasometer, len: usize) -> EvmResult<&[u8]> { - let range = self.move_cursor(gasometer, len)?; + pub fn read_raw_bytes(&mut self, len: usize) -> EvmResult<&[u8]> { + let range = self.move_cursor(len)?; let data = self .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse raw bytes out of bounds"))?; + .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; Ok(data) } /// Reads a pointer, returning a reader targetting the pointed location. - pub fn read_pointer(&mut self, gasometer: &mut Gasometer) -> EvmResult { + pub fn read_pointer(&mut self) -> EvmResult { let offset: usize = self - .read::(gasometer) - .map_err(|_| gasometer.revert("tried to parse array offset out of bounds"))? + .read::() + .map_err(|_| revert("tried to parse array offset out of bounds"))? .try_into() - .map_err(|_| gasometer.revert("array offset is too large"))?; + .map_err(|_| revert("array offset is too large"))?; if offset >= self.input.len() { - return Err(gasometer.revert("pointer points out of bounds")); + return Err(revert("pointer points out of bounds")); } Ok(Self { @@ -165,13 +172,13 @@ impl<'a> EvmDataReader<'a> { } /// Read remaining bytes - pub fn read_till_end(&mut self, gasometer: &mut Gasometer) -> EvmResult<&[u8]> { - let range = self.move_cursor(gasometer, self.input.len() - self.cursor)?; + pub fn read_till_end(&mut self) -> EvmResult<&[u8]> { + let range = self.move_cursor(self.input.len() - self.cursor)?; let data = self .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse raw bytes out of bounds"))?; + .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; Ok(data) } @@ -179,12 +186,12 @@ impl<'a> EvmDataReader<'a> { /// Move the reading cursor with provided length, and return a range from the previous cursor /// location to the new one. /// Checks cursor overflows. - fn move_cursor(&mut self, gasometer: &mut Gasometer, len: usize) -> EvmResult> { + fn move_cursor(&mut self, len: usize) -> EvmResult> { let start = self.cursor; let end = self .cursor .checked_add(len) - .ok_or_else(|| gasometer.revert("data reading cursor overflow"))?; + .ok_or_else(|| revert("data reading cursor overflow"))?; self.cursor = end; @@ -311,7 +318,7 @@ impl Default for EvmDataWriter { /// Data that can be converted from and to EVM data types. pub trait EvmData: Sized { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult; + fn read(reader: &mut EvmDataReader) -> EvmResult; fn write(writer: &mut EvmDataWriter, value: Self); fn has_static_size() -> bool; } @@ -322,12 +329,12 @@ impl EvmData for Tuple { for_tuples!(#( Tuple::has_static_size() )&*) } - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { + fn read(reader: &mut EvmDataReader) -> EvmResult { if !Self::has_static_size() { - let reader = &mut reader.read_pointer(gasometer)?; - Ok(for_tuples!( ( #( reader.read::(gasometer)? ),* ) )) + let reader = &mut reader.read_pointer()?; + Ok(for_tuples!( ( #( reader.read::()? ),* ) )) } else { - Ok(for_tuples!( ( #( reader.read::(gasometer)? ),* ) )) + Ok(for_tuples!( ( #( reader.read::()? ),* ) )) } } @@ -343,13 +350,13 @@ impl EvmData for Tuple { } impl EvmData for H256 { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let range = reader.move_cursor(gasometer, 32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; let data = reader .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse H256 out of bounds"))?; + .ok_or_else(|| revert("tried to parse H256 out of bounds"))?; Ok(H256::from_slice(data)) } @@ -364,13 +371,13 @@ impl EvmData for H256 { } impl EvmData for Address { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let range = reader.move_cursor(gasometer, 32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; let data = reader .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse H160 out of bounds"))?; + .ok_or_else(|| revert("tried to parse H160 out of bounds"))?; Ok(H160::from_slice(&data[12..32]).into()) } @@ -385,13 +392,13 @@ impl EvmData for Address { } impl EvmData for U256 { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let range = reader.move_cursor(gasometer, 32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; let data = reader .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse U256 out of bounds"))?; + .ok_or_else(|| revert("tried to parse U256 out of bounds"))?; Ok(U256::from_big_endian(data)) } @@ -411,13 +418,13 @@ macro_rules! impl_evmdata_for_uints { ($($uint:ty, )*) => { $( impl EvmData for $uint { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let range = reader.move_cursor(gasometer, 32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; let data = reader .input .get(range) - .ok_or_else(|| gasometer.revert(alloc::format!( + .ok_or_else(|| revert(alloc::format!( "tried to parse {} out of bounds", core::any::type_name::() )))?; @@ -444,13 +451,13 @@ impl_evmdata_for_uints!(u16, u32, u64, u128,); // The implementation for u8 is specific, for performance reasons. impl EvmData for u8 { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let range = reader.move_cursor(gasometer, 32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; let data = reader .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse u64 out of bounds"))?; + .ok_or_else(|| revert("tried to parse u64 out of bounds"))?; Ok(data[31]) } @@ -468,9 +475,8 @@ impl EvmData for u8 { } impl EvmData for bool { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let h256 = H256::read(reader, gasometer) - .map_err(|_| gasometer.revert("tried to parse bool out of bounds"))?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let h256 = H256::read(reader).map_err(|_| revert("tried to parse bool out of bounds"))?; Ok(!h256.is_zero()) } @@ -490,14 +496,14 @@ impl EvmData for bool { } impl EvmData for Vec { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let mut inner_reader = reader.read_pointer(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; let array_size: usize = inner_reader - .read::(gasometer) - .map_err(|_| gasometer.revert("tried to parse array length out of bounds"))? + .read::() + .map_err(|_| revert("tried to parse array length out of bounds"))? .try_into() - .map_err(|_| gasometer.revert("array length is too large"))?; + .map_err(|_| revert("array length is too large"))?; let mut array = vec![]; @@ -505,12 +511,12 @@ impl EvmData for Vec { input: inner_reader .input .get(32..) - .ok_or_else(|| gasometer.revert("try to read array items out of bound"))?, + .ok_or_else(|| revert("try to read array items out of bound"))?, cursor: 0, }; for _ in 0..array_size { - array.push(item_reader.read(gasometer)?); + array.push(item_reader.read()?); } Ok(array) @@ -544,23 +550,23 @@ impl EvmData for Vec { } impl EvmData for Bytes { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let mut inner_reader = reader.read_pointer(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; // Read bytes/string size. let array_size: usize = inner_reader - .read::(gasometer) - .map_err(|_| gasometer.revert("tried to parse bytes/string length out of bounds"))? + .read::() + .map_err(|_| revert("tried to parse bytes/string length out of bounds"))? .try_into() - .map_err(|_| gasometer.revert("bytes/string length is too large"))?; + .map_err(|_| revert("bytes/string length is too large"))?; // Get valid range over the bytes data. - let range = inner_reader.move_cursor(gasometer, array_size)?; + let range = inner_reader.move_cursor(array_size)?; let data = inner_reader .input .get(range) - .ok_or_else(|| gasometer.revert("tried to parse bytes/string out of bounds"))?; + .ok_or_else(|| revert("tried to parse bytes/string out of bounds"))?; let bytes = Self(data.to_owned()); diff --git a/precompiles/utils/src/data/xcm.rs b/precompiles/utils/src/data/xcm.rs index 36ca662c000..3b43acb1e46 100644 --- a/precompiles/utils/src/data/xcm.rs +++ b/precompiles/utils/src/data/xcm.rs @@ -16,7 +16,7 @@ //! Encoding of XCM types for solidity -use crate::{Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult, Gasometer}; +use crate::{revert, Bytes, EvmData, EvmDataReader, EvmDataWriter, EvmResult}; use frame_support::ensure; use sp_std::vec::Vec; @@ -55,101 +55,95 @@ pub(crate) fn network_id_to_bytes(network_id: NetworkId) -> Vec { } // Function to convert bytes to networkId -pub(crate) fn network_id_from_bytes( - gasometer: &mut Gasometer, - encoded_bytes: Vec, -) -> EvmResult { - ensure!( - encoded_bytes.len() > 0, - gasometer.revert("Junctions cannot be empty") - ); +pub(crate) fn network_id_from_bytes(encoded_bytes: Vec) -> EvmResult { + ensure!(encoded_bytes.len() > 0, revert("Junctions cannot be empty")); let mut encoded_network_id = EvmDataReader::new(&encoded_bytes); - let network_selector = encoded_network_id.read_raw_bytes(gasometer, 1)?; + let network_selector = encoded_network_id.read_raw_bytes(1)?; match network_selector[0] { 0 => Ok(NetworkId::Any), 1 => Ok(NetworkId::Named( - encoded_network_id.read_till_end(gasometer)?.to_vec(), + encoded_network_id.read_till_end()?.to_vec(), )), 2 => Ok(NetworkId::Polkadot), 3 => Ok(NetworkId::Kusama), - _ => Err(gasometer.revert("Non-valid Network Id")), + _ => Err(revert("Non-valid Network Id")), } } impl EvmData for Junction { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let junction = reader.read::(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let junction = reader.read::()?; let junction_bytes = junction.as_bytes(); ensure!( junction_bytes.len() > 0, - gasometer.revert("Junctions cannot be empty") + revert("Junctions cannot be empty") ); // For simplicity we use an EvmReader here let mut encoded_junction = EvmDataReader::new(&junction_bytes); // We take the first byte - let enum_selector = encoded_junction.read_raw_bytes(gasometer, 1)?; + let enum_selector = encoded_junction.read_raw_bytes(1)?; // The firs byte selects the enum variant match enum_selector[0] { 0 => { // In the case of Junction::Parachain, we need 4 additional bytes let mut data: [u8; 4] = Default::default(); - data.copy_from_slice(&encoded_junction.read_raw_bytes(gasometer, 4)?); + data.copy_from_slice(&encoded_junction.read_raw_bytes(4)?); let para_id = u32::from_be_bytes(data); Ok(Junction::Parachain(para_id)) } 1 => { // In the case of Junction::AccountId32, we need 32 additional bytes plus NetworkId let mut account: [u8; 32] = Default::default(); - account.copy_from_slice(&encoded_junction.read_raw_bytes(gasometer, 32)?); + account.copy_from_slice(&encoded_junction.read_raw_bytes(32)?); - let network = encoded_junction.read_till_end(gasometer)?.to_vec(); + let network = encoded_junction.read_till_end()?.to_vec(); Ok(Junction::AccountId32 { - network: network_id_from_bytes(gasometer, network)?, + network: network_id_from_bytes(network)?, id: account, }) } 2 => { // In the case of Junction::AccountIndex64, we need 8 additional bytes plus NetworkId let mut index: [u8; 8] = Default::default(); - index.copy_from_slice(&encoded_junction.read_raw_bytes(gasometer, 8)?); + index.copy_from_slice(&encoded_junction.read_raw_bytes(8)?); // Now we read the network - let network = encoded_junction.read_till_end(gasometer)?.to_vec(); + let network = encoded_junction.read_till_end()?.to_vec(); Ok(Junction::AccountIndex64 { - network: network_id_from_bytes(gasometer, network)?, + network: network_id_from_bytes(network)?, index: u64::from_be_bytes(index), }) } 3 => { // In the case of Junction::AccountKey20, we need 20 additional bytes plus NetworkId let mut account: [u8; 20] = Default::default(); - account.copy_from_slice(&encoded_junction.read_raw_bytes(gasometer, 20)?); + account.copy_from_slice(&encoded_junction.read_raw_bytes(20)?); - let network = encoded_junction.read_till_end(gasometer)?.to_vec(); + let network = encoded_junction.read_till_end()?.to_vec(); Ok(Junction::AccountKey20 { - network: network_id_from_bytes(gasometer, network)?, + network: network_id_from_bytes(network)?, key: account, }) } 4 => Ok(Junction::PalletInstance( - encoded_junction.read_raw_bytes(gasometer, 1)?[0], + encoded_junction.read_raw_bytes(1)?[0], )), 5 => { // In the case of Junction::GeneralIndex, we need 16 additional bytes let mut general_index: [u8; 16] = Default::default(); - general_index.copy_from_slice(&encoded_junction.read_raw_bytes(gasometer, 16)?); + general_index.copy_from_slice(&encoded_junction.read_raw_bytes(16)?); Ok(Junction::GeneralIndex(u128::from_be_bytes(general_index))) } 6 => Ok(Junction::GeneralKey( - encoded_junction.read_till_end(gasometer)?.to_vec(), + encoded_junction.read_till_end()?.to_vec(), )), 7 => Ok(Junction::OnlyChild), - _ => Err(gasometer.revert("No selector for this")), + _ => Err(revert("No selector for this")), } } @@ -211,13 +205,13 @@ impl EvmData for Junction { } impl EvmData for Junctions { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let junctions_bytes: Vec = reader.read(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let junctions_bytes: Vec = reader.read()?; let mut junctions = Junctions::Here; for item in junctions_bytes { junctions .push(item) - .map_err(|_| gasometer.revert("overflow when reading junctions"))?; + .map_err(|_| revert("overflow when reading junctions"))?; } Ok(junctions) @@ -234,8 +228,8 @@ impl EvmData for Junctions { } impl EvmData for MultiLocation { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let (parents, interior) = reader.read(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let (parents, interior) = reader.read()?; Ok(MultiLocation { parents, interior }) } diff --git a/precompiles/utils/src/lib.rs b/precompiles/utils/src/lib.rs index a9683a19f3e..638dc72edd9 100644 --- a/precompiles/utils/src/lib.rs +++ b/precompiles/utils/src/lib.rs @@ -15,11 +15,15 @@ // along with Moonbeam. If not, see . #![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] extern crate alloc; use crate::alloc::borrow::ToOwned; -use fp_evm::{Context, ExitError, ExitRevert, PrecompileFailure}; +use fp_evm::{ + Context, ExitError, ExitReason, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, + PrecompileOutput, Transfer, +}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, traits::Get, @@ -33,6 +37,8 @@ mod data; pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; pub use precompile_utils_macro::{generate_function_selector, keccak256}; +pub mod testing; + #[cfg(test)] mod tests; @@ -53,104 +59,93 @@ pub fn error>>(text: T) -> PrecompileFa #[derive(Clone, Debug)] pub struct LogsBuilder { address: H160, - logs: Vec, } impl LogsBuilder { /// Create a new builder with no logs. /// Takes the address of the precompile (usually `context.address`). pub fn new(address: H160) -> Self { - Self { - logs: vec![], - address, - } + Self { address } } - /// Returns the logs array. - pub fn build(self) -> Vec { - self.logs - } - - /// Add a 0-topic log. - pub fn log0(mut self, data: D) -> Self - where - D: Into>, - { - self.logs.push(Log { + /// Create a 0-topic log. + #[must_use] + pub fn log0(&self, data: impl Into>) -> Log { + Log { address: self.address, - data: data.into(), topics: vec![], - }); - self + data: data.into(), + } } - /// Add a 1-topic log. - pub fn log1(mut self, topic0: T0, data: D) -> Self - where - D: Into>, - T0: Into, - { - self.logs.push(Log { + /// Create a 1-topic log. + #[must_use] + pub fn log1(&self, topic0: impl Into, data: impl Into>) -> Log { + Log { address: self.address, - data: data.into(), topics: vec![topic0.into()], - }); - self + data: data.into(), + } } - /// Add a 2-topics log. - pub fn log2(mut self, topic0: T0, topic1: T1, data: D) -> Self - where - D: Into>, - T0: Into, - T1: Into, - { - self.logs.push(Log { + /// Create a 2-topics log. + #[must_use] + pub fn log2( + &self, + topic0: impl Into, + topic1: impl Into, + data: impl Into>, + ) -> Log { + Log { address: self.address, - data: data.into(), topics: vec![topic0.into(), topic1.into()], - }); - self + data: data.into(), + } } - /// Add a 3-topics log. - pub fn log3(mut self, topic0: T0, topic1: T1, topic2: T2, data: D) -> Self - where - D: Into>, - T0: Into, - T1: Into, - T2: Into, - { - self.logs.push(Log { + /// Create a 3-topics log. + #[must_use] + pub fn log3( + &self, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + data: impl Into>, + ) -> Log { + Log { address: self.address, - data: data.into(), topics: vec![topic0.into(), topic1.into(), topic2.into()], - }); - self + data: data.into(), + } } - /// Add a 4-topics log. - pub fn log4( - mut self, - topic0: T0, - topic1: T1, - topic2: T2, - topic3: T3, - data: D, - ) -> Self - where - D: Into>, - T0: Into, - T1: Into, - T2: Into, - T3: Into, - { - self.logs.push(Log { + /// Create a 4-topics log. + #[must_use] + pub fn log4( + &self, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + topic3: impl Into, + data: impl Into>, + ) -> Log { + Log { address: self.address, - data: data.into(), topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()], - }); - self + data: data.into(), + } + } +} + +/// Extension trait allowing to record logs into a PrecompileHandle. +pub trait LogExt { + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult; +} + +impl LogExt for Log { + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult { + handle.log(self.address, self.topics, self.data)?; + Ok(()) } } @@ -168,9 +163,9 @@ where /// Return an error if there are not enough gas, or if the call fails. /// If successful returns the used gas using the Runtime GasWeightMapping. pub fn try_dispatch( + handle: &mut impl PrecompileHandleExt, origin: ::Origin, call: Call, - gasometer: &mut Gasometer, ) -> EvmResult<()> where Runtime::Call: From, @@ -179,13 +174,12 @@ where let dispatch_info = call.get_dispatch_info(); // Make sure there is enough gas. - if let Some(gas_limit) = gasometer.remaining_gas()? { - let required_gas = Runtime::GasWeightMapping::weight_to_gas(dispatch_info.weight); - if required_gas > gas_limit { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } + let remaining_gas = handle.remaining_gas(); + let required_gas = Runtime::GasWeightMapping::weight_to_gas(dispatch_info.weight); + if required_gas > remaining_gas { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); } // Dispatch call. @@ -195,15 +189,13 @@ where // computations. let used_weight = call .dispatch(origin) - .map_err(|e| { - gasometer.revert(alloc::format!("Dispatched call failed with error: {:?}", e)) - })? + .map_err(|e| revert(alloc::format!("Dispatched call failed with error: {:?}", e)))? .actual_weight; let used_gas = Runtime::GasWeightMapping::weight_to_gas(used_weight.unwrap_or(dispatch_info.weight)); - gasometer.record_cost(used_gas)?; + handle.record_cost(used_gas)?; Ok(()) } @@ -240,55 +232,37 @@ pub enum FunctionModifier { Payable, } -/// Custom Gasometer to record costs in precompiles. -/// It is advised to record known costs as early as possible to -/// avoid unnecessary computations if there is an Out of Gas. -/// -/// Provides functions related to reverts, as reverts takes the recorded amount -/// of gas into account. -#[derive(Clone, Copy, Debug)] -pub struct Gasometer { - target_gas: Option, - used_gas: u64, -} +pub trait PrecompileHandleExt: PrecompileHandle { + /// Record cost of a log manually. + /// This can be useful to record log costs early when their content have static size. + #[must_use] + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult; -impl Gasometer { - /// Create a new Gasometer with provided gas limit. - /// None is no limit. - pub fn new(target_gas: Option) -> Self { - Self { - target_gas, - used_gas: 0, - } - } + /// Record cost of logs. + #[must_use] + fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult; - /// Get used gas. - pub fn used_gas(&self) -> u64 { - self.used_gas - } + #[must_use] + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult; - /// Record cost, and return error if it goes out of gas. #[must_use] - pub fn record_cost(&mut self, cost: u64) -> EvmResult { - self.used_gas = self - .used_gas - .checked_add(cost) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - })?; + /// Read the selector from the input data. + fn read_selector(&self) -> EvmResult + where + T: num_enum::TryFromPrimitive; - match self.target_gas { - Some(gas_limit) if self.used_gas > gas_limit => Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }), - _ => Ok(()), - } - } + #[must_use] + /// Returns a reader of the input, skipping the selector. + fn read_input(&self) -> EvmResult; +} - /// Record cost of a log manually. +impl PrecompileHandleExt for T { + /// Record cost of a log manualy. /// This can be useful to record log costs early when their content have static size. #[must_use] - pub fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { // Cost calculation is copied from EVM code that is not publicly exposed by the crates. // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 @@ -317,7 +291,7 @@ impl Gasometer { /// Record cost of logs. #[must_use] - pub fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult { + fn record_log_costs(&mut self, logs: &[Log]) -> EvmResult { for log in logs { self.record_log_costs_manual(log.topics.len(), log.data.len())?; } @@ -325,54 +299,60 @@ impl Gasometer { Ok(()) } - /// Compute remaining gas. - /// Returns error if out of gas. - /// Returns None if no gas limit. #[must_use] - pub fn remaining_gas(&self) -> EvmResult> { - Ok(match self.target_gas { - None => None, - Some(gas_limit) => Some(gas_limit.checked_sub(self.used_gas).ok_or( - PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }, - )?), - }) + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult { + check_function_modifier(self.context(), self.is_static(), modifier) } - /// Revert the execution, making the user pay for the the currently - /// recorded cost. It is better to **revert** instead of **error** as - /// erroring consumes the entire gas limit, and **revert** returns an error - /// message to the calling contract. - /// - /// TODO : Record cost of the input based on its size and handle Out of Gas ? - /// This might be required if we format revert messages using user data. #[must_use] - pub fn revert(&self, output: impl AsRef<[u8]>) -> PrecompileFailure { - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: output.as_ref().to_owned(), - cost: self.used_gas, - } + /// Read the selector from the input data. + fn read_selector(&self) -> EvmResult + where + S: num_enum::TryFromPrimitive, + { + EvmDataReader::read_selector(self.input()) } #[must_use] - /// Check that a function call is compatible with the context it is - /// called into. - pub fn check_function_modifier( - &self, - context: &Context, - is_static: bool, - modifier: FunctionModifier, - ) -> EvmResult { - if is_static && modifier != FunctionModifier::View { - return Err(self.revert("can't call non-static function in static context")); - } + /// Returns a reader of the input, skipping the selector. + fn read_input(&self) -> EvmResult { + EvmDataReader::new_skip_selector(self.input()) + } +} - if modifier != FunctionModifier::Payable && context.apparent_value > U256::zero() { - return Err(self.revert("function is not payable")); - } +#[must_use] +pub fn revert(output: impl AsRef<[u8]>) -> PrecompileFailure { + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: output.as_ref().to_owned(), + } +} - Ok(()) +#[must_use] +pub fn succeed(output: impl AsRef<[u8]>) -> PrecompileOutput { + PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: output.as_ref().to_owned(), } } + +#[must_use] +/// Check that a function call is compatible with the context it is +/// called into. +fn check_function_modifier( + context: &Context, + is_static: bool, + modifier: FunctionModifier, +) -> EvmResult { + if is_static && modifier != FunctionModifier::View { + return Err(revert("can't call non-static function in static context")); + } + + if modifier != FunctionModifier::Payable && context.apparent_value > U256::zero() { + return Err(revert("function is not payable")); + } + + Ok(()) +} diff --git a/precompiles/utils/src/testing.rs b/precompiles/utils/src/testing.rs new file mode 100644 index 00000000000..5ec45287286 --- /dev/null +++ b/precompiles/utils/src/testing.rs @@ -0,0 +1,354 @@ +// Copyright 2019-2022 PureStake Inc. +// This file is part of Moonbeam. + +// Moonbeam is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Moonbeam is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Moonbeam. If not, see . + +use super::*; +use core::assert_matches::assert_matches; +use fp_evm::{ExitSucceed, PrecompileOutput, PrecompileResult, PrecompileSet}; +use sp_std::boxed::Box; + +pub struct Subcall { + pub address: H160, + pub transfer: Option, + pub input: Vec, + pub target_gas: Option, + pub is_static: bool, + pub context: Context, +} + +pub struct SubcallOutput { + pub reason: ExitReason, + pub output: Vec, + pub cost: u64, + pub logs: Vec, +} + +pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {} + +impl SubcallOutput + 'static> SubcallTrait for T {} + +pub type SubcallHandle = Box; + +/// Mock handle to write tests for precompiles. +pub struct MockHandle { + pub gas_limit: u64, + pub gas_used: u64, + pub logs: Vec, + pub subcall_handle: Option, + pub code_address: H160, + pub input: Vec, + pub context: Context, + pub is_static: bool, +} + +impl MockHandle { + pub fn new(code_address: H160, context: Context) -> Self { + Self { + gas_limit: u64::MAX, + gas_used: 0, + logs: vec![], + subcall_handle: None, + code_address, + input: Vec::new(), + context, + is_static: false, + } + } + + // pub fn with_gas_limit(gas_limit: u64) -> Self { + // Self { + // gas_limit, + // gas_used: 0, + // logs: vec![], + // subcall_handle: None, + // } + // } + + // pub fn with_subcall_handle(mut self, handle: Option) -> Self { + // self.subcall_handle = handle; + // self + // } +} + +impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec) { + match &mut self.subcall_handle { + Some(handle) => { + let SubcallOutput { + reason, + output, + cost, + logs, + } = handle(Subcall { + address, + transfer, + input, + target_gas, + is_static, + context: context.clone(), + }); + + if self.record_cost(cost).is_err() { + return (ExitReason::Error(ExitError::OutOfGas), vec![]); + } + + for log in logs { + self.log(log.address, log.topics, log.data) + .expect("cannot fail"); + } + + (reason, output) + } + None => panic!("no subcall handle registered"), + } + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + + if self.gas_used > self.gas_limit { + Err(ExitError::OutOfGas) + } else { + Ok(()) + } + } + + fn remaining_gas(&self) -> u64 { + self.gas_limit - self.gas_used + } + + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + self.logs.push(Log { + address, + topics, + data, + }); + Ok(()) + } + + /// Retreive the code address (what is the address of the precompile being called). + fn code_address(&self) -> H160 { + self.code_address + } + + /// Retreive the input data the precompile is called with. + fn input(&self) -> &[u8] { + &self.input + } + + /// Retreive the context in which the precompile is executed. + fn context(&self) -> &Context { + &self.context + } + + /// Is the precompile call is done statically. + fn is_static(&self) -> bool { + self.is_static + } + + /// Retreive the gas limit of this call. + fn gas_limit(&self) -> Option { + Some(self.gas_limit) + } +} + +pub struct PrecompilesTester<'p, P> { + precompiles: &'p P, + handle: MockHandle, + + target_gas: Option, + subcall_handle: Option, + + expected_cost: Option, + expected_logs: Option>, +} + +impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { + pub fn new( + precompiles: &'p P, + from: impl Into, + to: impl Into, + data: Vec, + ) -> Self { + let to = to.into(); + let mut handle = MockHandle::new( + to.clone(), + Context { + address: to, + caller: from.into(), + apparent_value: U256::zero(), + }, + ); + + handle.input = data; + + Self { + precompiles, + handle, + + target_gas: None, + subcall_handle: None, + + expected_cost: None, + expected_logs: None, + } + } + + pub fn with_value(mut self, value: impl Into) -> Self { + self.handle.context.apparent_value = value.into(); + self + } + + pub fn with_subcall_handle(mut self, subcall_handle: impl SubcallTrait) -> Self { + self.subcall_handle = Some(Box::new(subcall_handle)); + self + } + + pub fn with_target_gas(mut self, target_gas: Option) -> Self { + self.target_gas = target_gas; + self + } + + pub fn expect_cost(mut self, cost: u64) -> Self { + self.expected_cost = Some(cost); + self + } + + pub fn expect_no_logs(mut self) -> Self { + self.expected_logs = Some(vec![]); + self + } + + pub fn expect_log(mut self, log: Log) -> Self { + self.expected_logs = Some({ + let mut logs = self.expected_logs.unwrap_or_else(Vec::new); + logs.push(log); + logs + }); + self + } + + fn assert_optionals(&self) { + if let Some(cost) = &self.expected_cost { + assert_eq!(&self.handle.gas_used, cost); + } + + if let Some(logs) = &self.expected_logs { + assert_eq!(&self.handle.logs, logs); + } + } + + fn execute(&mut self) -> Option { + let handle = &mut self.handle; + handle.subcall_handle = self.subcall_handle.take(); + + if let Some(gas_limit) = self.target_gas { + handle.gas_limit = gas_limit; + } + + let res = self.precompiles.execute( + handle, + // self.to, + // &self.data, + // self.target_gas, + // &self.context, + // self.is_static, + ); + + self.subcall_handle = handle.subcall_handle.take(); + + res + } + + /// Execute the precompile set and expect some precompile to have been executed, regardless of the + /// result. + pub fn execute_some(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and expect no precompile to have been executed. + pub fn execute_none(mut self) { + let res = self.execute(); + assert!(res.is_some()); + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_returns(mut self, output: Vec) { + let res = self.execute(); + assert_eq!( + res, + Some(Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output + })) + ); + self.assert_optionals(); + } + + /// Execute the precompile set and check if it reverts. + /// Take a closure allowing to perform custom matching on the output. + pub fn execute_reverts(mut self, check: impl Fn(&[u8]) -> bool) { + let res = self.execute(); + assert_matches!( + res, + Some(Err(PrecompileFailure::Revert { output, ..})) + if check(&output) + ); + self.assert_optionals(); + } + + /// Execute the precompile set and check it returns provided output. + pub fn execute_error(mut self, error: ExitError) { + let res = self.execute(); + assert_eq!( + res, + Some(Err(PrecompileFailure::Error { exit_status: error })) + ); + self.assert_optionals(); + } +} + +pub trait PrecompileTesterExt: PrecompileSet + Sized { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: Vec, + ) -> PrecompilesTester; +} + +impl PrecompileTesterExt for T { + fn prepare_test( + &self, + from: impl Into, + to: impl Into, + data: Vec, + ) -> PrecompilesTester { + PrecompilesTester::new(self, from, to, data) + } +} diff --git a/precompiles/utils/src/tests.rs b/precompiles/utils/src/tests.rs index ec735e5d717..92a81d6726d 100644 --- a/precompiles/utils/src/tests.rs +++ b/precompiles/utils/src/tests.rs @@ -20,6 +20,7 @@ use hex_literal::hex; use sp_core::{H256, U256}; use sp_std::convert::TryInto; use xcm::latest::{Junction, Junctions, NetworkId}; + fn u256_repeat_byte(byte: u8) -> U256 { let value = H256::repeat_byte(byte); @@ -53,11 +54,8 @@ fn read_bool() { let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: bool = reader - .read(&mut gasometer) - .expect("to correctly parse bool"); + let parsed: bool = reader.read().expect("to correctly parse bool"); assert_eq!(value, parsed); } @@ -79,9 +77,8 @@ fn read_u64() { let value = 42u64; let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: u64 = reader.read(&mut gasometer).expect("to correctly parse u64"); + let parsed: u64 = reader.read().expect("to correctly parse u64"); assert_eq!(value, parsed); } @@ -103,11 +100,8 @@ fn read_u128() { let value = 42u128; let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: u128 = reader - .read(&mut gasometer) - .expect("to correctly parse u128"); + let parsed: u128 = reader.read().expect("to correctly parse u128"); assert_eq!(value, parsed); } @@ -129,11 +123,8 @@ fn read_u256() { let value = U256::from(42); let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: U256 = reader - .read(&mut gasometer) - .expect("to correctly parse U256"); + let parsed: U256 = reader.read().expect("to correctly parse U256"); assert_eq!(value, parsed); } @@ -150,10 +141,9 @@ fn read_selector() { let selector = &Keccak256::digest(b"action1()")[0..4]; - let mut gasometer = Gasometer::new(None); - let (_, parsed_selector) = - EvmDataReader::new_with_selector::(&mut gasometer, selector) - .expect("there is a selector"); + let parsed_selector = + EvmDataReader::read_selector::(selector).expect("there is a selector"); + EvmDataReader::new_skip_selector(selector).expect("there is a selector"); assert_eq!(parsed_selector, FakeAction::Action1) } @@ -164,11 +154,8 @@ fn read_u256_too_short() { let value = U256::from(42); let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output[0..31]); - let _: U256 = reader - .read(&mut gasometer) - .expect("to correctly parse U256"); + let _: U256 = reader.read().expect("to correctly parse U256"); } #[test] @@ -200,11 +187,8 @@ fn read_h256() { let value = H256::from(raw); let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: H256 = reader - .read(&mut gasometer) - .expect("to correctly parse H256"); + let parsed: H256 = reader.read().expect("to correctly parse H256"); assert_eq!(value, parsed); } @@ -219,11 +203,8 @@ fn read_h256_too_short() { let value = H256::from(raw); let writer_output = EvmDataWriter::new().write(value).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output[0..31]); - let _: H256 = reader - .read(&mut gasometer) - .expect("to correctly parse H256"); + let _: H256 = reader.read().expect("to correctly parse H256"); } #[test] @@ -241,11 +222,8 @@ fn read_address() { let value = H160::repeat_byte(0xAA); let writer_output = EvmDataWriter::new().write(Address(value)).build(); - let mut gasometer = Gasometer::new(None); let mut reader = EvmDataReader::new(&writer_output); - let parsed: Address = reader - .read(&mut gasometer) - .expect("to correctly parse Address"); + let parsed: Address = reader.read().expect("to correctly parse Address"); assert_eq!(value, parsed.0); } @@ -263,17 +241,15 @@ fn write_h256_array() { assert_eq!(writer_output.len(), 0xE0); // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut gm = Gasometer::new(None); - let gm = &mut gm; let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); - assert_eq!(reader.read::(gm).expect("read size"), 5.into()); - assert_eq!(reader.read::(gm).expect("read 1st"), array[0]); - assert_eq!(reader.read::(gm).expect("read 2nd"), array[1]); - assert_eq!(reader.read::(gm).expect("read 3rd"), array[2]); - assert_eq!(reader.read::(gm).expect("read 4th"), array[3]); - assert_eq!(reader.read::(gm).expect("read 5th"), array[4]); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); } #[test] @@ -288,10 +264,7 @@ fn read_h256_array() { let writer_output = EvmDataWriter::new().write(array.clone()).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec = reader - .read(&mut gasometer) - .expect("to correctly parse Vec"); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); assert_eq!(array, parsed); } @@ -309,17 +282,15 @@ fn write_u256_array() { assert_eq!(writer_output.len(), 0xE0); // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut gm = Gasometer::new(None); - let gm = &mut gm; let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); - assert_eq!(reader.read::(gm).expect("read size"), 5.into()); - assert_eq!(reader.read::(gm).expect("read 1st"), array[0]); - assert_eq!(reader.read::(gm).expect("read 2nd"), array[1]); - assert_eq!(reader.read::(gm).expect("read 3rd"), array[2]); - assert_eq!(reader.read::(gm).expect("read 4th"), array[3]); - assert_eq!(reader.read::(gm).expect("read 5th"), array[4]); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); } #[test] @@ -334,10 +305,7 @@ fn read_u256_array() { let writer_output = EvmDataWriter::new().write(array.clone()).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec = reader - .read(&mut gasometer) - .expect("to correctly parse Vec"); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); assert_eq!(array, parsed); } @@ -355,16 +323,14 @@ fn write_address_array() { // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); - assert_eq!(reader.read::(gm).expect("read size"), 5.into()); - assert_eq!(reader.read::
(gm).expect("read 1st"), array[0]); - assert_eq!(reader.read::
(gm).expect("read 2nd"), array[1]); - assert_eq!(reader.read::
(gm).expect("read 3rd"), array[2]); - assert_eq!(reader.read::
(gm).expect("read 4th"), array[3]); - assert_eq!(reader.read::
(gm).expect("read 5th"), array[4]); + + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::
().expect("read 1st"), array[0]); + assert_eq!(reader.read::
().expect("read 2nd"), array[1]); + assert_eq!(reader.read::
().expect("read 3rd"), array[2]); + assert_eq!(reader.read::
().expect("read 4th"), array[3]); + assert_eq!(reader.read::
().expect("read 5th"), array[4]); } #[test] @@ -379,10 +345,7 @@ fn read_address_array() { let writer_output = EvmDataWriter::new().write(array.clone()).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec
= reader - .read(&mut gasometer) - .expect("to correctly parse Vec"); + let parsed: Vec
= reader.read().expect("to correctly parse Vec"); assert_eq!(array, parsed); } @@ -401,9 +364,8 @@ fn read_address_array_size_too_big() { U256::from(6u32).to_big_endian(&mut writer_output[0x20..0x40]); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - match reader.read::>(&mut gasometer) { + match reader.read::>() { Ok(_) => panic!("should not parse correctly"), Err(PrecompileFailure::Revert { output: err, .. }) => { assert_eq!(err, b"tried to parse H160 out of bounds") @@ -430,26 +392,18 @@ fn write_address_nested_array() { // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - assert_eq!(reader.read::(gm).expect("read offset"), 0x20.into()); // 0x00 - assert_eq!(reader.read::(gm).expect("read size"), 2.into()); // 0x20 - assert_eq!( - reader.read::(gm).expect("read 1st offset"), - 0x40.into() - ); // 0x40 - assert_eq!( - reader.read::(gm).expect("read 2st offset"), - 0xc0.into() - ); // 0x60 - assert_eq!(reader.read::(gm).expect("read 1st size"), 3.into()); // 0x80 - assert_eq!(reader.read::
(gm).expect("read 1-1"), array[0][0]); // 0xA0 - assert_eq!(reader.read::
(gm).expect("read 1-2"), array[0][1]); // 0xC0 - assert_eq!(reader.read::
(gm).expect("read 1-3"), array[0][2]); // 0xE0 - assert_eq!(reader.read::(gm).expect("read 2nd size"), 2.into()); // 0x100 - assert_eq!(reader.read::
(gm).expect("read 2-1"), array[1][0]); // 0x120 - assert_eq!(reader.read::
(gm).expect("read 2-2"), array[1][1]); // 0x140 + assert_eq!(reader.read::().expect("read offset"), 0x20.into()); // 0x00 + assert_eq!(reader.read::().expect("read size"), 2.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x40 + assert_eq!(reader.read::().expect("read 2st offset"), 0xc0.into()); // 0x60 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x80 + assert_eq!(reader.read::
().expect("read 1-1"), array[0][0]); // 0xA0 + assert_eq!(reader.read::
().expect("read 1-2"), array[0][1]); // 0xC0 + assert_eq!(reader.read::
().expect("read 1-3"), array[0][2]); // 0xE0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0x100 + assert_eq!(reader.read::
().expect("read 2-1"), array[1][0]); // 0x120 + assert_eq!(reader.read::
().expect("read 2-2"), array[1][1]); // 0x140 } #[test] @@ -468,10 +422,7 @@ fn read_address_nested_array() { let writer_output = EvmDataWriter::new().write(array.clone()).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec> = reader - .read(&mut gasometer) - .expect("to correctly parse Vec>"); + let parsed: Vec> = reader.read().expect("to correctly parse Vec>"); assert_eq!(array, parsed); } @@ -496,24 +447,16 @@ fn write_multiple_arrays() { // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - assert_eq!( - reader.read::(gm).expect("read 1st offset"), - 0x40.into() - ); // 0x00 - assert_eq!( - reader.read::(gm).expect("read 2nd offset"), - 0xc0.into() - ); // 0x20 - assert_eq!(reader.read::(gm).expect("read 1st size"), 3.into()); // 0x40 - assert_eq!(reader.read::
(gm).expect("read 1-1"), array1[0]); // 0x60 - assert_eq!(reader.read::
(gm).expect("read 1-2"), array1[1]); // 0x80 - assert_eq!(reader.read::
(gm).expect("read 1-3"), array1[2]); // 0xA0 - assert_eq!(reader.read::(gm).expect("read 2nd size"), 2.into()); // 0xC0 - assert_eq!(reader.read::(gm).expect("read 2-1"), array2[0]); // 0xE0 - assert_eq!(reader.read::(gm).expect("read 2-2"), array2[1]); // 0x100 + assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x00 + assert_eq!(reader.read::().expect("read 2nd offset"), 0xc0.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x40 + assert_eq!(reader.read::
().expect("read 1-1"), array1[0]); // 0x60 + assert_eq!(reader.read::
().expect("read 1-2"), array1[1]); // 0x80 + assert_eq!(reader.read::
().expect("read 1-3"), array1[2]); // 0xA0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0xC0 + assert_eq!(reader.read::().expect("read 2-1"), array2[0]); // 0xE0 + assert_eq!(reader.read::().expect("read 2-2"), array2[1]); // 0x100 } #[test] @@ -540,16 +483,11 @@ fn read_multiple_arrays() { assert_eq!(writer_output.len(), 0x120); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec
= reader - .read(&mut gasometer) - .expect("to correctly parse Vec
"); + let parsed: Vec
= reader.read().expect("to correctly parse Vec
"); assert_eq!(array1, parsed); - let parsed: Vec = reader - .read(&mut gasometer) - .expect("to correctly parse Vec"); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); assert_eq!(array2, parsed); } @@ -560,10 +498,7 @@ fn read_bytes() { let writer_output = EvmDataWriter::new().write(Bytes::from(&data[..])).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Bytes = reader - .read(&mut gasometer) - .expect("to correctly parse Bytes"); + let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); assert_eq!(data, parsed.as_bytes()); } @@ -577,20 +512,15 @@ fn write_bytes() { // We can read this "manualy" using simpler functions. let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; // We pad data to a multiple of 32 bytes. let mut padded = data.to_vec(); assert!(data.len() < 0x80); padded.resize(0x80, 0); - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); - assert_eq!( - reader.read::(gm).expect("read size"), - data.len().into() - ); - let mut read = |e| reader.read::(gm).expect(e); // shorthand + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), data.len().into()); + let mut read = |e| reader.read::().expect(e); // shorthand assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); @@ -604,10 +534,7 @@ fn read_string() { let writer_output = EvmDataWriter::new().write(Bytes::from(data)).build(); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Bytes = reader - .read(&mut gasometer) - .expect("to correctly parse Bytes"); + let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); assert_eq!(data, parsed.as_str().expect("valid utf8")); } @@ -621,20 +548,15 @@ fn write_string() { // We can read this "manualy" using simpler functions. let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; // We pad data to next multiple of 32 bytes. let mut padded = data.as_bytes().to_vec(); assert!(data.len() < 0x80); padded.resize(0x80, 0); - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); - assert_eq!( - reader.read::(gm).expect("read size"), - data.len().into() - ); - let mut read = |e| reader.read::(gm).expect(e); // shorthand + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), data.len().into()); + let mut read = |e| reader.read::().expect(e); // shorthand assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); @@ -661,41 +583,33 @@ fn write_vec_bytes() { padded.resize(0x80, 0); let mut reader = EvmDataReader::new(&writer_output); - let mut gm = Gasometer::new(None); - let gm = &mut gm; // Offset of vec - assert_eq!(reader.read::(gm).expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read offset"), 32.into()); // Length of vec - assert_eq!(reader.read::(gm).expect("read offset"), 2.into()); + assert_eq!(reader.read::().expect("read offset"), 2.into()); // Relative offset of first bytgmes object - assert_eq!(reader.read::(gm).expect("read offset"), 0x40.into()); + assert_eq!(reader.read::().expect("read offset"), 0x40.into()); // Relative offset of second bytes object - assert_eq!(reader.read::(gm).expect("read offset"), 0xe0.into()); + assert_eq!(reader.read::().expect("read offset"), 0xe0.into()); // Length of first bytes object - assert_eq!( - reader.read::(gm).expect("read size"), - data.len().into() - ); + assert_eq!(reader.read::().expect("read size"), data.len().into()); // First byte objects data - let mut read = |e| reader.read::(gm).expect(e); // shorthand + let mut read = |e| reader.read::().expect(e); // shorthand assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); // Length of second bytes object - assert_eq!( - reader.read::(gm).expect("read size"), - data.len().into() - ); + assert_eq!(reader.read::().expect("read size"), data.len().into()); // Second byte objects data - let mut read = |e| reader.read::(gm).expect(e); // shorthand + let mut read = |e| reader.read::().expect(e); // shorthand assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); @@ -717,10 +631,7 @@ fn read_vec_of_bytes() { .for_each(|hash| println!("{:?}", hash)); let mut reader = EvmDataReader::new(&writer_output); - let mut gasometer = Gasometer::new(None); - let parsed: Vec = reader - .read(&mut gasometer) - .expect("to correctly parse Vec"); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); assert_eq!(vec![Bytes::from(&data[..]), Bytes::from(&data[..])], parsed); } @@ -750,8 +661,8 @@ struct MultiLocation { } impl EvmData for MultiLocation { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let (parents, interior) = reader.read(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let (parents, interior) = reader.read()?; Ok(MultiLocation { parents, interior }) } @@ -797,16 +708,13 @@ fn read_complex_solidity_function() { 0100000000000000000000000000000000000000000000000000000000000000" ); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - - let (mut reader, selector) = - EvmDataReader::new_with_selector::(gm, &data).expect("to read selector"); + let selector = EvmDataReader::read_selector::(&data).expect("to read selector"); + let mut reader = EvmDataReader::new_skip_selector(&data).expect("to read selector"); assert_eq!(selector, Action::TransferMultiAsset); // asset assert_eq!( - reader.read::(gm).unwrap(), + reader.read::().unwrap(), MultiLocation { parents: 1, interior: vec![ @@ -817,11 +725,11 @@ fn read_complex_solidity_function() { ); // amount - assert_eq!(reader.read::(gm).unwrap(), 100u32.into()); + assert_eq!(reader.read::().unwrap(), 100u32.into()); // destination assert_eq!( - reader.read::(gm).unwrap(), + reader.read::().unwrap(), MultiLocation { parents: 1, interior: vec![Bytes::from( @@ -831,21 +739,18 @@ fn read_complex_solidity_function() { ); // weight - assert_eq!(reader.read::(gm).unwrap(), 100u32.into()); + assert_eq!(reader.read::().unwrap(), 100u32.into()); } #[test] fn junctions_decoder_works() { - let mut gm = Gasometer::new(None); - let gm = &mut gm; - let writer_output = EvmDataWriter::new() .write(Junctions::X1(Junction::OnlyChild)) .build(); let mut reader = EvmDataReader::new(&writer_output); let parsed: Junctions = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!(parsed, Junctions::X1(Junction::OnlyChild)); @@ -856,7 +761,7 @@ fn junctions_decoder_works() { let mut reader = EvmDataReader::new(&writer_output); let parsed: Junctions = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!( @@ -874,7 +779,7 @@ fn junctions_decoder_works() { let mut reader = EvmDataReader::new(&writer_output); let parsed: Junctions = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!( @@ -889,14 +794,11 @@ fn junctions_decoder_works() { #[test] fn junction_decoder_works() { - let mut gm = Gasometer::new(None); - let gm = &mut gm; - let writer_output = EvmDataWriter::new().write(Junction::Parachain(0)).build(); let mut reader = EvmDataReader::new(&writer_output); let parsed: Junction = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!(parsed, Junction::Parachain(0)); @@ -910,7 +812,7 @@ fn junction_decoder_works() { let mut reader = EvmDataReader::new(&writer_output); let parsed: Junction = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!( @@ -930,7 +832,7 @@ fn junction_decoder_works() { let mut reader = EvmDataReader::new(&writer_output); let parsed: Junction = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!( @@ -950,7 +852,7 @@ fn junction_decoder_works() { let mut reader = EvmDataReader::new(&writer_output); let parsed: Junction = reader - .read::(gm) + .read::() .expect("to correctly parse Junctions"); assert_eq!( @@ -964,100 +866,92 @@ fn junction_decoder_works() { #[test] fn network_id_decoder_works() { - let mut gm = Gasometer::new(None); - let gm = &mut gm; assert_eq!( - network_id_from_bytes(gm, network_id_to_bytes(NetworkId::Any)), + network_id_from_bytes(network_id_to_bytes(NetworkId::Any)), Ok(NetworkId::Any) ); assert_eq!( - network_id_from_bytes( - gm, - network_id_to_bytes(NetworkId::Named(b"myname".to_vec())) - ), + network_id_from_bytes(network_id_to_bytes(NetworkId::Named(b"myname".to_vec()))), Ok(NetworkId::Named(b"myname".to_vec())) ); assert_eq!( - network_id_from_bytes(gm, network_id_to_bytes(NetworkId::Kusama)), + network_id_from_bytes(network_id_to_bytes(NetworkId::Kusama)), Ok(NetworkId::Kusama) ); assert_eq!( - network_id_from_bytes(gm, network_id_to_bytes(NetworkId::Polkadot)), + network_id_from_bytes(network_id_to_bytes(NetworkId::Polkadot)), Ok(NetworkId::Polkadot) ); } #[test] -fn check_function_modifier() { - let mut gasometer = Gasometer::new(None); - let _ = gasometer.record_cost(500); - +fn test_check_function_modifier() { let context = |value: u32| Context { address: H160::zero(), caller: H160::zero(), apparent_value: U256::from(value), }; - let payable_error = || gasometer.revert("function is not payable"); - let static_error = || gasometer.revert("can't call non-static function in static context"); + let payable_error = || revert("function is not payable"); + let static_error = || revert("can't call non-static function in static context"); // Can't call non-static functions in static context. assert_eq!( - gasometer.check_function_modifier(&context(0), true, FunctionModifier::Payable), + check_function_modifier(&context(0), true, FunctionModifier::Payable), Err(static_error()) ); assert_eq!( - gasometer.check_function_modifier(&context(0), true, FunctionModifier::NonPayable), + check_function_modifier(&context(0), true, FunctionModifier::NonPayable), Err(static_error()) ); assert_eq!( - gasometer.check_function_modifier(&context(0), true, FunctionModifier::View), + check_function_modifier(&context(0), true, FunctionModifier::View), Ok(()) ); // Static check is performed before non-payable check. assert_eq!( - gasometer.check_function_modifier(&context(1), true, FunctionModifier::Payable), + check_function_modifier(&context(1), true, FunctionModifier::Payable), Err(static_error()) ); assert_eq!( - gasometer.check_function_modifier(&context(1), true, FunctionModifier::NonPayable), + check_function_modifier(&context(1), true, FunctionModifier::NonPayable), Err(static_error()) ); // FunctionModifier::View pass static check but fail for payable. assert_eq!( - gasometer.check_function_modifier(&context(1), true, FunctionModifier::View), + check_function_modifier(&context(1), true, FunctionModifier::View), Err(payable_error()) ); // Can't send funds to non payable function assert_eq!( - gasometer.check_function_modifier(&context(1), false, FunctionModifier::Payable), + check_function_modifier(&context(1), false, FunctionModifier::Payable), Ok(()) ); assert_eq!( - gasometer.check_function_modifier(&context(1), false, FunctionModifier::NonPayable), + check_function_modifier(&context(1), false, FunctionModifier::NonPayable), Err(payable_error()) ); assert_eq!( - gasometer.check_function_modifier(&context(1), false, FunctionModifier::View), + check_function_modifier(&context(1), false, FunctionModifier::View), Err(payable_error()) ); // Any function can be called without funds. assert_eq!( - gasometer.check_function_modifier(&context(0), false, FunctionModifier::Payable), + check_function_modifier(&context(0), false, FunctionModifier::Payable), Ok(()) ); assert_eq!( - gasometer.check_function_modifier(&context(0), false, FunctionModifier::NonPayable), + check_function_modifier(&context(0), false, FunctionModifier::NonPayable), Ok(()) ); assert_eq!( - gasometer.check_function_modifier(&context(0), false, FunctionModifier::View), + check_function_modifier(&context(0), false, FunctionModifier::View), Ok(()) ); } @@ -1070,13 +964,10 @@ fn read_static_size_tuple() { 0000000000000000000000000000000000000000000000000000000000000001" ); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - let mut reader = EvmDataReader::new(&data); assert_eq!( - reader.read::<(Address, U256)>(gm).unwrap(), + reader.read::<(Address, U256)>().unwrap(), (Address(H160::repeat_byte(0x11)), U256::from(1u8)) ); } @@ -1094,13 +985,10 @@ fn read_dynamic_size_tuple() { 0100000000000000000000000000000000000000000000000000000000000000" ); - let mut gm = Gasometer::new(None); - let gm = &mut gm; - let mut reader = EvmDataReader::new(&data); assert_eq!( - reader.read::<(u8, Vec)>(gm).unwrap(), + reader.read::<(u8, Vec)>().unwrap(), (1, vec![Bytes(vec![0x01])]) ); } diff --git a/precompiles/xcm_transactor/Cargo.toml b/precompiles/xcm_transactor/Cargo.toml index ca88b7b7831..aab83be54db 100644 --- a/precompiles/xcm_transactor/Cargo.toml +++ b/precompiles/xcm_transactor/Cargo.toml @@ -23,7 +23,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20", default-features = false } # Frontier -evm = { version = "0.35.0", default-features = false, features = [ "with-codec" ] } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false, features = [ "with-codec" ] } fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } @@ -59,7 +59,6 @@ orml-traits = { git = "https://github.com/purestake/open-runtime-module-library" [features] default = [ "std" ] std = [ - "evm/std", "frame-support/std", "frame-system/std", "pallet-evm/std", diff --git a/precompiles/xcm_transactor/src/lib.rs b/precompiles/xcm_transactor/src/lib.rs index 527076172c0..b2034a84e41 100644 --- a/precompiles/xcm_transactor/src/lib.rs +++ b/precompiles/xcm_transactor/src/lib.rs @@ -19,14 +19,13 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(assert_matches)] -use evm::{executor::stack::PrecompileOutput, Context, ExitSucceed}; +use fp_evm::PrecompileHandle; use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; -use pallet_evm::{AddressMapping, Precompile}; +use pallet_evm::{AddressMapping, PrecompileOutput}; use precompile_utils::{ - Address, Bytes, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, - RuntimeHelper, + revert, succeed, Address, Bytes, EvmDataWriter, EvmResult, FunctionModifier, + PrecompileHandleExt, RuntimeHelper, }; - use sp_core::H160; use sp_std::{ boxed::Box, @@ -37,13 +36,13 @@ use sp_std::{ use xcm::latest::MultiLocation; use xcm_primitives::AccountIdToCurrencyId; use xcm_transactor::RemoteTransactInfoWithMaxWeight; + #[cfg(test)] mod mock; #[cfg(test)] mod tests; pub type TransactorOf = ::Transactor; - pub type CurrencyIdOf = ::CurrencyId; #[precompile_utils::generate_function_selector] @@ -59,7 +58,7 @@ pub enum Action { /// A precompile to wrap the functionality from xcm transactor pub struct XcmTransactorWrapper(PhantomData); -impl Precompile for XcmTransactorWrapper +impl pallet_evm::Precompile for XcmTransactorWrapper where Runtime: xcm_transactor::Config + pallet_evm::Config + frame_system::Config, Runtime::Call: Dispatchable + GetDispatchInfo, @@ -69,37 +68,24 @@ where Runtime::AccountId: Into, Runtime: AccountIdToCurrencyId>, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier( - context, - is_static, - match selector { - Action::TransactThroughDerivativeMultiLocation - | Action::TransactThroughDerivative => FunctionModifier::NonPayable, - _ => FunctionModifier::View, - }, - )?; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; + + handle.check_function_modifier(match selector { + Action::TransactThroughDerivativeMultiLocation | Action::TransactThroughDerivative => { + FunctionModifier::NonPayable + } + _ => FunctionModifier::View, + })?; match selector { // Check for accessor methods first. These return results immediately - Action::IndexToAccount => Self::account_index(input, gasometer), - Action::TransactInfo => Self::transact_info(input, gasometer), + Action::IndexToAccount => Self::account_index(handle), + Action::TransactInfo => Self::transact_info(handle), Action::TransactThroughDerivativeMultiLocation => { - Self::transact_through_derivative_multilocation(input, gasometer, context) - } - Action::TransactThroughDerivative => { - Self::transact_through_derivative(input, gasometer, context) + Self::transact_through_derivative_multilocation(handle) } + Action::TransactThroughDerivative => Self::transact_through_derivative(handle), } } } @@ -114,82 +100,71 @@ where Runtime::AccountId: Into, Runtime: AccountIdToCurrencyId>, { - fn account_index( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn account_index(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; // Bound check - input.expect_arguments(gasometer, 1)?; - let index: u16 = input.read::(gasometer)?; + let mut input = handle.read_input()?; + input.expect_arguments(1)?; + let index: u16 = input.read::()?; // fetch data from pallet let account: H160 = xcm_transactor::Pallet::::index_to_account(index) - .ok_or(gasometer.revert("No index assigned"))? + .ok_or(revert("No index assigned"))? .into(); - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new().write(Address(account)).build(), - logs: Default::default(), - }) + Ok(succeed( + EvmDataWriter::new().write(Address(account)).build(), + )) } - fn transact_info( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - ) -> EvmResult { - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + fn transact_info(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - let multilocation: MultiLocation = input.read::(gasometer)?; + let mut input = handle.read_input()?; + let multilocation: MultiLocation = input.read::()?; // fetch data from pallet let remote_transact_info: RemoteTransactInfoWithMaxWeight = xcm_transactor::Pallet::::transact_info(multilocation) - .ok_or(gasometer.revert("Transact Info not set"))?; + .ok_or(revert("Transact Info not set"))?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: EvmDataWriter::new() + Ok(succeed( + EvmDataWriter::new() .write(remote_transact_info.transact_extra_weight) .write(remote_transact_info.fee_per_second) .write(remote_transact_info.max_weight) .build(), - logs: Default::default(), - }) + )) } fn transact_through_derivative_multilocation( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 5)?; + input.expect_arguments(5)?; // Does not need DB read let transactor: TransactorOf = input - .read::(gasometer)? + .read::()? .try_into() - .map_err(|_| gasometer.revert("Non-existent transactor"))?; - let index: u16 = input.read::(gasometer)?; + .map_err(|_| revert("Non-existent transactor"))?; + let index: u16 = input.read::()?; // read fee location // defined as a multiLocation. For now we are assuming these are concrete // fungible assets - let fee_multilocation: MultiLocation = input.read::(gasometer)?; + let fee_multilocation: MultiLocation = input.read::()?; // read fee amount - let weight: u64 = input.read::(gasometer)?; + let weight: u64 = input.read::()?; // inner call - let inner_call = input.read::(gasometer)?; + let inner_call = input.read::()?; // Depending on the Runtime, this might involve a DB read. This is not the case in // moonbeam, as we are using IdentityMapping - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = xcm_transactor::Call::::transact_through_derivative_multilocation { dest: transactor, index, @@ -198,50 +173,44 @@ where inner_call: inner_call.0, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } fn transact_through_derivative( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { + let mut input = handle.read_input()?; // Bound check - input.expect_arguments(gasometer, 5)?; + input.expect_arguments(5)?; let transactor: TransactorOf = input - .read::(gasometer)? + .read::()? .try_into() - .map_err(|_| gasometer.revert("Non-existent transactor"))?; - let index: u16 = input.read::(gasometer)?; + .map_err(|_| revert("Non-existent transactor"))?; + let index: u16 = input.read::()?; // read currencyId - let to_address: H160 = input.read::
(gasometer)?.into(); + let to_address: H160 = input.read::
()?.into(); + + // read fee amount + let weight: u64 = input.read::()?; + + // inner call + let inner_call = input.read::()?; let to_account = Runtime::AddressMapping::into_account_id(to_address); // We convert the address into a currency // This involves a DB read in moonbeam, hence the db Read - gasometer.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; let currency_id: ::CurrencyId = Runtime::account_to_currency_id(to_account) - .ok_or(gasometer.revert("cannot convert into currency id"))?; - - // read fee amount - let weight: u64 = input.read::(gasometer)?; - - // inner call - let inner_call = input.read::(gasometer)?; + .ok_or(revert("cannot convert into currency id"))?; // Depending on the Runtime, this might involve a DB read. This is not the case in // moonbeam, as we are using IdentityMapping - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let call = xcm_transactor::Call::::transact_through_derivative { dest: transactor, index, @@ -250,13 +219,8 @@ where inner_call: inner_call.0, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } } diff --git a/precompiles/xcm_transactor/src/mock.rs b/precompiles/xcm_transactor/src/mock.rs index c1d45aa771c..9dbab57bd72 100644 --- a/precompiles/xcm_transactor/src/mock.rs +++ b/precompiles/xcm_transactor/src/mock.rs @@ -18,15 +18,15 @@ use super::*; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ + construct_runtime, parameter_types, traits::{EnsureOrigin, Everything, OriginTrait, PalletInfo as PalletInfoTrait}, weights::{RuntimeDbWeight, Weight}, }; - -use frame_support::{construct_runtime, parameter_types}; - use pallet_evm::{ - AddressMapping, EnsureAddressNever, EnsureAddressRoot, GasWeightMapping, PrecompileSet, + AddressMapping, EnsureAddressNever, EnsureAddressRoot, GasWeightMapping, Precompile, + PrecompileSet, }; +use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_core::H256; use sp_io; @@ -39,10 +39,7 @@ use xcm::latest::{ Junction::{AccountKey20, GeneralIndex, PalletInstance, Parachain}, Junctions, MultiAsset, MultiLocation, NetworkId, Result as XcmResult, SendResult, SendXcm, Xcm, }; - use xcm_builder::FixedWeightBounds; - -use scale_info::TypeInfo; use xcm_executor::{ traits::{InvertLocation, TransactAsset, WeightTrader}, Assets, @@ -232,18 +229,9 @@ impl PrecompileSet for TestPrecompiles where XcmTransactorWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == precompile_address() => Some(XcmTransactorWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == precompile_address() => Some(XcmTransactorWrapper::::execute(handle)), _ => None, } } @@ -529,13 +517,3 @@ impl ExtBuilder { ext } } -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> evm::Context { - evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} diff --git a/precompiles/xcm_transactor/src/tests.rs b/precompiles/xcm_transactor/src/tests.rs index 5c859bd6a82..d966bce5c7d 100644 --- a/precompiles/xcm_transactor/src/tests.rs +++ b/precompiles/xcm_transactor/src/tests.rs @@ -14,20 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . use crate::mock::{ - evm_test_context, ExtBuilder, Origin, PrecompilesValue, Runtime, TestAccount::*, - TestPrecompiles, XcmTransactor, + ExtBuilder, Origin, PrecompilesValue, Runtime, TestAccount::*, TestPrecompiles, XcmTransactor, }; -use crate::{Action, PrecompileOutput}; +use crate::Action; -use fp_evm::PrecompileFailure; use frame_support::assert_ok; -use num_enum::TryFromPrimitive; -use pallet_evm::{ExitSucceed, PrecompileSet}; -use precompile_utils::{Address, Bytes, EvmDataWriter}; -use sha3::{Digest, Keccak256}; +use precompile_utils::{testing::*, Address, Bytes, EvmDataWriter}; use sp_core::{H160, U256}; use sp_std::boxed::Box; -use std::assert_matches::assert_matches; use xcm::v1::MultiLocation; fn precompiles() -> TestPrecompiles { @@ -36,73 +30,30 @@ fn precompiles() -> TestPrecompiles { #[test] fn test_selector_enum() { - let mut buffer = [0u8; 4]; - buffer.copy_from_slice(&Keccak256::digest(b"index_to_account(uint16)")[0..4]); + assert_eq!(Action::IndexToAccount as u32, 0x71b0edfa); + assert_eq!(Action::TransactInfo as u32, 0xf87f493f); assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::IndexToAccount, - ); - - buffer.copy_from_slice(&Keccak256::digest(b"transact_info((uint8,bytes[]))")[0..4]); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransactInfo, - ); - buffer.copy_from_slice( - &Keccak256::digest( - b"transact_through_derivative_multilocation(uint8,uint16,(uint8,bytes[]),uint64,bytes)", - )[0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransactThroughDerivativeMultiLocation, - ); - - buffer.copy_from_slice( - &Keccak256::digest(b"transact_through_derivative(uint8,uint16,address,uint64,bytes)")[0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransactThroughDerivative, + Action::TransactThroughDerivativeMultiLocation as u32, + 0x9f89f03e ); + assert_eq!(Action::TransactThroughDerivative as u32, 0x267d4062); } #[test] fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { - // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"tried to parse selector out of bounds", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - Precompile.into(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"unknown selector", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -117,29 +68,23 @@ fn take_index_for_account() { .build(); // Assert that errors since no index is assigned - assert_matches!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"No index assigned" - ); + precompiles() + .prepare_test(Alice, Precompile, input.clone()) + .execute_reverts(|output| output == b"No index assigned"); // register index assert_ok!(XcmTransactor::register(Origin::root(), Alice.into(), 0)); // Expected result is zero - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() - .write(Address(H160::from(Alice))) - .build(), - cost: 1, - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_result - ); + precompiles() + .prepare_test(Alice, Precompile, input) + .expect_cost(1) + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(Address(H160::from(Alice))) + .build(), + ); }); } @@ -154,11 +99,9 @@ fn take_transact_info() { .build(); // Assert that errors since no index is assigned - assert_matches!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"Transact Info not set" - ); + precompiles() + .prepare_test(Alice, Precompile, input.clone()) + .execute_reverts(|output| output == b"Transact Info not set"); // Root can set transact info assert_ok!(XcmTransactor::set_transact_info( @@ -169,22 +112,17 @@ fn take_transact_info() { 10000, )); - // Expected result is zero - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new() - .write(0u64) - .write(1u128) - .write(10000u64) - .build(), - cost: 1, - logs: Default::default(), - })); - - assert_eq!( - precompiles().execute(Precompile.into(), &input, None, &evm_test_context(), false), - expected_result - ); + precompiles() + .prepare_test(Alice, Precompile, input) + .expect_cost(1) + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(0u64) + .write(1u128) + .write(10000u64) + .build(), + ); }); } @@ -209,14 +147,15 @@ fn test_transactor_multilocation() { // we pay with our current self reserve. let fee_payer_asset = MultiLocation::parent(); - let bytes: Bytes = vec![1u8, 2u8, 3u8].as_slice().into(); + let bytes = Bytes(vec![1u8, 2u8, 3u8]); // We are transferring asset 0, which we have instructed to be the relay asset - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector( - Action::TransactThroughDerivativeMultiLocation + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector( + Action::TransactThroughDerivativeMultiLocation, ) .write(0u8) .write(0u16) @@ -224,21 +163,10 @@ fn test_transactor_multilocation() { .write(U256::from(4000000)) .write(bytes) .build(), - None, - &evm::Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 4004000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(4004000) + .expect_no_logs() + .execute_returns(vec![]); }); } @@ -260,33 +188,23 @@ fn test_transactor() { 10000000 )); - let bytes: Bytes = vec![1u8, 2u8, 3u8].as_slice().into(); + let bytes = Bytes(vec![1u8, 2u8, 3u8]); // We are transferring asset 0, which we have instructed to be the relay asset - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransactThroughDerivative) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransactThroughDerivative) .write(0u8) .write(0u16) .write(Address(AssetId(0).into())) .write(U256::from(4000000)) .write(bytes) .build(), - None, - &evm::Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 4004001, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(4004001) + .expect_no_logs() + .execute_returns(vec![]); }); } diff --git a/precompiles/xtokens/Xtokens.sol b/precompiles/xtokens/Xtokens.sol index bdc83346f69..93fbd4ce5b1 100644 --- a/precompiles/xtokens/Xtokens.sol +++ b/precompiles/xtokens/Xtokens.sol @@ -11,7 +11,7 @@ interface Xtokens { // A multilocation is defined by its number of parents and the encoded junctions (interior) struct Multilocation { uint8 parents; - bytes [] interior; + bytes[] interior; } // A MultiAsset is defined by a multilocation and an amount @@ -27,13 +27,14 @@ interface Xtokens { } /** Transfer a token through XCM based on its currencyId - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param currency_address The ERC20 address of the currency we want to transfer - * @param amount The amount of tokens we want to transfer - * @param destination The Multilocation to which we want to send the tokens - * @param destination The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param currency_address The ERC20 address of the currency we want to transfer + * @param amount The amount of tokens we want to transfer + * @param destination The Multilocation to which we want to send the tokens + * @param destination The weight we want to buy in the destination chain + * Selector: b9f813ff + */ function transfer( address currency_address, uint256 amount, @@ -42,13 +43,13 @@ interface Xtokens { ) external; /** Transfer a token through XCM based on its currencyId specifying fee - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param currency_address The ERC20 address of the currency we want to transfer - * @param amount The amount of tokens we want to transfer - * @param destination The Multilocation to which we want to send the tokens - * @param destination The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param currency_address The ERC20 address of the currency we want to transfer + * @param amount The amount of tokens we want to transfer + * @param destination The Multilocation to which we want to send the tokens + * @param destination The weight we want to buy in the destination chain + */ function transfer_with_fee( address currency_address, uint256 amount, @@ -58,42 +59,49 @@ interface Xtokens { ) external; /** Transfer a token through XCM based on its MultiLocation - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param asset The asset we want to transfer, defined by its multilocation. - * Currently only Concrete Fungible assets - * @param amount The amount of tokens we want to transfer - * @param destination The Multilocation to which we want to send the tokens - * @param destination The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param asset The asset we want to transfer, defined by its multilocation. + * Currently only Concrete Fungible assets + * @param amount The amount of tokens we want to transfer + * @param destination The Multilocation to which we want to send the tokens + * @param destination The weight we want to buy in the destination chain + * Selector: b38c60fa + */ function transfer_multiasset( Multilocation memory asset, uint256 amount, - Multilocation memory destination, uint64 weight) external; - + Multilocation memory destination, + uint64 weight + ) external; + /** Transfer a token through XCM based on its MultiLocation specifying fee - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param asset The asset we want to transfer, defined by its multilocation. - * Currently only Concrete Fungible assets - * @param amount The amount of tokens we want to transfer - * @param destination The Multilocation to which we want to send the tokens - * @param destination The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param asset The asset we want to transfer, defined by its multilocation. + * Currently only Concrete Fungible assets + * @param amount The amount of tokens we want to transfer + * @param destination The Multilocation to which we want to send the tokens + * @param destination The weight we want to buy in the destination chain + * Selector: 89a570fc + */ function transfer_multiasset_with_fee( Multilocation memory asset, uint256 amount, uint256 fee, - Multilocation memory destination, uint64 weight) external; + Multilocation memory destination, + uint64 weight + ) external; /** Transfer several tokens at once through XCM based on its address specifying fee - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param currencies The currencies we want to transfer, defined by their address and amount. - * @param fee_item Which of the currencies to be used as fee - * @param destination The Multilocation to which we want to send the tokens - * @param weight The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param currencies The currencies we want to transfer, defined by their address and amount. + * @param fee_item Which of the currencies to be used as fee + * @param destination The Multilocation to which we want to send the tokens + * @param weight The weight we want to buy in the destination chain + * Selector: 8a362d5c + */ function transfer_multi_currencies( Currency[] memory currencies, uint32 fee_item, @@ -102,13 +110,14 @@ interface Xtokens { ) external; /** Transfer several tokens at once through XCM based on its location specifying fee - * - * @dev The token transfer burns/transfers the corresponding amount before sending - * @param assets The assets we want to transfer, defined by their location and amount. - * @param fee_item Which of the currencies to be used as fee - * @param destination The Multilocation to which we want to send the tokens - * @param weight The weight we want to buy in the destination chain - */ + * + * @dev The token transfer burns/transfers the corresponding amount before sending + * @param assets The assets we want to transfer, defined by their location and amount. + * @param fee_item Which of the currencies to be used as fee + * @param destination The Multilocation to which we want to send the tokens + * @param weight The weight we want to buy in the destination chain + * Selector: b38c60fa + */ function transfer_multi_assets( MultiAsset[] memory assets, uint32 fee_item, diff --git a/precompiles/xtokens/src/lib.rs b/precompiles/xtokens/src/lib.rs index 61605dd1132..a1b647076e7 100644 --- a/precompiles/xtokens/src/lib.rs +++ b/precompiles/xtokens/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature(assert_matches)] -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::{PrecompileHandle, PrecompileOutput}; use frame_support::{ dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, ensure, @@ -27,20 +27,21 @@ use frame_support::{ }; use pallet_evm::{AddressMapping, Precompile}; use precompile_utils::{ - Address, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, Gasometer, - RuntimeHelper, + revert, succeed, Address, EvmData, EvmDataReader, EvmDataWriter, EvmResult, FunctionModifier, + PrecompileHandleExt, RuntimeHelper, }; - use sp_core::{H160, U256}; -use sp_std::boxed::Box; use sp_std::{ + boxed::Box, convert::{TryFrom, TryInto}, fmt::Debug, marker::PhantomData, vec::Vec, }; -use xcm::latest::{AssetId, Fungibility, MultiAsset, MultiAssets, MultiLocation}; -use xcm::{VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation}; +use xcm::{ + latest::{AssetId, Fungibility, MultiAsset, MultiAssets, MultiLocation}, + VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, +}; use xcm_primitives::AccountIdToCurrencyId; #[cfg(test)] @@ -80,31 +81,18 @@ where XBalanceOf: TryFrom + Into + EvmData, Runtime: AccountIdToCurrencyId>, { - fn execute( - input: &[u8], //Reminder this is big-endian - target_gas: Option, - context: &Context, - is_static: bool, - ) -> EvmResult { - let mut gasometer = Gasometer::new(target_gas); - let gasometer = &mut gasometer; + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; - let (mut input, selector) = EvmDataReader::new_with_selector(gasometer, input)?; - let input = &mut input; - - gasometer.check_function_modifier(context, is_static, FunctionModifier::NonPayable)?; + handle.check_function_modifier(FunctionModifier::NonPayable)?; match selector { - Action::Transfer => Self::transfer(input, gasometer, context), - Action::TransferWithFee => Self::transfer_with_fee(input, gasometer, context), - Action::TransferMultiAsset => Self::transfer_multiasset(input, gasometer, context), - Action::TransferMultiAssetWithFee => { - Self::transfer_multiasset_with_fee(input, gasometer, context) - } - Action::TransferMultiCurrencies => { - Self::transfer_multi_currencies(input, gasometer, context) - } - Action::TransferMultiAssets => Self::transfer_multi_assets(input, gasometer, context), + Action::Transfer => Self::transfer(handle), + Action::TransferWithFee => Self::transfer_with_fee(handle), + Action::TransferMultiAsset => Self::transfer_multiasset(handle), + Action::TransferMultiAssetWithFee => Self::transfer_multiasset_with_fee(handle), + Action::TransferMultiCurrencies => Self::transfer_multi_currencies(handle), + Action::TransferMultiAssets => Self::transfer_multi_assets(handle), } } } @@ -118,34 +106,32 @@ where XBalanceOf: TryFrom + Into + EvmData, Runtime: AccountIdToCurrencyId>, { - fn transfer( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn transfer(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + // Bound check - input.expect_arguments(gasometer, 2)?; - let to_address: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; + input.expect_arguments(2)?; + let to_address: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; // We use the MultiLocation, which we have instructed how to read // In the end we are using the encoding - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; // Bound check - input.expect_arguments(gasometer, 1)?; - let dest_weight: u64 = input.read::(gasometer)?; + input.expect_arguments(1)?; + let dest_weight: u64 = input.read::()?; let to_account = Runtime::AddressMapping::into_account_id(to_address); // We convert the address into a currency id xtokens understands let currency_id: ::CurrencyId = Runtime::account_to_currency_id(to_account) - .ok_or(gasometer.revert("cannot convert into currency id"))?; + .ok_or(revert("cannot convert into currency id"))?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let amount = amount .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; let call = orml_xtokens::Call::::transfer { currency_id, @@ -154,50 +140,42 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn transfer_with_fee( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - input.expect_arguments(gasometer, 5)?; + fn transfer_with_fee(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(5)?; - let to_address: H160 = input.read::
(gasometer)?.into(); - let amount: U256 = input.read(gasometer)?; - let fee: U256 = input.read(gasometer)?; + let to_address: H160 = input.read::
()?.into(); + let amount: U256 = input.read()?; + let fee: U256 = input.read()?; // We use the MultiLocation, which we have instructed how to read // In the end we are using the encoding - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; - let dest_weight: u64 = input.read::(gasometer)?; + let dest_weight: u64 = input.read::()?; let to_account = Runtime::AddressMapping::into_account_id(to_address); // We convert the address into a currency id xtokens understands let currency_id: ::CurrencyId = Runtime::account_to_currency_id(to_account) - .ok_or(gasometer.revert("cannot convert into currency id"))?; + .ok_or(revert("cannot convert into currency id"))?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Transferred amount let amount = amount .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; // Fee amount let fee = fee .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; let call = orml_xtokens::Call::::transfer_with_fee { currency_id, @@ -207,39 +185,31 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn transfer_multiasset( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { + fn transfer_multiasset(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; // asset is defined as a multiLocation. For now we are assuming these are concrete // fungible assets - let asset_multilocation: MultiLocation = input.read::(gasometer)?; + let asset_multilocation: MultiLocation = input.read::()?; // Bound check - input.expect_arguments(gasometer, 1)?; - let amount: U256 = input.read(gasometer)?; + input.expect_arguments(1)?; + let amount: U256 = input.read()?; // read destination - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; // Bound check - input.expect_arguments(gasometer, 1)?; - let dest_weight: u64 = input.read::(gasometer)?; + input.expect_arguments(1)?; + let dest_weight: u64 = input.read::()?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let to_balance = amount .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; let call = orml_xtokens::Call::::transfer_multiasset { asset: Box::new(VersionedMultiAsset::V1(MultiAsset { @@ -250,41 +220,35 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } fn transfer_multiasset_with_fee( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - input.expect_arguments(gasometer, 5)?; + let mut input = handle.read_input()?; + input.expect_arguments(5)?; // asset is defined as a multiLocation. For now we are assuming these are concrete // fungible assets - let asset_multilocation: MultiLocation = input.read::(gasometer)?; - let amount: U256 = input.read(gasometer)?; - let fee: U256 = input.read(gasometer)?; + let asset_multilocation: MultiLocation = input.read::()?; + let amount: U256 = input.read()?; + let fee: U256 = input.read()?; // read destination - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; - let dest_weight: u64 = input.read::(gasometer)?; + let dest_weight: u64 = input.read::()?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let amount = amount .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; let fee = fee .try_into() - .map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?; + .map_err(|_| revert("Amount is too large for provided balance type"))?; let call = orml_xtokens::Call::::transfer_multiasset_with_fee { asset: Box::new(VersionedMultiAsset::V1(MultiAsset { @@ -299,40 +263,34 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } fn transfer_multi_currencies( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, + handle: &mut impl PrecompileHandle, ) -> EvmResult { - input.expect_arguments(gasometer, 4)?; - let non_mapped_currencies: Vec = input.read::>(gasometer)?; + let mut input = handle.read_input()?; + input.expect_arguments(4)?; + let non_mapped_currencies: Vec = input.read::>()?; let max_assets = MaxAssetsForTransfer::::get(); // We check this here so that we avoid iterating over the vec // if the len is more than the max permitted ensure!( max_assets >= non_mapped_currencies.len(), - gasometer.revert("More than max number of assets given") + revert("More than max number of assets given") ); - let fee_item: u32 = input.read::(gasometer)?; + let fee_item: u32 = input.read::()?; // read destination - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; - let dest_weight: u64 = input.read::(gasometer)?; + let dest_weight: u64 = input.read::()?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); // Build all currencies let currencies: EvmResult< @@ -344,15 +302,17 @@ where .iter() .map(|currency| { let address_as_h160: H160 = currency.address.clone().into(); - let amount = currency.amount.clone().try_into().map_err(|_| { - gasometer.revert("Amount is too large for provided balance type") - })?; + let amount = currency + .amount + .clone() + .try_into() + .map_err(|_| revert("Amount is too large for provided balance type"))?; Ok(( Runtime::account_to_currency_id(Runtime::AddressMapping::into_account_id( address_as_h160, )) - .ok_or(gasometer.revert("cannot convert into currency id"))?, + .ok_or(revert("cannot convert into currency id"))?, amount, )) }) @@ -367,47 +327,41 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } - fn transfer_multi_assets( - input: &mut EvmDataReader, - gasometer: &mut Gasometer, - context: &Context, - ) -> EvmResult { - input.expect_arguments(gasometer, 4)?; - let assets: Vec = input.read::>(gasometer)?; + fn transfer_multi_assets(handle: &mut impl PrecompileHandle) -> EvmResult { + let mut input = handle.read_input()?; + input.expect_arguments(4)?; + let assets: Vec = input.read::>()?; let max_assets = MaxAssetsForTransfer::::get(); // We check this here so that we avoid iterating over the vec // if the len is more than the max permitted ensure!( max_assets >= assets.len(), - gasometer.revert("More than max number of assets given") + revert("More than max number of assets given") ); - let fee_item: u32 = input.read::(gasometer)?; + let fee_item: u32 = input.read::()?; // read destination - let destination: MultiLocation = input.read::(gasometer)?; + let destination: MultiLocation = input.read::()?; - let dest_weight: u64 = input.read::(gasometer)?; + let dest_weight: u64 = input.read::()?; - let origin = Runtime::AddressMapping::into_account_id(context.caller); + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let multiasset_vec: EvmResult> = assets .iter() .map(|evm_multiasset| { - let to_balance: u128 = evm_multiasset.amount.clone().try_into().map_err(|_| { - gasometer.revert("Amount is too large for provided balance type") - })?; + let to_balance: u128 = evm_multiasset + .amount + .clone() + .try_into() + .map_err(|_| revert("Amount is too large for provided balance type"))?; Ok((evm_multiasset.location.clone(), to_balance).into()) }) .collect(); @@ -415,7 +369,7 @@ where // Since multiassets sorts them, we need to check whether the index is still correct, // and error otherwise as there is not much we can do other than that let multiassets = MultiAssets::from_sorted_and_deduplicated(multiasset_vec?) - .map_err(|_| gasometer.revert("Provided vector either not sorted nor deduplicated"))?; + .map_err(|_| revert("Provided vector either not sorted nor deduplicated"))?; let call = orml_xtokens::Call::::transfer_multiassets { assets: Box::new(VersionedMultiAssets::V1(multiassets)), @@ -424,14 +378,9 @@ where dest_weight, }; - RuntimeHelper::::try_dispatch(Some(origin).into(), call, gasometer)?; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: gasometer.used_gas(), - output: Default::default(), - logs: Default::default(), - }) + Ok(succeed([])) } } @@ -442,8 +391,8 @@ pub struct Currency { } // For Currencies impl EvmData for Currency { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let (address, amount) = reader.read(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let (address, amount) = reader.read()?; Ok(Currency { address, amount }) } @@ -472,8 +421,8 @@ pub struct EvmMultiAsset { } impl EvmData for EvmMultiAsset { - fn read(reader: &mut EvmDataReader, gasometer: &mut Gasometer) -> EvmResult { - let (location, amount) = reader.read(gasometer)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let (location, amount) = reader.read()?; Ok(EvmMultiAsset { location, amount }) } diff --git a/precompiles/xtokens/src/mock.rs b/precompiles/xtokens/src/mock.rs index 1416d4d3d0d..0b507f0e4b4 100644 --- a/precompiles/xtokens/src/mock.rs +++ b/precompiles/xtokens/src/mock.rs @@ -17,13 +17,11 @@ //! Test utilities use super::*; use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{construct_runtime, parameter_types}; use frame_support::{ traits::{EnsureOrigin, Everything, OriginTrait, PalletInfo as PalletInfoTrait}, weights::Weight, }; - -use frame_support::{construct_runtime, parameter_types}; - use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key}; use pallet_evm::{AddressMapping, EnsureAddressNever, EnsureAddressRoot, PrecompileSet}; use serde::{Deserialize, Serialize}; @@ -220,18 +218,9 @@ impl PrecompileSet for TestPrecompiles where XtokensWrapper: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option> { - match address { - a if a == precompile_address() => Some(XtokensWrapper::::execute( - input, target_gas, context, is_static, - )), + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option> { + match handle.code_address() { + a if a == precompile_address() => Some(XtokensWrapper::::execute(handle)), _ => None, } } @@ -517,14 +506,3 @@ pub(crate) fn events() -> Vec { .map(|r| r.event) .collect::>() } - -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} diff --git a/precompiles/xtokens/src/tests.rs b/precompiles/xtokens/src/tests.rs index 9cc9cd358f9..47e22ff2f94 100644 --- a/precompiles/xtokens/src/tests.rs +++ b/precompiles/xtokens/src/tests.rs @@ -14,19 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Moonbeam. If not, see . -use std::assert_matches::assert_matches; - use crate::mock::{ - events, evm_test_context, precompile_address, CurrencyId, CurrencyIdToMultiLocation, - ExtBuilder, PrecompilesValue, Runtime, TestAccount::*, TestPrecompiles, + events, CurrencyId, CurrencyIdToMultiLocation, ExtBuilder, PrecompilesValue, Runtime, + TestAccount::*, TestPrecompiles, }; -use crate::{Action, Currency, EvmMultiAsset, PrecompileOutput}; -use fp_evm::{Context, PrecompileFailure}; -use num_enum::TryFromPrimitive; +use crate::{Action, Currency, EvmMultiAsset}; use orml_xtokens::Event as XtokensEvent; -use pallet_evm::{ExitSucceed, PrecompileSet}; -use precompile_utils::{Address, EvmDataWriter}; -use sha3::{Digest, Keccak256}; +use precompile_utils::{testing::*, Address, EvmDataWriter}; use sp_core::U256; use sp_runtime::traits::Convert; use xcm::latest::{ @@ -39,80 +33,28 @@ fn precompiles() -> TestPrecompiles { #[test] fn test_selector_enum() { - let mut buffer = [0u8; 4]; - buffer.copy_from_slice( - &Keccak256::digest(b"transfer(address,uint256,(uint8,bytes[]),uint64)")[0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::Transfer, - ); - - buffer.copy_from_slice( - &Keccak256::digest(b"transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),uint64)") - [0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransferMultiAsset, - ); - - buffer.copy_from_slice( - &Keccak256::digest(b"transfer_with_fee(address,uint256,uint256,(uint8,bytes[]),uint64)") - [0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransferWithFee, - ); - - buffer.copy_from_slice( - &Keccak256::digest( - b"transfer_multiasset_with_fee((uint8,bytes[]),uint256,uint256,(uint8,bytes[]),uint64)", - )[0..4], - ); - assert_eq!( - Action::try_from_primitive(u32::from_be_bytes(buffer)).unwrap(), - Action::TransferMultiAssetWithFee, - ); + assert_eq!(Action::Transfer as u32, 0xb9f813ff); + assert_eq!(Action::TransferMultiAsset as u32, 0xb38c60fa); + assert_eq!(Action::TransferMultiCurrencies as u32, 0x8a362d5c); + assert_eq!(Action::TransferWithFee as u32, 0x94f69115); + assert_eq!(Action::TransferMultiAssetWithFee as u32, 0x89a570fc); } #[test] fn selector_less_than_four_bytes() { ExtBuilder::default().build().execute_with(|| { - // This selector is only three bytes long when four are required. - let bogus_selector = vec![1u8, 2u8, 3u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"tried to parse selector out of bounds", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8]) + .execute_reverts(|output| output == b"tried to parse selector out of bounds"); }); } #[test] fn no_selector_exists_but_length_is_right() { ExtBuilder::default().build().execute_with(|| { - let bogus_selector = vec![1u8, 2u8, 3u8, 4u8]; - - assert_matches!( - precompiles().execute( - precompile_address(), - &bogus_selector, - None, - &evm_test_context(), - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"unknown selector", - ); + precompiles() + .prepare_test(Alice, Precompile, vec![1u8, 2u8, 3u8, 4u8]) + .execute_reverts(|output| output == b"unknown selector"); }); } @@ -130,30 +72,21 @@ fn transfer_self_reserve_works() { }), ); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(SelfReserve.into())) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); + let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete( CurrencyIdToMultiLocation::convert(CurrencyId::SelfReserve).unwrap(), @@ -186,30 +119,21 @@ fn transfer_to_reserve_works() { }), ); // We are transferring asset 0, which we have instructed to be the relay asset - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(AssetId(0u128).into())) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); + let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete( CurrencyIdToMultiLocation::convert(CurrencyId::OtherReserve(0u128)).unwrap(), @@ -243,31 +167,21 @@ fn transfer_to_reserve_with_fee_works() { ); // We are transferring asset 0, which we have instructed to be the relay asset // Fees are not trully charged, so no worries - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferWithFee) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferWithFee) .write(Address(AssetId(0u128).into())) .write(U256::from(500u64)) .write(U256::from(50u64)) .write(destination.clone()) .write(U256::from(4000000u64)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete( @@ -309,30 +223,20 @@ fn transfer_non_reserve_to_non_reserve_works() { ); // We are transferring asset 1, which corresponds to another parachain Id asset - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::Transfer) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::Transfer) .write(Address(AssetId(1u128).into())) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete( @@ -367,31 +271,22 @@ fn transfer_non_reserve_to_non_reserve_with_fee_works() { ); // We are transferring asset 1, which corresponds to another parachain Id asset - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferWithFee) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferWithFee) .write(Address(AssetId(1u128).into())) .write(U256::from(500u32)) .write(U256::from(50u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); + let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete( CurrencyIdToMultiLocation::convert(CurrencyId::OtherReserve(1u128)).unwrap(), @@ -432,30 +327,20 @@ fn transfer_multi_asset_to_reserve_works() { let asset = MultiLocation::parent(); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAsset) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAsset) .write(asset.clone()) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete(asset), @@ -490,30 +375,20 @@ fn transfer_multi_asset_self_reserve_works() { let self_reserve = crate::mock::SelfReserve::get(); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAsset) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAsset) .write(self_reserve.clone()) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete(self_reserve), @@ -547,31 +422,21 @@ fn transfer_multi_asset_self_reserve_with_fee_works() { let self_reserve = crate::mock::SelfReserve::get(); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAssetWithFee) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAssetWithFee) .write(self_reserve.clone()) .write(U256::from(500u32)) .write(U256::from(50u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete(self_reserve.clone()), @@ -612,30 +477,20 @@ fn transfer_multi_asset_non_reserve_to_non_reserve() { Junctions::X2(Junction::Parachain(2), Junction::GeneralIndex(5u128)), ); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAsset) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAsset) .write(asset_location.clone()) .write(U256::from(500u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete(asset_location), @@ -672,31 +527,21 @@ fn transfer_multi_asset_non_reserve_to_non_reserve_with_fee() { Junctions::X2(Junction::Parachain(2), Junction::GeneralIndex(5u128)), ); - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAssetWithFee) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAssetWithFee) .write(asset_location.clone()) .write(U256::from(500u32)) .write(U256::from(50u32)) .write(destination.clone()) .write(U256::from(4000000u32)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0u32), - }, - false - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); let expected_asset: MultiAsset = MultiAsset { id: AssetId::Concrete(asset_location.clone()), @@ -737,30 +582,21 @@ fn transfer_multi_currencies() { ]; // We are transferring 2 assets - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiCurrencies) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiCurrencies) .write(currencies) .write(0u32) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); + let expected_asset_1: MultiAsset = MultiAsset { id: AssetId::Concrete( CurrencyIdToMultiLocation::convert(CurrencyId::OtherReserve(1u128)).unwrap(), @@ -823,30 +659,21 @@ fn transfer_multi_assets() { .unwrap(); // We are transferring 2 assets - assert_eq!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAssets) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAssets) .write(assets) .write(0u32) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 3000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(3000) + .expect_no_logs() + .execute_returns(vec![]); + let expected: crate::mock::Event = XtokensEvent::TransferredMultiAssets { sender: Alice, assets: multiassets, @@ -881,26 +708,18 @@ fn transfer_multi_currencies_cannot_insert_more_than_max() { ]; // We are transferring 3 assets, when max is 2 - assert_matches!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiCurrencies) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiCurrencies) .write(currencies) .write(0u32) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"More than max number of assets given", - ); + ) + .execute_reverts(|output| output == b"More than max number of assets given"); }); } @@ -942,26 +761,18 @@ fn transfer_multi_assets_cannot_insert_more_than_max() { ]; // We are transferring 3 assets, when max is 2 - assert_matches!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAssets) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAssets) .write(assets) .write(0u32) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"More than max number of assets given", - ); + ) + .execute_reverts(|output| output == b"More than max number of assets given"); }); } @@ -998,25 +809,19 @@ fn transfer_multi_assets_is_not_sorted_error() { ]; // We are transferring 3 assets, when max is 2 - assert_matches!( - precompiles().execute( - Precompile.into(), - &EvmDataWriter::new_with_selector(Action::TransferMultiAssets) + precompiles() + .prepare_test( + Alice, + Precompile, + EvmDataWriter::new_with_selector(Action::TransferMultiAssets) .write(assets) .write(0u32) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: Precompile.into(), - caller: Alice.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Err(PrecompileFailure::Revert { output, ..})) - if output == b"Provided vector either not sorted nor deduplicated", - ); + ) + .execute_reverts(|output| { + output == b"Provided vector either not sorted nor deduplicated" + }); }); } diff --git a/primitives/rpc/evm-tracing-events/Cargo.toml b/primitives/rpc/evm-tracing-events/Cargo.toml index d697d193d26..a15260610cf 100644 --- a/primitives/rpc/evm-tracing-events/Cargo.toml +++ b/primitives/rpc/evm-tracing-events/Cargo.toml @@ -17,9 +17,9 @@ sp-runtime-interface = { git = "https://github.com/paritytech/substrate", branch # Ethereum ethereum = { version = "0.12.0", default-features = false, features = [ "with-codec" ] } ethereum-types = { version = "0.13.1", default-features = false } -evm = { version = "0.35.0", default-features = false, features = [ "with-codec" ] } -evm-gasometer = { version = "0.35.0", default-features = false } -evm-runtime = { version = "0.35.0", default-features = false } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false, features = [ "with-codec" ] } +evm-gasometer = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false } +evm-runtime = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false } [features] default = [ "std" ] diff --git a/primitives/rpc/evm-tracing-events/src/evm.rs b/primitives/rpc/evm-tracing-events/src/evm.rs index 8154dbb200b..949589731c1 100644 --- a/primitives/rpc/evm-tracing-events/src/evm.rs +++ b/primitives/rpc/evm-tracing-events/src/evm.rs @@ -128,6 +128,14 @@ pub enum EvmEvent { gas_limit: u64, address: H160, }, + PrecompileSubcall { + code_address: H160, + transfer: Option, + input: Vec, + target_gas: Option, + is_static: bool, + context: super::Context, + }, } #[cfg(feature = "evm-tracing")] @@ -225,6 +233,25 @@ impl<'a> From> for EvmEvent { gas_limit, address, }, + evm::tracing::Event::PrecompileSubcall { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => Self::Call { + code_address, + transfer: if let Some(transfer) = transfer { + Some(transfer.clone().into()) + } else { + None + }, + input: input.to_vec(), + target_gas, + is_static, + context: context.clone().into(), + }, } } } diff --git a/runtime/evm_tracer/Cargo.toml b/runtime/evm_tracer/Cargo.toml index 7a88cef00e2..6a97fd5d38f 100644 --- a/runtime/evm_tracer/Cargo.toml +++ b/runtime/evm_tracer/Cargo.toml @@ -22,9 +22,9 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v # Frontier ethereum-types = { version = "0.13.1", default-features = false } -evm = { version = "0.35.0", default-features = false, features = [ "with-codec" ] } -evm-gasometer = { version = "0.35.0", default-features = false } -evm-runtime = { version = "0.35.0", default-features = false } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false, features = [ "with-codec" ] } +evm-gasometer = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false } +evm-runtime = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false } fp-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } pallet-evm = { git = "https://github.com/purestake/frontier", branch = "moonbeam-polkadot-v0.9.20", default-features = false } diff --git a/runtime/moonbase/src/precompiles.rs b/runtime/moonbase/src/precompiles.rs index 68822c10dcd..b2b0a016a34 100644 --- a/runtime/moonbase/src/precompiles.rs +++ b/runtime/moonbase/src/precompiles.rs @@ -16,7 +16,7 @@ use crate::asset_config::{ForeignAssetInstance, LocalAssetInstance}; use crowdloan_rewards_precompiles::CrowdloanRewardsWrapper; -use fp_evm::Context; +use fp_evm::PrecompileHandle; use moonbeam_relay_encoder::westend::WestendEncoder; use pallet_author_mapping_precompiles::AuthorMappingWrapper; use pallet_democracy_precompiles::DemocracyWrapper; @@ -113,71 +113,43 @@ where AuthorMappingWrapper: Precompile, R: pallet_evm::Config, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option { - match address { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + match handle.code_address() { // Ethereum precompiles : - a if a == hash(1) => Some(ECRecover::execute(input, target_gas, context, is_static)), - a if a == hash(2) => Some(Sha256::execute(input, target_gas, context, is_static)), - a if a == hash(3) => Some(Ripemd160::execute(input, target_gas, context, is_static)), - a if a == hash(5) => Some(Modexp::execute(input, target_gas, context, is_static)), - a if a == hash(4) => Some(Identity::execute(input, target_gas, context, is_static)), - a if a == hash(6) => Some(Bn128Add::execute(input, target_gas, context, is_static)), - a if a == hash(7) => Some(Bn128Mul::execute(input, target_gas, context, is_static)), - a if a == hash(8) => Some(Bn128Pairing::execute(input, target_gas, context, is_static)), - a if a == hash(9) => Some(Blake2F::execute(input, target_gas, context, is_static)), + a if a == hash(1) => Some(ECRecover::execute(handle)), + a if a == hash(2) => Some(Sha256::execute(handle)), + a if a == hash(3) => Some(Ripemd160::execute(handle)), + a if a == hash(5) => Some(Modexp::execute(handle)), + a if a == hash(4) => Some(Identity::execute(handle)), + a if a == hash(6) => Some(Bn128Add::execute(handle)), + a if a == hash(7) => Some(Bn128Mul::execute(handle)), + a if a == hash(8) => Some(Bn128Pairing::execute(handle)), + a if a == hash(9) => Some(Blake2F::execute(handle)), + // Non-Moonbeam specific nor Ethereum precompiles : - a if a == hash(1024) => { - Some(Sha3FIPS256::execute(input, target_gas, context, is_static)) - } - a if a == hash(1025) => Some(Dispatch::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(1026) => Some(ECRecoverPublicKey::execute( - input, target_gas, context, is_static, - )), + a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), + a if a == hash(1025) => Some(Dispatch::::execute(handle)), + a if a == hash(1026) => Some(ECRecoverPublicKey::execute(handle)), + // Moonbeam specific precompiles : - a if a == hash(2048) => Some(ParachainStakingWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2050) => { - Some(Erc20BalancesPrecompile::::execute( - input, target_gas, context, is_static, - )) - } - a if a == hash(2051) => Some(DemocracyWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2052) => Some(XtokensWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2053) => Some(RelayEncoderWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2054) => Some(XcmTransactorWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2055) => Some(AuthorMappingWrapper::::execute( - input, target_gas, context, is_static, - )), + a if a == hash(2048) => Some(ParachainStakingWrapper::::execute(handle)), + a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute(handle)), + a if a == hash(2050) => Some( + Erc20BalancesPrecompile::::execute(handle), + ), + a if a == hash(2051) => Some(DemocracyWrapper::::execute(handle)), + a if a == hash(2052) => Some(XtokensWrapper::::execute(handle)), + a if a == hash(2053) => Some(RelayEncoderWrapper::::execute(handle)), + a if a == hash(2054) => Some(XcmTransactorWrapper::::execute(handle)), + a if a == hash(2055) => Some(AuthorMappingWrapper::::execute(handle)), // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + .execute(handle) } // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX => { - Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + Erc20AssetsPrecompileSet::::new().execute(handle) } _ => None, } diff --git a/runtime/moonbase/tests/common/mod.rs b/runtime/moonbase/tests/common/mod.rs index ae0024f6b68..07918df1b50 100644 --- a/runtime/moonbase/tests/common/mod.rs +++ b/runtime/moonbase/tests/common/mod.rs @@ -87,17 +87,6 @@ pub fn last_event() -> Event { System::events().pop().expect("Event expected").event } -// Helper function to give a simple evm context suitable for tests. -// We can remove this once https://github.com/rust-blockchain/evm/pull/35 -// is in our dependency graph. -pub fn evm_test_context() -> fp_evm::Context { - fp_evm::Context { - address: Default::default(), - caller: Default::default(), - apparent_value: From::from(0), - } -} - // Test struct with the purpose of initializing xcm assets #[derive(Clone)] pub struct XcmAssetInitialization { diff --git a/runtime/moonbase/tests/integration_test.rs b/runtime/moonbase/tests/integration_test.rs index 02d432a4165..cddffccdab4 100644 --- a/runtime/moonbase/tests/integration_test.rs +++ b/runtime/moonbase/tests/integration_test.rs @@ -19,9 +19,9 @@ mod common; use common::*; -use precompile_utils::{Address as EvmAddress, EvmDataWriter, LogsBuilder}; +use precompile_utils::{testing::*, Address as EvmAddress, EvmDataWriter, LogsBuilder}; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use fp_evm::Context; use frame_support::{ assert_noop, assert_ok, dispatch::Dispatchable, @@ -40,6 +40,7 @@ use moonbase_runtime::{ LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, }; +use crowdloan_rewards_precompiles::Action as CrowdloanAction; use nimbus_primitives::NimbusId; use pallet_author_mapping_precompiles::Action as AuthorMappingAction; use pallet_evm::PrecompileSet; @@ -838,61 +839,31 @@ fn is_contributor_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut bob_input_data = Vec::::from([0u8; 36]); - bob_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - bob_input_data[16..36].copy_from_slice(&BOB); - - // Expected result is an EVM boolean false which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 0; - let expected_false_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &bob_input_data, - None, // target_gas is not necessary right now because consumed none now - &evm_test_context(), - false - ), - expected_false_result - ); - - // Construct the input data to check if Charlie is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - - // Expected result is an EVM boolean true which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 1; - let expected_true_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is a nominator - assert_eq!( - Precompiles::new().execute( + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(BOB.into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &evm_test_context(), - false, - ), - expected_true_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(CHARLIE.into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -948,37 +919,26 @@ fn reward_info_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"reward_info(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - let expected_total: U256 = (1_500_000 * UNIT).into(); let expected_claimed: U256 = (450_000 * UNIT).into(); - // Expected result is two EVM u256 false which are 256 bits long. - let mut expected_bytes = Vec::from([0u8; 64]); - expected_total.to_big_endian(&mut expected_bytes[0..32]); - expected_claimed.to_big_endian(&mut expected_bytes[32..64]); - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &evm_test_context(), - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::RewardInfo) + .write(EvmAddress(AccountId::from(CHARLIE).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(expected_total) + .write(expected_claimed) + .build(), + ); }) } @@ -1127,50 +1087,31 @@ fn asset_erc20_precompiles_supply_and_balance() { // Convert the assetId to its corresponding precompile address let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + // Access totalSupply through precompile. + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * UNIT)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * UNIT)).build()); }); } @@ -1189,67 +1130,39 @@ fn asset_erc20_precompiles_transfer() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); - - // Transfer tokens from Aice to Bob, 400 unit. - assert_eq!( - Precompiles::new().execute( + // Transfer tokens from Alice to Bob, 400 UNIT. + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); - - // Make sure BOB has 400 unit - assert_eq!( - Precompiles::new().execute( + ) + .expect_cost(23516u64) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Make sure BOB has 400 UNIT + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * UNIT)).build()); }); } @@ -1268,102 +1181,59 @@ fn asset_erc20_precompiles_approve() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Aprove Bob for spending 400 unit from Alice - assert_eq!( - Precompiles::new().execute( + // Aprove Bob for spending 400 UNIT from Alice + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); - - // Make sure CHARLIE has 400 unit - assert_eq!( - Precompiles::new().execute( + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Make sure CHARLIE has 400 UNIT + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * UNIT)).build()); }); } @@ -1382,41 +1252,26 @@ fn asset_erc20_precompiles_mint_burn() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12821u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::default(), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(1000 * UNIT)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Mint 1000 UNITS to BOB - assert_eq!( - Precompiles::new().execute( + // Mint 1000 MOVRS to BOB + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Mint) + EvmDataWriter::new_with_selector(AssetAction::Mint) .write(EvmAddress(BOB.into())) .write(U256::from(1000 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12821) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::default(), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(1000 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been minted assert_eq!(LocalAssets::total_supply(0u128), 2_000 * UNIT); @@ -1425,39 +1280,24 @@ fn asset_erc20_precompiles_mint_burn() { 1_000 * UNIT ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12957u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(BOB), - H160::default(), - EvmDataWriter::new().write(U256::from(500 * UNIT)).build(), - ) - .build(), - })); - - // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + // Burn tokens + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Burn) + EvmDataWriter::new_with_selector(AssetAction::Burn) .write(EvmAddress(BOB.into())) .write(U256::from(500 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12957) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(BOB), + H160::default(), + EvmDataWriter::new().write(U256::from(500 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been burnt assert_eq!(LocalAssets::total_supply(0u128), 1_500 * UNIT); @@ -1483,33 +1323,20 @@ fn asset_erc20_precompiles_freeze_thaw_account() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6732u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Freeze Account - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Freeze) + EvmDataWriter::new_with_selector(AssetAction::Freeze) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6732) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1517,31 +1344,18 @@ fn asset_erc20_precompiles_freeze_thaw_account() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6731u64, - logs: Default::default(), - })); - // Thaw Account - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Thaw) + EvmDataWriter::new_with_selector(AssetAction::Thaw) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6731) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is not frozen assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) @@ -1565,31 +1379,18 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5589u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Freeze Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), + ) + .expect_cost(5589) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1597,34 +1398,16 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5593u64, - logs: Default::default(), - })); - // Thaw Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Assert account is not frozen - assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) - .into_result() - .is_ok()); + EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), + ) + .expect_cost(5593) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -1643,41 +1426,20 @@ fn asset_erc20_precompiles_freeze_transfer_ownership() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6666u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Transfer ownerhsip of an asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) + EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // No clear way of testing BOB is new owner, other than doing a priviledged function - // e.g., transfer_ownership again - assert_ok!(LocalAssets::transfer_ownership( - origin_of(AccountId::from(BOB)), - 0u128, - AccountId::from(ALICE) - )); + ) + .expect_cost(6666) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }); } @@ -1696,35 +1458,22 @@ fn asset_erc20_precompiles_freeze_set_team() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5614u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Set Bob as issuer, admin and freezer - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::SetTeam) + EvmDataWriter::new_with_selector(AssetAction::SetTeam) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(5614) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Bob should be able to mint, freeze, and thaw assert_ok!(LocalAssets::mint( @@ -1773,54 +1522,34 @@ fn xcm_asset_erc20_precompiles_supply_and_balance() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Assert the asset has been created with the correct supply assert_eq!(Assets::total_supply(relay_asset_id), 1_000 * UNIT); - // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * UNIT)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * UNIT)).build()); }); } @@ -1851,68 +1580,39 @@ fn xcm_asset_erc20_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); + ); - // Transfer tokens from Aice to Bob, 400 unit. - assert_eq!( - Precompiles::new().execute( + // Transfer tokens from Alice to Bob, 400 UNIT. + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); - - // Make sure BOB has 400 unit - assert_eq!( - Precompiles::new().execute( + ) + .expect_cost(23516) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Make sure BOB has 400 UNIT + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * UNIT)).build()); }); } @@ -1943,103 +1643,59 @@ fn xcm_asset_erc20_precompiles_approve() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); + ); - // Aprove Bob for spending 400 unit from Alice - assert_eq!( - Precompiles::new().execute( + // Aprove Bob for spending 400 UNIT from Alice + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * UNIT)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), - cost: 1000, - logs: Default::default(), - })); - - // Make sure CHARLIE has 400 unit - assert_eq!( - Precompiles::new().execute( + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * UNIT)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); + + // Make sure CHARLIE has 400 UNIT + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * UNIT)).build()); }); } @@ -2073,8 +1729,7 @@ fn xtokens_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Alice has 1000 tokens. She should be able to send through precompile let destination = MultiLocation::new( @@ -2085,31 +1740,21 @@ fn xtokens_precompiles_transfer() { }), ); - // We use the address of the asset as an identifier of the asset we want to transferS - assert_eq!( - Precompiles::new().execute( + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::Transfer) - .write(EvmAddress(asset_precompile_address)) + EvmDataWriter::new_with_selector(XtokensAction::Transfer) + .write(EvmAddress(asset_precompile_address.into())) .write(U256::from(500_000_000_000_000u128)) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 12000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(12000) + .expect_no_logs() + .execute_returns(vec![]) }) } @@ -2147,31 +1792,21 @@ fn xtokens_precompiles_transfer_multiasset() { // This time we transfer it through TransferMultiAsset // Instead of the address, we encode directly the multilocation referencing the asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) + EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) // We want to transfer the relay token .write(MultiLocation::parent()) .write(U256::from(500_000_000_000_000u128)) .write(destination) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 12000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(12000) + .expect_no_logs() + .execute_returns(vec![]); }) } @@ -2199,31 +1834,21 @@ fn xtokens_precompiles_transfer_native() { }), ); - // We use the address of the asset as an identifier of the asset we want to transferS - assert_eq!( - Precompiles::new().execute( + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::Transfer) + EvmDataWriter::new_with_selector(XtokensAction::Transfer) .write(EvmAddress(asset_precompile_address)) .write(U256::from(500 * UNIT)) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 12000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(12000) + .expect_no_logs() + .execute_returns(vec![]); }) } @@ -2257,31 +1882,21 @@ fn xtokens_precompile_transfer_local_asset() { }), ); - // We use the address of the asset as an identifier of the asset we want to transferS - assert_eq!( - Precompiles::new().execute( + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::Transfer) + EvmDataWriter::new_with_selector(XtokensAction::Transfer) .write(EvmAddress(asset_precompile_address)) .write(U256::from(500 * UNIT)) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 12000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(12000) + .expect_no_logs() + .execute_returns(vec![]); }) } @@ -2749,30 +2364,18 @@ fn author_mapping_precompile_associate_update_and_clear() { let second_vrf_id: session_keys_primitives::VrfId = sp_core::sr25519::Public::unchecked_from([2u8; 32]).into(); - let associate_expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 15388u64, - logs: Default::default(), - })); - // Associate it - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, author_mapping_precompile_address, - &EvmDataWriter::new_with_selector(AuthorMappingAction::AddAssociation) + EvmDataWriter::new_with_selector(AuthorMappingAction::AddAssociation) .write(sp_core::H256::from([1u8; 32])) .build(), - None, - &Context { - address: author_mapping_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - associate_expected_result - ); + ) + .expect_cost(15388) + .expect_no_logs() + .execute_returns(vec![]); let expected_associate_event = Event::AuthorMapping(pallet_author_mapping::Event::AuthorRegistered { @@ -2782,31 +2385,19 @@ fn author_mapping_precompile_associate_update_and_clear() { }); assert_eq!(last_event(), expected_associate_event); - let update_expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 15190u64, - logs: Default::default(), - })); - // Update it - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, author_mapping_precompile_address, - &EvmDataWriter::new_with_selector(AuthorMappingAction::UpdateAssociation) + EvmDataWriter::new_with_selector(AuthorMappingAction::UpdateAssociation) .write(sp_core::H256::from([1u8; 32])) .write(sp_core::H256::from([2u8; 32])) .build(), - None, - &Context { - address: author_mapping_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - update_expected_result - ); + ) + .expect_cost(15190) + .expect_no_logs() + .execute_returns(vec![]); let expected_update_event = Event::AuthorMapping(pallet_author_mapping::Event::AuthorRotated { @@ -2816,30 +2407,18 @@ fn author_mapping_precompile_associate_update_and_clear() { }); assert_eq!(last_event(), expected_update_event); - let clear_expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 15447u64, - logs: Default::default(), - })); - // Clear it - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, author_mapping_precompile_address, - &EvmDataWriter::new_with_selector(AuthorMappingAction::ClearAssociation) + EvmDataWriter::new_with_selector(AuthorMappingAction::ClearAssociation) .write(sp_core::H256::from([2u8; 32])) .build(), - None, - &Context { - address: author_mapping_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - clear_expected_result - ); + ) + .expect_cost(15447) + .expect_no_logs() + .execute_returns(vec![]); let expected_clear_event = Event::AuthorMapping(pallet_author_mapping::Event::AuthorDeRegistered { @@ -2867,31 +2446,19 @@ fn author_mapping_register_and_set_keys() { let second_vrf_key: session_keys_primitives::VrfId = sp_core::sr25519::Public::unchecked_from([4u8; 32]).into(); - let associate_expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 15428u64, - logs: Default::default(), - })); - // Associate it - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, author_mapping_precompile_address, - &EvmDataWriter::new_with_selector(AuthorMappingAction::RegisterKeys) + EvmDataWriter::new_with_selector(AuthorMappingAction::RegisterKeys) .write(sp_core::H256::from([1u8; 32])) .write(sp_core::H256::from([3u8; 32])) .build(), - None, - &Context { - address: author_mapping_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - associate_expected_result - ); + ) + .expect_cost(15428) + .expect_no_logs() + .execute_returns(vec![]); let expected_associate_event = Event::AuthorMapping(pallet_author_mapping::Event::AuthorRegistered { @@ -2901,31 +2468,19 @@ fn author_mapping_register_and_set_keys() { }); assert_eq!(last_event(), expected_associate_event); - let update_expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: Default::default(), - cost: 16280u64, - logs: Default::default(), - })); - // Update it - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, author_mapping_precompile_address, - &EvmDataWriter::new_with_selector(AuthorMappingAction::SetKeys) + EvmDataWriter::new_with_selector(AuthorMappingAction::SetKeys) .write(sp_core::H256::from([2u8; 32])) .write(sp_core::H256::from([4u8; 32])) .build(), - None, - &Context { - address: author_mapping_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - update_expected_result - ); + ) + .expect_cost(16280) + .expect_no_logs() + .execute_returns(vec![]); let expected_update_event = Event::AuthorMapping(pallet_author_mapping::Event::AuthorRotated { @@ -2961,17 +2516,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_some(), "execute({},..) should return Some(_)", i @@ -2985,17 +2537,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_none(), "execute({},..) should return None", i diff --git a/runtime/moonbeam/src/precompiles.rs b/runtime/moonbeam/src/precompiles.rs index 47c6e4ff56a..f422d639314 100644 --- a/runtime/moonbeam/src/precompiles.rs +++ b/runtime/moonbeam/src/precompiles.rs @@ -16,7 +16,7 @@ use crate::asset_config::{ForeignAssetInstance, LocalAssetInstance}; use crowdloan_rewards_precompiles::CrowdloanRewardsWrapper; -use fp_evm::Context; +use fp_evm::PrecompileHandle; use moonbeam_relay_encoder::polkadot::PolkadotEncoder; use pallet_author_mapping_precompiles::AuthorMappingWrapper; use pallet_democracy_precompiles::DemocracyWrapper; @@ -113,71 +113,44 @@ where AuthorMappingWrapper: Precompile, R: pallet_evm::Config, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option { - match address { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + match handle.code_address() { // Ethereum precompiles : - a if a == hash(1) => Some(ECRecover::execute(input, target_gas, context, is_static)), - a if a == hash(2) => Some(Sha256::execute(input, target_gas, context, is_static)), - a if a == hash(3) => Some(Ripemd160::execute(input, target_gas, context, is_static)), - a if a == hash(4) => Some(Identity::execute(input, target_gas, context, is_static)), - a if a == hash(5) => Some(Modexp::execute(input, target_gas, context, is_static)), - a if a == hash(6) => Some(Bn128Add::execute(input, target_gas, context, is_static)), - a if a == hash(7) => Some(Bn128Mul::execute(input, target_gas, context, is_static)), - a if a == hash(8) => Some(Bn128Pairing::execute(input, target_gas, context, is_static)), - a if a == hash(9) => Some(Blake2F::execute(input, target_gas, context, is_static)), + a if a == hash(1) => Some(ECRecover::execute(handle)), + a if a == hash(2) => Some(Sha256::execute(handle)), + a if a == hash(3) => Some(Ripemd160::execute(handle)), + a if a == hash(4) => Some(Identity::execute(handle)), + a if a == hash(5) => Some(Modexp::execute(handle)), + a if a == hash(6) => Some(Bn128Add::execute(handle)), + a if a == hash(7) => Some(Bn128Mul::execute(handle)), + a if a == hash(8) => Some(Bn128Pairing::execute(handle)), + a if a == hash(9) => Some(Blake2F::execute(handle)), // Non-Moonbeam specific nor Ethereum precompiles : - a if a == hash(1024) => { - Some(Sha3FIPS256::execute(input, target_gas, context, is_static)) - } - a if a == hash(1025) => Some(Dispatch::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(1026) => Some(ECRecoverPublicKey::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2048) => Some(ParachainStakingWrapper::::execute( - // Moonbeam specific precompiles : - input, target_gas, context, is_static, - )), - a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2050) => { - Some(Erc20BalancesPrecompile::::execute( - input, target_gas, context, is_static, - )) + a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), + a if a == hash(1025) => Some(Dispatch::::execute(handle)), + a if a == hash(1026) => Some(ECRecoverPublicKey::execute(handle)), + + // Moonbeam specific precompiles : + a if a == hash(2048) => Some(ParachainStakingWrapper::::execute(handle)), + a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute(handle)), + a if a == hash(2050) => Some( + Erc20BalancesPrecompile::::execute(handle), + ), + a if a == hash(2051) => Some(DemocracyWrapper::::execute(handle)), + a if a == hash(2052) => Some(XtokensWrapper::::execute(handle)), + a if a == hash(2053) => { + Some(RelayEncoderWrapper::::execute(handle)) } - a if a == hash(2051) => Some(DemocracyWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2052) => Some(XtokensWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2053) => Some(RelayEncoderWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2054) => Some(XcmTransactorWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2055) => Some(AuthorMappingWrapper::::execute( - input, target_gas, context, is_static, - )), + a if a == hash(2054) => Some(XcmTransactorWrapper::::execute(handle)), + a if a == hash(2055) => Some(AuthorMappingWrapper::::execute(handle)), // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + .execute(handle) } // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX => { - Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + Erc20AssetsPrecompileSet::::new().execute(handle) } _ => None, } diff --git a/runtime/moonbeam/tests/integration_test.rs b/runtime/moonbeam/tests/integration_test.rs index c31b17ed8f8..ce5483656f3 100644 --- a/runtime/moonbeam/tests/integration_test.rs +++ b/runtime/moonbeam/tests/integration_test.rs @@ -21,7 +21,8 @@ mod common; use common::*; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use crowdloan_rewards_precompiles::Action as CrowdloanAction; +use fp_evm::Context; use frame_support::{ assert_noop, assert_ok, dispatch::Dispatchable, @@ -45,7 +46,7 @@ use pallet_evm_precompile_assets_erc20::{ }; use pallet_transaction_payment::Multiplier; use parity_scale_codec::Encode; -use precompile_utils::{Address as EvmAddress, EvmDataWriter, LogsBuilder}; +use precompile_utils::{testing::*, Address as EvmAddress, EvmDataWriter, LogsBuilder}; use sha3::{Digest, Keccak256}; use sp_core::{ByteArray, Pair, H160, U256}; use sp_runtime::{ @@ -852,71 +853,31 @@ fn is_contributor_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut bob_input_data = Vec::::from([0u8; 36]); - bob_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - bob_input_data[16..36].copy_from_slice(&BOB); - - // Expected result is an EVM boolean false which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 0; - let expected_false_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &bob_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_false_result - ); - - // Construct the input data to check if Charlie is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - - // Expected result is an EVM boolean true which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 1; - let expected_true_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is a nominator - assert_eq!( - Precompiles::new().execute( + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(AccountId::from(BOB).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_true_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(AccountId::from(CHARLIE).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -972,42 +933,26 @@ fn reward_info_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"reward_info(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - let expected_total: U256 = (1_500_000 * GLMR).into(); let expected_claimed: U256 = (450_000 * GLMR).into(); - // Expected result is two EVM u256 false which are 256 bits long. - let mut expected_bytes = Vec::from([0u8; 64]); - expected_total.to_big_endian(&mut expected_bytes[0..32]); - expected_claimed.to_big_endian(&mut expected_bytes[32..64]); - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::RewardInfo) + .write(EvmAddress(AccountId::from(CHARLIE).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(expected_total) + .write(expected_claimed) + .build(), + ); }) } @@ -1480,50 +1425,31 @@ fn asset_erc20_precompiles_supply_and_balance() { // Convert the assetId to its corresponding precompile address let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); - - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + // Access totalSupply through precompile. + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * GLMR)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * GLMR)).build()); }); } @@ -1542,67 +1468,39 @@ fn asset_erc20_precompiles_transfer() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Transfer tokens from Aice to Bob, 400 GLMR. - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(23516u64) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure BOB has 400 GLMR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * GLMR)).build()); }); } @@ -1621,102 +1519,59 @@ fn asset_erc20_precompiles_approve() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Aprove Bob for spending 400 GLMR from Alice - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure CHARLIE has 400 GLMR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * GLMR)).build()); }); } @@ -1735,41 +1590,26 @@ fn asset_erc20_precompiles_mint_burn() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12821u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::default(), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(1000 * GLMR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Mint 1000 GLMRS to BOB - assert_eq!( - Precompiles::new().execute( + // Mint 1000 MOVRS to BOB + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Mint) + EvmDataWriter::new_with_selector(AssetAction::Mint) .write(EvmAddress(BOB.into())) .write(U256::from(1000 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12821) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::default(), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(1000 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been minted assert_eq!(LocalAssets::total_supply(0u128), 2_000 * GLMR); @@ -1778,39 +1618,24 @@ fn asset_erc20_precompiles_mint_burn() { 1_000 * GLMR ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12957u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(BOB), - H160::default(), - EvmDataWriter::new().write(U256::from(500 * GLMR)).build(), - ) - .build(), - })); - - // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + // Burn tokens + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Burn) + EvmDataWriter::new_with_selector(AssetAction::Burn) .write(EvmAddress(BOB.into())) .write(U256::from(500 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12957) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(BOB), + H160::default(), + EvmDataWriter::new().write(U256::from(500 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been burnt assert_eq!(LocalAssets::total_supply(0u128), 1_500 * GLMR); @@ -1836,33 +1661,20 @@ fn asset_erc20_precompiles_freeze_thaw_account() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6732u64, - logs: Default::default(), - })); - - // Freeze Account - assert_eq!( - Precompiles::new().execute( + // Freeze account + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Freeze) + EvmDataWriter::new_with_selector(AssetAction::Freeze) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6732) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1870,31 +1682,18 @@ fn asset_erc20_precompiles_freeze_thaw_account() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6731u64, - logs: Default::default(), - })); - // Thaw Account - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Thaw) + EvmDataWriter::new_with_selector(AssetAction::Thaw) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6731) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is not frozen assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) @@ -1918,31 +1717,18 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5589u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Freeze Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), + ) + .expect_cost(5589) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1950,29 +1736,16 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5593u64, - logs: Default::default(), - })); - // Thaw Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), + ) + .expect_cost(5593) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is not frozen assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) @@ -1996,33 +1769,20 @@ fn asset_erc20_precompiles_freeze_transfer_ownership() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6666u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Transfer ownerhsip of an asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) + EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6666) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // No clear way of testing BOB is new owner, other than doing a priviledged function // e.g., transfer_ownership again @@ -2049,35 +1809,22 @@ fn asset_erc20_precompiles_freeze_set_team() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5614u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Set Bob as issuer, admin and freezer - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::SetTeam) + EvmDataWriter::new_with_selector(AssetAction::SetTeam) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(5614) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Bob should be able to mint, freeze, and thaw assert_ok!(LocalAssets::mint( @@ -2127,54 +1874,34 @@ fn xcm_asset_erc20_precompiles_supply_and_balance() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Assert the asset has been created with the correct supply assert_eq!(Assets::total_supply(relay_asset_id), 1_000 * GLMR); - // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * GLMR)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * GLMR)).build()); }); } @@ -2206,68 +1933,39 @@ fn xcm_asset_erc20_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + ); // Transfer tokens from Aice to Bob, 400 GLMR. - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(23516) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure BOB has 400 GLMR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * GLMR)).build()); }); } @@ -2299,103 +1997,59 @@ fn xcm_asset_erc20_precompiles_approve() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + ); // Aprove Bob for spending 400 GLMR from Alice - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * GLMR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * GLMR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure CHARLIE has 400 GLMR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * GLMR)).build()); }); } @@ -2430,8 +2084,7 @@ fn xtokens_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Alice has 1000 tokens. She should be able to send through precompile let destination = MultiLocation::new( @@ -2442,31 +2095,21 @@ fn xtokens_precompiles_transfer() { }), ); - // We use the address of the asset as an identifier of the asset we want to transferS - assert_eq!( - Precompiles::new().execute( + // We use the address of the asset as an identifier of the asset we want to transfer + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::Transfer) - .write(EvmAddress(asset_precompile_address)) + EvmDataWriter::new_with_selector(XtokensAction::Transfer) + .write(EvmAddress(asset_precompile_address.into())) .write(U256::from(500_000_000_000_000u128)) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 20000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(20000) + .expect_no_logs() + .execute_returns(vec![]) }) } @@ -2504,31 +2147,21 @@ fn xtokens_precompiles_transfer_multiasset() { // This time we transfer it through TransferMultiAsset // Instead of the address, we encode directly the multilocation referencing the asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) + EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) // We want to transfer the relay token .write(MultiLocation::parent()) .write(U256::from(500_000_000_000_000u128)) .write(destination) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 20000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(20000) + .expect_no_logs() + .execute_returns(vec![]); }) } @@ -2778,17 +2411,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_some(), "execute({},..) should return Some(_)", i @@ -2802,17 +2432,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_none(), "execute({},..) should return None", i diff --git a/runtime/moonriver/src/precompiles.rs b/runtime/moonriver/src/precompiles.rs index e49be1eb2ae..480e7758d12 100644 --- a/runtime/moonriver/src/precompiles.rs +++ b/runtime/moonriver/src/precompiles.rs @@ -16,7 +16,7 @@ use crate::asset_config::{ForeignAssetInstance, LocalAssetInstance}; use crowdloan_rewards_precompiles::CrowdloanRewardsWrapper; -use fp_evm::Context; +use fp_evm::PrecompileHandle; use moonbeam_relay_encoder::kusama::KusamaEncoder; use pallet_author_mapping_precompiles::AuthorMappingWrapper; use pallet_democracy_precompiles::DemocracyWrapper; @@ -113,71 +113,43 @@ where AuthorMappingWrapper: Precompile, R: pallet_evm::Config, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option { - match address { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + match handle.code_address() { // Ethereum precompiles : - a if a == hash(1) => Some(ECRecover::execute(input, target_gas, context, is_static)), - a if a == hash(2) => Some(Sha256::execute(input, target_gas, context, is_static)), - a if a == hash(3) => Some(Ripemd160::execute(input, target_gas, context, is_static)), - a if a == hash(4) => Some(Identity::execute(input, target_gas, context, is_static)), - a if a == hash(5) => Some(Modexp::execute(input, target_gas, context, is_static)), - a if a == hash(6) => Some(Bn128Add::execute(input, target_gas, context, is_static)), - a if a == hash(7) => Some(Bn128Mul::execute(input, target_gas, context, is_static)), - a if a == hash(8) => Some(Bn128Pairing::execute(input, target_gas, context, is_static)), - a if a == hash(9) => Some(Blake2F::execute(input, target_gas, context, is_static)), + a if a == hash(1) => Some(ECRecover::execute(handle)), + a if a == hash(2) => Some(Sha256::execute(handle)), + a if a == hash(3) => Some(Ripemd160::execute(handle)), + a if a == hash(4) => Some(Identity::execute(handle)), + a if a == hash(5) => Some(Modexp::execute(handle)), + a if a == hash(6) => Some(Bn128Add::execute(handle)), + a if a == hash(7) => Some(Bn128Mul::execute(handle)), + a if a == hash(8) => Some(Bn128Pairing::execute(handle)), + a if a == hash(9) => Some(Blake2F::execute(handle)), + // Non-Moonbeam specific nor Ethereum precompiles : - a if a == hash(1024) => { - Some(Sha3FIPS256::execute(input, target_gas, context, is_static)) - } - a if a == hash(1025) => Some(Dispatch::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(1026) => Some(ECRecoverPublicKey::execute( - input, target_gas, context, is_static, - )), + a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), + a if a == hash(1025) => Some(Dispatch::::execute(handle)), + a if a == hash(1026) => Some(ECRecoverPublicKey::execute(handle)), + // Moonbeam specific precompiles : - a if a == hash(2048) => Some(ParachainStakingWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2050) => { - Some(Erc20BalancesPrecompile::::execute( - input, target_gas, context, is_static, - )) - } - a if a == hash(2051) => Some(DemocracyWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2052) => Some(XtokensWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2053) => Some(RelayEncoderWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2054) => Some(XcmTransactorWrapper::::execute( - input, target_gas, context, is_static, - )), - a if a == hash(2055) => Some(AuthorMappingWrapper::::execute( - input, target_gas, context, is_static, - )), + a if a == hash(2048) => Some(ParachainStakingWrapper::::execute(handle)), + a if a == hash(2049) => Some(CrowdloanRewardsWrapper::::execute(handle)), + a if a == hash(2050) => Some( + Erc20BalancesPrecompile::::execute(handle), + ), + a if a == hash(2051) => Some(DemocracyWrapper::::execute(handle)), + a if a == hash(2052) => Some(XtokensWrapper::::execute(handle)), + a if a == hash(2053) => Some(RelayEncoderWrapper::::execute(handle)), + a if a == hash(2054) => Some(XcmTransactorWrapper::::execute(handle)), + a if a == hash(2055) => Some(AuthorMappingWrapper::::execute(handle)), // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + .execute(handle) } // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX => { - Erc20AssetsPrecompileSet::::new() - .execute(address, input, target_gas, context, is_static) + Erc20AssetsPrecompileSet::::new().execute(handle) } _ => None, } diff --git a/runtime/moonriver/tests/integration_test.rs b/runtime/moonriver/tests/integration_test.rs index c57ea17670c..cedd309d422 100644 --- a/runtime/moonriver/tests/integration_test.rs +++ b/runtime/moonriver/tests/integration_test.rs @@ -21,7 +21,8 @@ mod common; use common::*; -use fp_evm::{Context, ExitSucceed, PrecompileOutput}; +use crowdloan_rewards_precompiles::Action as CrowdloanAction; +use fp_evm::Context; use frame_support::{ assert_noop, assert_ok, dispatch::Dispatchable, @@ -44,7 +45,7 @@ use pallet_evm_precompile_assets_erc20::{ }; use pallet_transaction_payment::Multiplier; use parity_scale_codec::Encode; -use precompile_utils::{Address as EvmAddress, EvmDataWriter, LogsBuilder}; +use precompile_utils::{testing::*, Address as EvmAddress, EvmDataWriter, LogsBuilder}; use sha3::{Digest, Keccak256}; use sp_core::{ByteArray, Pair, H160, U256}; use sp_runtime::{ @@ -839,71 +840,31 @@ fn is_contributor_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut bob_input_data = Vec::::from([0u8; 36]); - bob_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - bob_input_data[16..36].copy_from_slice(&BOB); - - // Expected result is an EVM boolean false which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 0; - let expected_false_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &bob_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_false_result - ); - - // Construct the input data to check if Charlie is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"is_contributor(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - - // Expected result is an EVM boolean true which is 256 bits long. - let mut expected_bytes = Vec::from([0u8; 32]); - expected_bytes[31] = 1; - let expected_true_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is a nominator - assert_eq!( - Precompiles::new().execute( + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(AccountId::from(BOB).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(false).build()); + + // Assert precompile reports Charlie is a nominator + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_true_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::IsContributor) + .write(EvmAddress(AccountId::from(CHARLIE).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); }) } @@ -959,42 +920,26 @@ fn reward_info_via_precompile() { let crowdloan_precompile_address = H160::from_low_u64_be(2049); - // Construct the input data to check if Bob is a contributor - let mut charlie_input_data = Vec::::from([0u8; 36]); - charlie_input_data[0..4] - .copy_from_slice(&Keccak256::digest(b"reward_info(address)")[0..4]); - charlie_input_data[16..36].copy_from_slice(&CHARLIE); - let expected_total: U256 = (1_500_000 * MOVR).into(); let expected_claimed: U256 = (450_000 * MOVR).into(); - // Expected result is two EVM u256 false which are 256 bits long. - let mut expected_bytes = Vec::from([0u8; 64]); - expected_total.to_big_endian(&mut expected_bytes[0..32]); - expected_claimed.to_big_endian(&mut expected_bytes[32..64]); - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: expected_bytes, - cost: 1000, - logs: Default::default(), - })); - - // Assert precompile reports Bob is not a contributor - assert_eq!( - Precompiles::new().execute( + // Assert precompile reports correct Charlie reward info. + Precompiles::new() + .prepare_test( + ALICE, crowdloan_precompile_address, - &charlie_input_data, - None, // target_gas is not necessary right now because consumed none now - &Context { - // This context copied from Sacrifice tests, it's not great. - address: Default::default(), - caller: Default::default(), - apparent_value: U256::zero(), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(CrowdloanAction::RewardInfo) + .write(EvmAddress(AccountId::from(CHARLIE).into())) + .build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns( + EvmDataWriter::new() + .write(expected_total) + .write(expected_claimed) + .build(), + ); }) } @@ -1468,50 +1413,34 @@ fn asset_erc20_precompiles_supply_and_balance() { // Convert the assetId to its corresponding precompile address let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); + // holds balance. - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + // Access totalSupply through precompile. + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * MOVR)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * MOVR)).build()); }); } @@ -1530,67 +1459,39 @@ fn asset_erc20_precompiles_transfer() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Transfer tokens from Aice to Bob, 400 MOVR. - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(23516u64) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure BOB has 400 MOVR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * MOVR)).build()); }); } @@ -1609,102 +1510,59 @@ fn asset_erc20_precompiles_approve() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Aprove Bob for spending 400 MOVR from Alice - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure CHARLIE has 400 MOVR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * MOVR)).build()); }); } @@ -1723,41 +1581,26 @@ fn asset_erc20_precompiles_mint_burn() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12821u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::default(), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(1000 * MOVR)).build(), - ) - .build(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Mint 1000 MOVRS to BOB - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Mint) + EvmDataWriter::new_with_selector(AssetAction::Mint) .write(EvmAddress(BOB.into())) .write(U256::from(1000 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12821) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::default(), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(1000 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been minted assert_eq!(LocalAssets::total_supply(0u128), 2_000 * MOVR); @@ -1766,39 +1609,24 @@ fn asset_erc20_precompiles_mint_burn() { 1_000 * MOVR ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 12957u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(BOB), - H160::default(), - EvmDataWriter::new().write(U256::from(500 * MOVR)).build(), - ) - .build(), - })); - - // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + // Burn tokens + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Burn) + EvmDataWriter::new_with_selector(AssetAction::Burn) .write(EvmAddress(BOB.into())) .write(U256::from(500 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(12957) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(BOB), + H160::default(), + EvmDataWriter::new().write(U256::from(500 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert the asset has been burnt assert_eq!(LocalAssets::total_supply(0u128), 1_500 * MOVR); @@ -1824,33 +1652,20 @@ fn asset_erc20_precompiles_freeze_thaw_account() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6732u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Freeze Account - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Freeze) + EvmDataWriter::new_with_selector(AssetAction::Freeze) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6732) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1858,31 +1673,18 @@ fn asset_erc20_precompiles_freeze_thaw_account() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6731u64, - logs: Default::default(), - })); - // Thaw Account - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Thaw) + EvmDataWriter::new_with_selector(AssetAction::Thaw) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6731) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is not frozen assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) @@ -1906,31 +1708,18 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5589u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Freeze Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::FreezeAsset).build(), + ) + .expect_cost(5589) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is frozen assert_eq!( @@ -1938,29 +1727,16 @@ fn asset_erc20_precompiles_freeze_thaw_asset() { Err(TokenError::Frozen.into()) ); - // Expected result for burn - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5593u64, - logs: Default::default(), - })); - // Thaw Asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::ThawAsset).build(), + ) + .expect_cost(5593) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Assert account is not frozen assert!(LocalAssets::can_withdraw(0u128, &AccountId::from(ALICE), 1) @@ -1984,33 +1760,20 @@ fn asset_erc20_precompiles_freeze_transfer_ownership() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 6666u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Transfer ownerhsip of an asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) + EvmDataWriter::new_with_selector(AssetAction::TransferOwnership) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(6666) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // No clear way of testing BOB is new owner, other than doing a priviledged function // e.g., transfer_ownership again @@ -2037,35 +1800,22 @@ fn asset_erc20_precompiles_freeze_set_team() { .build() .execute_with(|| { let asset_precompile_address = - Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128).into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 5614u64, - logs: Default::default(), - })); + Runtime::asset_id_to_account(LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX, 0u128); // Set Bob as issuer, admin and freezer - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::SetTeam) + EvmDataWriter::new_with_selector(AssetAction::SetTeam) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(5614) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(true).build()); // Bob should be able to mint, freeze, and thaw assert_ok!(LocalAssets::mint( @@ -2114,54 +1864,34 @@ fn xcm_asset_erc20_precompiles_supply_and_balance() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Assert the asset has been created with the correct supply assert_eq!(Assets::total_supply(relay_asset_id), 1_000 * MOVR); - // The expected result for both total supply and balance of is the same, as only Alice - // holds balance - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(1000 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); - // Access totalSupply through precompile. Important that the context is correct - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + EvmDataWriter::new_with_selector(AssetAction::TotalSupply).build(), + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * MOVR)).build()); // Access balanceOf through precompile - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(ALICE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(1000 * MOVR)).build()); }); } @@ -2192,68 +1922,39 @@ fn xcm_asset_erc20_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for a transfer - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 23516u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + ); // Transfer tokens from Aice to Bob, 400 MOVR. - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Transfer) + EvmDataWriter::new_with_selector(AssetAction::Transfer) .write(EvmAddress(BOB.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balanceOf BOB - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(23516) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure BOB has 400 MOVR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(BOB.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * MOVR)).build()); }); } @@ -2284,103 +1985,59 @@ fn xcm_asset_erc20_precompiles_approve() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); - - // Expected result for approve - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 13989u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_APPROVAL, - H160::from(ALICE), - H160::from(BOB), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + ); // Aprove Bob for spending 400 MOVR from Alice - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::Approve) + EvmDataWriter::new_with_selector(AssetAction::Approve) .write(EvmAddress(BOB.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for transfer_from - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(true).build(), - cost: 29006u64, - logs: LogsBuilder::new(asset_precompile_address) - .log3( - SELECTOR_LOG_TRANSFER, - H160::from(ALICE), - H160::from(CHARLIE), - EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - ) - .build(), - })); + ) + .expect_cost(13989) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_APPROVAL, + H160::from(ALICE), + H160::from(BOB), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Transfer tokens from Alice to Charlie by using BOB as origin - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + BOB, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::TransferFrom) + EvmDataWriter::new_with_selector(AssetAction::TransferFrom) .write(EvmAddress(ALICE.into())) .write(EvmAddress(CHARLIE.into())) .write(U256::from(400 * MOVR)) .build(), - None, - &Context { - address: asset_precompile_address, - caller: BOB.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); - - // Expected result for balance of CHARLIE - let expected_result = Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), - cost: 1000, - logs: Default::default(), - })); + ) + .expect_cost(29006) + .expect_log(LogsBuilder::new(asset_precompile_address.into()).log3( + SELECTOR_LOG_TRANSFER, + H160::from(ALICE), + H160::from(CHARLIE), + EvmDataWriter::new().write(U256::from(400 * MOVR)).build(), + )) + .execute_returns(EvmDataWriter::new().write(true).build()); // Make sure CHARLIE has 400 MOVR - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + CHARLIE, asset_precompile_address, - &EvmDataWriter::new_with_selector(AssetAction::BalanceOf) + EvmDataWriter::new_with_selector(AssetAction::BalanceOf) .write(EvmAddress(CHARLIE.into())) .build(), - None, - &Context { - address: asset_precompile_address, - caller: CHARLIE.into(), - apparent_value: From::from(0), - }, - false, - ), - expected_result - ); + ) + .expect_cost(1000) + .expect_no_logs() + .execute_returns(EvmDataWriter::new().write(U256::from(400 * MOVR)).build()); }); } @@ -2415,8 +2072,7 @@ fn xtokens_precompiles_transfer() { let asset_precompile_address = Runtime::asset_id_to_account( FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, relay_asset_id, - ) - .into(); + ); // Alice has 1000 tokens. She should be able to send through precompile let destination = MultiLocation::new( @@ -2428,30 +2084,20 @@ fn xtokens_precompiles_transfer() { ); // We use the address of the asset as an identifier of the asset we want to transferS - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::Transfer) - .write(EvmAddress(asset_precompile_address)) + EvmDataWriter::new_with_selector(XtokensAction::Transfer) + .write(EvmAddress(asset_precompile_address.into())) .write(U256::from(500_000_000_000_000u128)) .write(destination.clone()) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 20000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(20000) + .expect_no_logs() + .execute_returns(vec![]) }) } @@ -2489,31 +2135,21 @@ fn xtokens_precompiles_transfer_multiasset() { // This time we transfer it through TransferMultiAsset // Instead of the address, we encode directly the multilocation referencing the asset - assert_eq!( - Precompiles::new().execute( + Precompiles::new() + .prepare_test( + ALICE, xtokens_precompile_address, - &EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) + EvmDataWriter::new_with_selector(XtokensAction::TransferMultiAsset) // We want to transfer the relay token .write(MultiLocation::parent()) .write(U256::from(500_000_000_000_000u128)) .write(destination) .write(U256::from(4000000)) .build(), - None, - &Context { - address: xtokens_precompile_address, - caller: ALICE.into(), - apparent_value: From::from(0), - }, - false, - ), - Some(Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - cost: 20000, - output: vec![], - logs: vec![] - })) - ); + ) + .expect_cost(20000) + .expect_no_logs() + .execute_returns(vec![]); }) } @@ -2697,17 +2333,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_some(), "execute({},..) should return Some(_)", i @@ -2721,17 +2354,14 @@ fn precompile_existence() { assert!( precompiles - .execute( + .execute(&mut MockHandle::new( address, - &vec![], - None, - &Context { + Context { address, caller: H160::zero(), apparent_value: U256::zero() - }, - false - ) + } + ),) .is_none(), "execute({},..) should return None", i diff --git a/scripts/build-last-tracing-runtime.sh b/scripts/build-last-tracing-runtime.sh index 51ce137de39..84e9a3acc85 100755 --- a/scripts/build-last-tracing-runtime.sh +++ b/scripts/build-last-tracing-runtime.sh @@ -11,6 +11,5 @@ git clone --depth 1 -b master-without-wasm https://github.com/PureStake/moonbeam cd build/moonbeam-runtime-overrides ./scripts/import-tracing-runtime.sh local ${1:-"$LOCAL_GIT_BRANCH"} -cd tracing/local && cargo update -p evm && cd ../.. ./scripts/build-tracing-runtime.sh local moonbase mv wasm/moonbase-runtime-local-substitute-tracing.wasm ../wasm/moonbase-runtime-local-substitute-tracing.wasm