From 97bfc236ef6a0023aa0eadb9d55c38208f4cf00e Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Mon, 29 Jan 2024 10:33:35 +0800 Subject: [PATCH] Update evm and switch to shanghai hardfork (#2691) * update PrecompileHandle ref: https://github.com/rust-ethereum/evm/pull/122 * update fee calculation ref: https://github.com/rust-ethereum/evm/pull/132 * add code_size/code_hash fn in StackState trait ref: https://github.com/rust-ethereum/evm/pull/140 * update evm call stack ref: https://github.com/rust-ethereum/evm/pull/136 * update evm call stack ref: https://github.com/rust-ethereum/evm/pull/155 * add shanghai eips 3651, 3855, 3860 ref: https://github.com/rust-ethereum/evm/pull/152 * update is_precompile ref: https://github.com/rust-ethereum/evm/pull/157 * fix eip-3860 ref: https://github.com/rust-ethereum/evm/pull/160 * update runtime config ref: https://github.com/rust-ethereum/evm/pull/161 * add eip-4399 ref: https://github.com/rust-ethereum/evm/pull/162 * fix eip-2618 ref: https://github.com/rust-ethereum/evm/pull/163 * fix nonce back to U256 ref: https://github.com/rust-ethereum/evm/pull/166 * remove exit_substate in create functions ref: https://github.com/rust-ethereum/evm/pull/168 * record external cost ref: https://github.com/rust-ethereum/evm/pull/170 * add record_external_operation ref: https://github.com/rust-ethereum/evm/pull/171 * add storage_growth ref: https://github.com/rust-ethereum/evm/pull/173 * update evm * switch to shanghai hardfork * update ecrecover ref: https://github.com/polkadot-evm/frontier/pull/964 (#2696) --- Cargo.lock | 62 +- evm-bench | 2 +- evm-tests | 2 +- modules/asset-registry/src/mock.rs | 4 +- modules/currencies/src/mock.rs | 2 +- modules/evm-bridge/src/mock.rs | 6 +- modules/evm-utility/Cargo.toml | 8 +- modules/evm/src/bench/mod.rs | 8 +- modules/evm/src/lib.rs | 23 +- modules/evm/src/precompiles/blake2/mod.rs | 72 +- modules/evm/src/precompiles/bn128.rs | 99 ++- modules/evm/src/precompiles/ecrecover.rs | 20 +- .../src/precompiles/ecrecover_publickey.rs | 2 +- modules/evm/src/precompiles/identity.rs | 2 +- modules/evm/src/precompiles/mod.rs | 137 ++- modules/evm/src/precompiles/modexp.rs | 204 +++-- modules/evm/src/precompiles/ripemd.rs | 2 +- modules/evm/src/precompiles/sha256.rs | 2 +- modules/evm/src/precompiles/sha3fips.rs | 2 +- modules/evm/src/runner/mod.rs | 1 + modules/evm/src/runner/stack.rs | 19 +- modules/evm/src/runner/state.rs | 784 +++++++++++++----- modules/evm/src/runner/tagged_runtime.rs | 33 + modules/evm/src/tests.rs | 26 +- modules/honzon-bridge/src/mock.rs | 2 +- primitives/src/evm.rs | 2 + runtime/common/src/bench/mod.rs | 19 +- runtime/common/src/precompile/dex.rs | 89 +- runtime/common/src/precompile/evm.rs | 70 +- runtime/common/src/precompile/evm_accounts.rs | 58 +- runtime/common/src/precompile/homa.rs | 61 +- runtime/common/src/precompile/honzon.rs | 62 +- runtime/common/src/precompile/incentives.rs | 84 +- runtime/common/src/precompile/input.rs | 54 +- .../common/src/precompile/liquid_crowdloan.rs | 53 +- runtime/common/src/precompile/mod.rs | 127 ++- .../common/src/precompile/multicurrency.rs | 121 ++- runtime/common/src/precompile/nft.rs | 42 +- runtime/common/src/precompile/oracle.rs | 56 +- runtime/common/src/precompile/schedule.rs | 69 +- runtime/common/src/precompile/stable_asset.rs | 174 ++-- runtime/common/src/precompile/tests.rs | 36 +- runtime/common/src/precompile/xtokens.rs | 104 +-- runtime/integration-tests/src/evm.rs | 8 +- runtime/integration-tests/src/honzon.rs | 4 +- runtime/mandala/src/benchmarking/evm.rs | 4 +- runtime/mandala/src/lib.rs | 4 +- ts-tests/tests/test-gas.ts | 2 +- 48 files changed, 1565 insertions(+), 1262 deletions(-) create mode 100644 modules/evm/src/runner/tagged_runtime.rs diff --git a/Cargo.lock b/Cargo.lock index 50eeab6b4b..58253beee0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1035,7 +1035,7 @@ version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a399848a68a5196a04c19db5bfc4dca3cd0989a3165150f06c1ad1bc8882aa34" dependencies = [ - "hash-db 0.16.0", + "hash-db", "log", ] @@ -3249,20 +3249,20 @@ dependencies = [ [[package]] name = "ethereum" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" +checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" dependencies = [ "bytes", "ethereum-types", - "hash-db 0.15.2", + "hash-db", "hash256-std-hasher", "parity-scale-codec", "rlp", "scale-info", "serde", "sha3", - "triehash", + "trie-root", ] [[package]] @@ -3332,8 +3332,9 @@ dependencies = [ [[package]] name = "evm" -version = "0.36.0" -source = "git+https://github.com/rust-blockchain/evm?rev=13240a8a551586fdef0b5028ed73af80b248092a#13240a8a551586fdef0b5028ed73af80b248092a" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" dependencies = [ "auto_impl", "environmental", @@ -3352,8 +3353,9 @@ dependencies = [ [[package]] name = "evm-core" -version = "0.36.0" -source = "git+https://github.com/rust-blockchain/evm?rev=13240a8a551586fdef0b5028ed73af80b248092a#13240a8a551586fdef0b5028ed73af80b248092a" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", "primitive-types", @@ -3363,8 +3365,9 @@ dependencies = [ [[package]] name = "evm-gasometer" -version = "0.36.0" -source = "git+https://github.com/rust-blockchain/evm?rev=13240a8a551586fdef0b5028ed73af80b248092a#13240a8a551586fdef0b5028ed73af80b248092a" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc0eb591abc5cd7b05bef6a036c2bb6c66ab6c5e0c5ce94bfe377ab670b1fd7" dependencies = [ "environmental", "evm-core", @@ -3410,8 +3413,9 @@ dependencies = [ [[package]] name = "evm-runtime" -version = "0.36.0" -source = "git+https://github.com/rust-blockchain/evm?rev=13240a8a551586fdef0b5028ed73af80b248092a#13240a8a551586fdef0b5028ed73af80b248092a" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" dependencies = [ "auto_impl", "environmental", @@ -4283,12 +4287,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - [[package]] name = "hash-db" version = "0.16.0" @@ -6093,7 +6091,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "hash-db 0.16.0", + "hash-db", ] [[package]] @@ -11850,7 +11848,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ced79f609a44782874d856cf39d256838957195ef34f4fb8ced90bf4b725d0" dependencies = [ - "hash-db 0.16.0", + "hash-db", "kvdb", "kvdb-memorydb", "kvdb-rocksdb", @@ -13457,7 +13455,7 @@ version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f582f92ce47c86e4ffffe81fdd5120fea7c850dc0800653a7fa203bcc1532335" dependencies = [ - "hash-db 0.16.0", + "hash-db", "log", "parity-scale-codec", "scale-info", @@ -13682,7 +13680,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra 3.1.0", "futures", - "hash-db 0.16.0", + "hash-db", "hash256-std-hasher", "impl-serde", "lazy_static", @@ -14044,7 +14042,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96e087fa4430befd2047b61d912c9d6fa4eaed408c4b58b46c6e9acd7965f2d3" dependencies = [ - "hash-db 0.16.0", + "hash-db", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -14165,7 +14163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e359b358263cc322c3f678c272a3a519621d9853dcfa1374dfcbdb5f54c6f85" dependencies = [ "ahash 0.8.7", - "hash-db 0.16.0", + "hash-db", "hashbrown 0.13.2", "lazy_static", "memory-db", @@ -15174,7 +15172,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff28e0f815c2fea41ebddf148e008b077d2faddb026c9555b29696114d602642" dependencies = [ - "hash-db 0.16.0", + "hash-db", "hashbrown 0.13.2", "log", "rustc-hex", @@ -15187,17 +15185,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "hash-db 0.16.0", -] - -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db 0.15.2", - "rlp", + "hash-db", ] [[package]] diff --git a/evm-bench b/evm-bench index 600de23996..0c0fa3380a 160000 --- a/evm-bench +++ b/evm-bench @@ -1 +1 @@ -Subproject commit 600de2399601cec07b59342ab5e53e7b2d5237d7 +Subproject commit 0c0fa3380a3fa8d2bf70216f9ce2eddaf9f1b17e diff --git a/evm-tests b/evm-tests index 767a1f025d..a214e67035 160000 --- a/evm-tests +++ b/evm-tests @@ -1 +1 @@ -Subproject commit 767a1f025d74e383108ba5e0c778b65350e88a1a +Subproject commit a214e6703549208c9168a26b1e107ff8519ec4a6 diff --git a/modules/asset-registry/src/mock.rs b/modules/asset-registry/src/mock.rs index a52a177df7..f52532cf1e 100644 --- a/modules/asset-registry/src/mock.rs +++ b/modules/asset-registry/src/mock.rs @@ -201,7 +201,7 @@ pub fn deploy_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 5131, })); @@ -242,7 +242,7 @@ pub fn deploy_contracts_same_prefix() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 5131, })); diff --git a/modules/currencies/src/mock.rs b/modules/currencies/src/mock.rs index 676ca53e89..1e6166e7a0 100644 --- a/modules/currencies/src/mock.rs +++ b/modules/currencies/src/mock.rs @@ -303,7 +303,7 @@ pub fn deploy_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 5131, })); diff --git a/modules/evm-bridge/src/mock.rs b/modules/evm-bridge/src/mock.rs index bf47794cab..be632d40e1 100644 --- a/modules/evm-bridge/src/mock.rs +++ b/modules/evm-bridge/src/mock.rs @@ -203,7 +203,7 @@ pub fn deploy_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 5131, })); @@ -230,7 +230,7 @@ pub fn deploy_liquidation_ok_contracts() { from: alice_evm_addr(), contract: erc20_address(), logs: vec![], - used_gas: 235274, + used_gas: 235330, used_storage: 844, })); @@ -257,7 +257,7 @@ pub fn deploy_liquidation_err_contracts() { from: alice_evm_addr(), contract: erc20_address(), logs: vec![], - used_gas: 228284, + used_gas: 228338, used_storage: 818, })); diff --git a/modules/evm-utility/Cargo.toml b/modules/evm-utility/Cargo.toml index e995b74160..6cd409a0ae 100644 --- a/modules/evm-utility/Cargo.toml +++ b/modules/evm-utility/Cargo.toml @@ -9,10 +9,10 @@ sha3 = { workspace = true } sp-std = { workspace = true } -evm = { git = "https://github.com/rust-blockchain/evm", rev = "13240a8a551586fdef0b5028ed73af80b248092a", default-features = false, features = ["with-codec"] } -evm-gasometer = { git = "https://github.com/rust-blockchain/evm", rev = "13240a8a551586fdef0b5028ed73af80b248092a", default-features = false } -evm-runtime = { git = "https://github.com/rust-blockchain/evm", rev = "13240a8a551586fdef0b5028ed73af80b248092a", default-features = false } -ethereum = { version = "0.14.0", default-features = false, features = ["with-codec"] } +evm = { version = "0.41.1", default-features = false, features = ["with-codec"] } +evm-gasometer = { version = "0.41.0", default-features = false } +evm-runtime = { version = "0.41.0", default-features = false } +ethereum = { version = "0.15.0", default-features = false, features = ["with-codec"] } [features] default = ["std"] diff --git a/modules/evm/src/bench/mod.rs b/modules/evm/src/bench/mod.rs index 4c0fa5c54e..d3c54bc773 100644 --- a/modules/evm/src/bench/mod.rs +++ b/modules/evm/src/bench/mod.rs @@ -80,7 +80,13 @@ fn whitelist_keys(b: &mut Bencher, from: H160, code: Vec) -> H160 { let state = SubstrateStackState::::new(&vicinity, metadata); let mut executor = StackExecutor::new_with_precompiles(state, config, &()); - let mut runtime = EVMRuntime::new(Rc::new(code.clone()), Rc::new(Vec::new()), context, config); + let mut runtime = EVMRuntime::new( + Rc::new(code.clone()), + Rc::new(Vec::new()), + context, + config.stack_limit, + config.memory_limit, + ); let reason = executor.execute(&mut runtime); assert!(reason.is_succeed(), "{:?}", reason); diff --git a/modules/evm/src/lib.rs b/modules/evm/src/lib.rs index e3b7a5a8f9..b40a625c29 100644 --- a/modules/evm/src/lib.rs +++ b/modules/evm/src/lib.rs @@ -24,7 +24,7 @@ pub use crate::runner::{ stack::SubstrateStackState, - state::{PrecompileSet, StackExecutor, StackSubstateMetadata}, + state::{PrecompileResult, StackExecutor, StackSubstateMetadata}, storage_meter::StorageMeter, Runner, }; @@ -46,7 +46,11 @@ use frame_system::{ensure_root, ensure_signed, pallet_prelude::*, EnsureRoot, En use hex_literal::hex; pub use module_evm_utility::{ ethereum::{AccessListItem, Log, TransactionAction}, - evm::{self, Config as EvmConfig, Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed}, + evm::{ + self, + executor::stack::{IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet}, + Config as EvmConfig, Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, ExternalOperation, + }, Account, }; pub use module_support::{ @@ -100,17 +104,17 @@ pub type NegativeImbalanceOf = pub const RESERVE_ID_STORAGE_DEPOSIT: ReserveIdentifier = ReserveIdentifier::EvmStorageDeposit; pub const RESERVE_ID_DEVELOPER_DEPOSIT: ReserveIdentifier = ReserveIdentifier::EvmDeveloperDeposit; -// Initially based on London hard fork configuration. +// Initially based on shanghai hard fork configuration. static ACALA_CONFIG: EvmConfig = EvmConfig { refund_sstore_clears: 0, // no gas refund sstore_gas_metering: false, // no gas refund sstore_revert_under_stipend: false, // ignored create_contract_limit: Some(MaxCodeSize::get() as usize), - ..module_evm_utility::evm::Config::london() + ..module_evm_utility::evm::Config::shanghai() }; /// Create an empty contract `contract Empty { }`. -pub const BASE_CREATE_GAS: u64 = 67_066; +pub const BASE_CREATE_GAS: u64 = 67_072; /// Call function that just set a storage `function store(uint256 num) public { number = num; }`. pub const BASE_CALL_GAS: u64 = 43_702; @@ -410,8 +414,13 @@ pub mod module { let state = SubstrateStackState::::new(&vicinity, metadata); let mut executor = StackExecutor::new_with_precompiles(state, T::config(), &()); - let mut runtime = - evm::Runtime::new(Rc::new(account.code.clone()), Rc::new(Vec::new()), context, T::config()); + let mut runtime = evm::Runtime::new( + Rc::new(account.code.clone()), + Rc::new(Vec::new()), + context, + T::config().stack_limit, + T::config().memory_limit, + ); let reason = executor.execute(&mut runtime); assert!( diff --git a/modules/evm/src/precompiles/blake2/mod.rs b/modules/evm/src/precompiles/blake2/mod.rs index 8f1dbba808..52ba4e47b8 100644 --- a/modules/evm/src/precompiles/blake2/mod.rs +++ b/modules/evm/src/precompiles/blake2/mod.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . use super::Precompile; -use crate::runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}; -use module_evm_utility::evm::{Context, ExitError, ExitSucceed}; +use crate::{PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult}; +use module_evm_utility::evm::{ExitError, ExitSucceed}; mod eip_152; @@ -32,9 +32,11 @@ impl Precompile for Blake2F { /// Format of `input`: /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 /// byte for f] - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { const BLAKE2_F_ARG_LEN: usize = 213; + let input = handle.input(); + if input.len() != BLAKE2_F_ARG_LEN { return Err(PrecompileFailure::Error { exit_status: ExitError::Other( @@ -48,13 +50,9 @@ impl Precompile for Blake2F { let rounds: u32 = u32::from_be_bytes(rounds_buf); let gas_cost: u64 = (rounds as u64) * Blake2F::GAS_COST_PER_ROUND; - if let Some(gas_left) = target_gas { - if gas_left < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } + handle.record_cost(gas_cost)?; + + let input = handle.input(); // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE @@ -107,17 +105,31 @@ impl Precompile for Blake2F { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: output_buf.to_vec(), - logs: Default::default(), }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } #[cfg(test)] mod tests { use super::*; + use crate::precompiles::tests::MockPrecompileHandle; + use frame_support::assert_ok; use hex_literal::hex; + use module_evm_utility::evm::Context; use sp_core::U256; fn get_context() -> Context { @@ -132,8 +144,10 @@ mod tests { fn blake2f_cost() { // 5 rounds let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let result = Blake2F::execute(&input[..], None, &get_context(), false).unwrap(); - assert_eq!(result.cost, 5); + let context = get_context(); + let mut mock_handle = MockPrecompileHandle::new(&input[..], None, &context, false); + assert_ok!(Blake2F::execute(&mut mock_handle)); + assert_eq!(mock_handle.gas_used, 5); } #[test] @@ -144,15 +158,24 @@ mod tests { // invalid input (too short) let input = hex!("00"); - assert_eq!(Blake2F::execute(&input[..], None, &get_context(), false), err); + assert_eq!( + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), + err + ); // Test vector 1 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-1 let input = hex!("00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - assert_eq!(Blake2F::execute(&input[..], None, &get_context(), false), err); + assert_eq!( + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), + err + ); // Test vector 2 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-2 let input = hex!("000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - assert_eq!(Blake2F::execute(&input[..], None, &get_context(), false), err); + assert_eq!( + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), + err + ); } #[test] @@ -163,7 +186,10 @@ mod tests { // Test vector 3 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-3 let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002"); - assert_eq!(Blake2F::execute(&input[..], None, &get_context(), false), err); + assert_eq!( + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), + err + ); } #[test] @@ -172,7 +198,7 @@ mod tests { let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); assert_eq!( - Blake2F::execute(&input[..], None, &get_context(), false) + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -185,7 +211,7 @@ mod tests { let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); assert_eq!( - Blake2F::execute(&input[..], None, &get_context(), false) + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -198,7 +224,7 @@ mod tests { let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); assert_eq!( - Blake2F::execute(&input[..], None, &get_context(), false) + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -211,7 +237,7 @@ mod tests { let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); assert_eq!( - Blake2F::execute(&input[..], None, &get_context(), false) + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -226,7 +252,7 @@ mod tests { let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615"); assert_eq!( - Blake2F::execute(&input[..], None, &get_context(), false) + Blake2F::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected diff --git a/modules/evm/src/precompiles/bn128.rs b/modules/evm/src/precompiles/bn128.rs index 092a7c19c6..c73983899c 100644 --- a/modules/evm/src/precompiles/bn128.rs +++ b/modules/evm/src/precompiles/bn128.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . use super::Precompile; -use crate::runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}; -use module_evm_utility::evm::{Context, ExitError, ExitSucceed}; +use crate::{PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult}; +use module_evm_utility::evm::{ExitError, ExitSucceed}; use sp_core::U256; use sp_std::vec::Vec; @@ -67,9 +67,13 @@ impl Bn128Add { } impl Precompile for Bn128Add { - fn execute(input: &[u8], _target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { use bn::AffineG1; + handle.record_cost(Bn128Add::GAS_COST)?; + + let input = handle.input(); + let p1 = read_point(input, 0)?; let p2 = read_point(input, 64)?; @@ -90,11 +94,22 @@ impl Precompile for Bn128Add { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: Bn128Add::GAS_COST, output: buf.to_vec(), - logs: Default::default(), }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } /// The Bn128Mul builtin @@ -105,9 +120,13 @@ impl Bn128Mul { } impl Precompile for Bn128Mul { - fn execute(input: &[u8], _target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { use bn::AffineG1; + handle.record_cost(Bn128Mul::GAS_COST)?; + + let input = handle.input(); + let p = read_point(input, 0)?; let fr = read_fr(input, 64)?; @@ -128,11 +147,22 @@ impl Precompile for Bn128Mul { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: Bn128Mul::GAS_COST, output: buf.to_vec(), - logs: Default::default(), }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } /// The Bn128Pairing builtin @@ -145,17 +175,10 @@ impl Bn128Pairing { } impl Precompile for Bn128Pairing { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; - if let Some(gas_left) = target_gas { - if gas_left < Bn128Pairing::BASE_GAS_COST { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - + let input = handle.input(); if input.len() % 192 != 0 { return Err(PrecompileFailure::Error { exit_status: ExitError::Other("Invalid input length, must be multiple of 192 (3 * (32*2))".into()), @@ -169,13 +192,6 @@ impl Precompile for Bn128Pairing { let elements = input.len() / 192; let gas_cost: u64 = Bn128Pairing::BASE_GAS_COST + (elements as u64 * Bn128Pairing::GAS_COST_PER_PAIRING); - if let Some(gas_left) = target_gas { - if gas_left < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } let mut vals = Vec::new(); for idx in 0..elements { @@ -236,22 +252,37 @@ impl Precompile for Bn128Pairing { } }; + handle.record_cost(gas_cost)?; + let mut buf = [0u8; 32]; ret_val.to_big_endian(&mut buf); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: buf.to_vec(), - logs: Default::default(), }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } #[cfg(test)] mod tests { use super::*; + use crate::precompiles::tests::MockPrecompileHandle; use hex_literal::hex; + use module_evm_utility::evm::Context; fn get_context() -> Context { Context { @@ -278,7 +309,7 @@ mod tests { "}; assert_eq!( - Bn128Add::execute(&input[..], None, &get_context(), false) + Bn128Add::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -295,7 +326,7 @@ mod tests { "}; assert_eq!( - Bn128Add::execute(&input[..], None, &get_context(), false) + Bn128Add::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -312,7 +343,7 @@ mod tests { "}; assert_eq!( - Bn128Add::execute(&input[..], None, &get_context(), false), + Bn128Add::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), Err(PrecompileFailure::Error { exit_status: ExitError::Other("Invalid curve point".into()) }) @@ -336,7 +367,7 @@ mod tests { "}; assert_eq!( - Bn128Mul::execute(&input[..], None, &get_context(), false) + Bn128Mul::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -352,7 +383,7 @@ mod tests { "}; assert_eq!( - Bn128Mul::execute(&input[..], None, &get_context(), false), + Bn128Mul::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), Err(PrecompileFailure::Error { exit_status: ExitError::Other("Invalid curve point".into()) }) @@ -370,7 +401,7 @@ mod tests { "}; assert_eq!( - Bn128Pairing::execute(&input[..], None, &get_context(), false) + Bn128Pairing::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)) .unwrap() .output, expected @@ -390,7 +421,7 @@ mod tests { "}; assert_eq!( - Bn128Pairing::execute(&input[..], None, &get_context(), false), + Bn128Pairing::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), Err(PrecompileFailure::Error { exit_status: ExitError::Other("Invalid b argument - not on curve".into()) }) @@ -407,7 +438,7 @@ mod tests { "}; assert_eq!( - Bn128Pairing::execute(&input[..], None, &get_context(), false), + Bn128Pairing::execute(&mut MockPrecompileHandle::new(&input[..], None, &get_context(), false)), Err(PrecompileFailure::Error { exit_status: ExitError::Other("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) }) diff --git a/modules/evm/src/precompiles/ecrecover.rs b/modules/evm/src/precompiles/ecrecover.rs index 1f1c6fe219..794a63007e 100644 --- a/modules/evm/src/precompiles/ecrecover.rs +++ b/modules/evm/src/precompiles/ecrecover.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::ExitSucceed; use sp_std::{cmp::min, vec::Vec}; @@ -36,15 +36,15 @@ impl LinearCostPrecompile for ECRecover { let mut sig = [0u8; 65]; msg[0..32].copy_from_slice(&input[0..32]); - sig[0..32].copy_from_slice(&input[64..96]); - sig[32..64].copy_from_slice(&input[96..128]); - - sig[64] = match input[63] { - v if v > 26 && input[32..63] == [0; 31] => v - 27, - _ => { - return Ok((ExitSucceed::Returned, [0u8; 0].to_vec())); - } - }; + sig[0..32].copy_from_slice(&input[64..96]); // r + sig[32..64].copy_from_slice(&input[96..128]); // s + sig[64] = input[63]; // v + + // v can only be 27 or 28 on the full 32 bytes value. + // https://github.com/ethereum/go-ethereum/blob/a907d7e81aaeea15d80b2d3209ad8e08e3bf49e0/core/vm/contracts.go#L177 + if input[32..63] != [0u8; 31] || ![27, 28].contains(&input[63]) { + return Ok((ExitSucceed::Returned, [0u8; 0].to_vec())); + } let result = match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) { Ok(pubkey) => { diff --git a/modules/evm/src/precompiles/ecrecover_publickey.rs b/modules/evm/src/precompiles/ecrecover_publickey.rs index c5eda0aab9..6d1a4674ef 100644 --- a/modules/evm/src/precompiles/ecrecover_publickey.rs +++ b/modules/evm/src/precompiles/ecrecover_publickey.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::{ExitError, ExitSucceed}; use sp_std::{cmp::min, vec::Vec}; diff --git a/modules/evm/src/precompiles/identity.rs b/modules/evm/src/precompiles/identity.rs index 73fa623a27..c47efb4c18 100644 --- a/modules/evm/src/precompiles/identity.rs +++ b/modules/evm/src/precompiles/identity.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::ExitSucceed; use sp_std::vec::Vec; diff --git a/modules/evm/src/precompiles/mod.rs b/modules/evm/src/precompiles/mod.rs index 05b7363db5..c616b2639e 100644 --- a/modules/evm/src/precompiles/mod.rs +++ b/modules/evm/src/precompiles/mod.rs @@ -18,8 +18,8 @@ //! Builtin precompiles. -use crate::runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}; -use module_evm_utility::evm::{Context, ExitError, ExitSucceed}; +use crate::{PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult}; +use module_evm_utility::evm::{ExitError, ExitSucceed}; use sp_std::vec::Vec; mod blake2; @@ -47,7 +47,15 @@ pub trait Precompile { /// Try to execute the precompile. Calculate the amount of gas needed with given `input` and /// `target_gas`. Return `Ok(status, output, gas_used)` if the execution is /// successful. Otherwise return `Err(_)`. - fn execute(input: &[u8], target_gas: Option, context: &Context, is_static: bool) -> PrecompileResult; + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult; + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure>; } pub trait LinearCostPrecompile { @@ -58,16 +66,25 @@ pub trait LinearCostPrecompile { } impl Precompile for T { - fn execute(input: &[u8], target_gas: Option, _: &Context, _: bool) -> PrecompileResult { - let cost = ensure_linear_cost(target_gas, input.len() as u64, T::BASE, T::WORD)?; + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let target_gas = handle.gas_limit(); + let cost = ensure_linear_cost(target_gas, handle.input().len() as u64, T::BASE, T::WORD)?; + + handle.record_cost(cost)?; + let (exit_status, output) = T::execute(handle.input(), cost)?; + Ok(PrecompileOutput { exit_status, output }) + } + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let cost = ensure_linear_cost(target_gas, input.len() as u64, T::BASE, T::WORD)?; let (exit_status, output) = T::execute(input, cost)?; - Ok(PrecompileOutput { - exit_status, - cost, - output, - logs: Default::default(), - }) + Ok((PrecompileOutput { exit_status, output }, cost)) } } @@ -94,3 +111,101 @@ fn ensure_linear_cost(target_gas: Option, len: u64, base: u64, word: u64) - Ok(cost) } + +pub mod tests { + use crate::{ExitError, ExitReason, PrecompileHandle}; + use module_evm_utility::evm::{Context, Transfer}; + use sp_core::{H160, H256}; + use sp_std::vec::Vec; + + pub struct MockPrecompileHandle<'inner> { + pub input: &'inner [u8], + pub code_address: H160, + pub gas_limit: Option, + pub gas_used: u64, + pub context: &'inner Context, + pub is_static: bool, + } + + impl<'inner> MockPrecompileHandle<'inner> { + pub fn new(input: &'inner [u8], gas_limit: Option, context: &'inner Context, is_static: bool) -> Self { + Self { + input, + code_address: H160::default(), + gas_limit, + gas_used: 0, + context, + is_static, + } + } + } + + impl<'inner> PrecompileHandle for MockPrecompileHandle<'inner> { + fn call( + &mut self, + _: H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &Context, + ) -> (ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + + if let Some(gas_limit) = self.gas_limit { + if self.gas_used > gas_limit { + Err(ExitError::OutOfGas) + } else { + Ok(()) + } + } else { + Ok(()) + } + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + _storage_growth: Option, + ) -> Result<(), ExitError> { + unimplemented!() + } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) { + unimplemented!() + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn log(&mut self, _address: H160, _topics: Vec, _data: Vec) -> Result<(), ExitError> { + unimplemented!() + } + + fn code_address(&self) -> H160 { + self.code_address + } + + fn input(&self) -> &[u8] { + self.input + } + + fn context(&self) -> &Context { + self.context + } + + fn is_static(&self) -> bool { + self.is_static + } + + fn gas_limit(&self) -> Option { + self.gas_limit + } + } +} diff --git a/modules/evm/src/precompiles/modexp.rs b/modules/evm/src/precompiles/modexp.rs index 0553bb0a70..586c82b23f 100644 --- a/modules/evm/src/precompiles/modexp.rs +++ b/modules/evm/src/precompiles/modexp.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . use super::Precompile; -use crate::runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}; -use module_evm_utility::evm::{Context, ExitError, ExitSucceed}; +use crate::{PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult}; +use module_evm_utility::evm::{ExitError, ExitSucceed}; use num::{BigUint, One, Zero}; use sp_core::U256; use sp_runtime::traits::UniqueSaturatedInto; @@ -269,7 +269,10 @@ impl ModexpImpl for Modexp { } impl Precompile for IstanbulModexp { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let input = handle.input(); + let target_gas = handle.gas_limit(); + if input.len() as u64 > MAX_LENGTH { return Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, @@ -284,17 +287,34 @@ impl Precompile for IstanbulModexp { } } + let output = Self::execute_modexp(input); + handle.record_cost(cost.as_u64())?; + Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: cost.as_u64(), - output: Self::execute_modexp(input), - logs: Default::default(), + output, }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } impl Precompile for Modexp { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let input = handle.input(); + let target_gas = handle.gas_limit(); + if input.len() as u64 > MAX_LENGTH { return Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, @@ -320,19 +340,35 @@ impl Precompile for Modexp { } } + let output = Self::execute_modexp(input); + handle.record_cost(cost.as_u64())?; + Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: cost.as_u64(), - output: Self::execute_modexp(input), - logs: Default::default(), + output, }) } + + #[cfg(feature = "evm-tests")] + fn execute_ext( + input: &[u8], + target_gas: Option, + context: &crate::Context, + is_static: bool, + ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { + let mut handle = crate::precompiles::tests::MockPrecompileHandle::new(&input, target_gas, context, is_static); + let output = Self::execute(&mut handle)?; + + Ok((output, handle.gas_used)) + } } #[cfg(test)] mod tests { use super::*; + use crate::precompiles::tests::MockPrecompileHandle; use hex_literal::hex; + use module_evm_utility::evm::Context; fn get_context() -> Context { Context { @@ -345,19 +381,17 @@ mod tests { #[test] fn handle_min_gas() { assert_eq!( - Modexp::execute(&[], Some(199), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new(&[], Some(199), &get_context(), false)), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas }) ); assert_eq!( - Modexp::execute(&[], Some(200), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new(&[], Some(200), &get_context(), false)), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 0].to_vec(), - logs: Default::default(), }) ); } @@ -365,12 +399,10 @@ mod tests { #[test] fn test_empty_input() { assert_eq!( - Modexp::execute(&[], None, &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new(&[], None, &get_context(), false)), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 0].to_vec(), - logs: Default::default(), }) ); } @@ -384,12 +416,10 @@ mod tests { "}; assert_eq!( - Modexp::execute(&input, None, &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new(&input, None, &get_context(), false)), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 1].to_vec(), - logs: Default::default(), }) ); } @@ -403,7 +433,12 @@ mod tests { "}; assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, }) @@ -419,7 +454,12 @@ mod tests { "}; assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, }) @@ -434,7 +474,12 @@ mod tests { 00000000000000000000000000000000000000000000000000000000503c8ac3 "}; assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, }) @@ -455,12 +500,15 @@ mod tests { // 3 ^ 5 % 7 == 5 assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: vec![5], - logs: Default::default(), }) ); } @@ -482,22 +530,28 @@ mod tests { U256::from(10055u64).to_big_endian(&mut output); assert_eq!( - IstanbulModexp::execute(&input, Some(100_000), &get_context(), false), + IstanbulModexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 204, output: output.to_vec(), - logs: Default::default(), }) ); assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: output.to_vec(), - logs: Default::default(), }) ); } @@ -517,22 +571,28 @@ mod tests { U256::from(1u64).to_big_endian(&mut output); assert_eq!( - IstanbulModexp::execute(&input, Some(100_000), &get_context(), false), + IstanbulModexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 13056, output: output.to_vec(), - logs: Default::default(), }) ); assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 1360, output: output.to_vec(), - logs: Default::default(), }) ); } @@ -551,22 +611,28 @@ mod tests { let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); assert_eq!( - IstanbulModexp::execute(&input, Some(100_000), &get_context(), false), + IstanbulModexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 768, output: expected.to_vec(), - logs: Default::default(), }) ); assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: expected.to_vec(), - logs: Default::default(), }) ); } @@ -582,12 +648,15 @@ mod tests { "}; assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 0].to_vec(), - logs: Default::default(), }) ); } @@ -610,12 +679,15 @@ mod tests { ]; assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 1].to_vec(), - logs: Default::default(), }) ); } @@ -625,34 +697,50 @@ mod tests { let input = vec![0u8; 1025]; assert_eq!( - IstanbulModexp::execute(&input[..1024], Some(100_000), &get_context(), false), + IstanbulModexp::execute(&mut MockPrecompileHandle::new( + &input[..1024], + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: [0u8; 0].to_vec(), - logs: Default::default(), }) ); assert_eq!( - Modexp::execute(&input[..1024], Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input[..1024], + Some(100_000), + &get_context(), + false + )), Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 200, output: [0u8; 0].to_vec(), - logs: Default::default(), }) ); assert_eq!( - IstanbulModexp::execute(&input, Some(100_000), &get_context(), false), + IstanbulModexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, }) ); assert_eq!( - Modexp::execute(&input, Some(100_000), &get_context(), false), + Modexp::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &get_context(), + false + )), Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, }) diff --git a/modules/evm/src/precompiles/ripemd.rs b/modules/evm/src/precompiles/ripemd.rs index 967ba63801..e908327790 100644 --- a/modules/evm/src/precompiles/ripemd.rs +++ b/modules/evm/src/precompiles/ripemd.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::ExitSucceed; use sha3::Digest; use sp_std::vec::Vec; diff --git a/modules/evm/src/precompiles/sha256.rs b/modules/evm/src/precompiles/sha256.rs index 296bf46d5f..106e8b8718 100644 --- a/modules/evm/src/precompiles/sha256.rs +++ b/modules/evm/src/precompiles/sha256.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::ExitSucceed; use sp_std::vec::Vec; diff --git a/modules/evm/src/precompiles/sha3fips.rs b/modules/evm/src/precompiles/sha3fips.rs index 9d6842aae9..656e670cb5 100644 --- a/modules/evm/src/precompiles/sha3fips.rs +++ b/modules/evm/src/precompiles/sha3fips.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use super::LinearCostPrecompile; -use crate::runner::state::PrecompileFailure; +use crate::PrecompileFailure; use module_evm_utility::evm::ExitSucceed; use sp_std::vec::Vec; use tiny_keccak::Hasher; diff --git a/modules/evm/src/runner/mod.rs b/modules/evm/src/runner/mod.rs index 90e82316a8..65174c8d33 100644 --- a/modules/evm/src/runner/mod.rs +++ b/modules/evm/src/runner/mod.rs @@ -19,6 +19,7 @@ pub mod stack; pub mod state; pub mod storage_meter; +pub mod tagged_runtime; use crate::{BalanceOf, CallInfo, Config, CreateInfo}; use module_evm_utility::evm; diff --git a/modules/evm/src/runner/stack.rs b/modules/evm/src/runner/stack.rs index 71c0e2b3f9..7d812ac43d 100644 --- a/modules/evm/src/runner/stack.rs +++ b/modules/evm/src/runner/stack.rs @@ -21,7 +21,7 @@ use crate::{ runner::{ - state::{Accessed, CustomStackState, StackExecutor, StackState as StackStateT, StackSubstateMetadata}, + state::{Accessed, StackExecutor, StackState as StackStateT, StackSubstateMetadata}, Runner as RunnerT, RunnerExtended, }, AccountStorages, BalanceOf, CallInfo, Config, CreateInfo, Error, ExecutionInfo, Pallet, STORAGE_SIZE, @@ -621,6 +621,10 @@ impl<'vicinity, 'config, T: Config> BackendT for SubstrateStackState<'vicinity, self.vicinity.origin } + fn block_randomness(&self) -> Option { + self.vicinity.block_randomness + } + fn block_hash(&self, number: U256) -> H256 { if number > U256::from(u32::MAX) { H256::default() @@ -729,8 +733,9 @@ impl<'vicinity, 'config, T: Config> StackStateT<'config> for SubstrateStackState self.substate.deleted(address) } - fn inc_nonce(&mut self, address: H160) { + fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> { Pallet::::inc_nonce(&address); + Ok(()) } fn set_storage(&mut self, address: H160, index: H256, value: H256) { @@ -911,14 +916,12 @@ impl<'vicinity, 'config, T: Config> StackStateT<'config> for SubstrateStackState self.substate .recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key))) } -} -impl<'vicinity, 'config, T: Config> CustomStackState for SubstrateStackState<'vicinity, 'config, T> { - fn code_hash_at_address(&self, address: H160) -> H256 { - Pallet::::code_hash_at_address(&address) + fn code_size(&self, address: H160) -> U256 { + Pallet::::code_size_at_address(&address) } - fn code_size_at_address(&self, address: H160) -> U256 { - Pallet::::code_size_at_address(&address) + fn code_hash(&self, address: H160) -> H256 { + Pallet::::code_hash_at_address(&address) } } diff --git a/modules/evm/src/runner/state.rs b/modules/evm/src/runner/state.rs index ccfe2a6bed..1caba41197 100644 --- a/modules/evm/src/runner/state.rs +++ b/modules/evm/src/runner/state.rs @@ -16,15 +16,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Synchronize with https://github.com/rust-blockchain/evm/blob/6534c1dd/src/executor/stack/executor.rs +// Synchronize with https://github.com/rust-blockchain/evm/blob/d543f10/src/executor/stack/executor.rs -use crate::{encode_revert_message, StorageMeter}; +use crate::{ + encode_revert_message, + runner::tagged_runtime::{RuntimeKind, TaggedRuntime}, + IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet, StorageMeter, +}; use core::{cmp::min, convert::Infallible}; use module_evm_utility::{ - ethereum::Log, evm::{ - backend::Backend, Capture, Config, Context, CreateScheme, ExitError, ExitFatal, ExitReason, ExitRevert, - ExitSucceed, Opcode, Runtime, Stack, Transfer, + backend::Backend, maybe_borrowed::MaybeBorrowed, Capture, Config, Context, CreateScheme, ExitError, ExitFatal, + ExitReason, Opcode, Resolve, Runtime, Stack, Transfer, }, evm_gasometer::{self as gasometer, Gasometer, StorageTarget}, evm_runtime::Handler, @@ -40,11 +43,7 @@ pub use primitives::{ use sha3::{Digest, Keccak256}; use sp_core::{H160, H256, U256}; use sp_runtime::traits::Zero; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - rc::Rc, - vec::Vec, -}; +use sp_std::{collections::btree_set::BTreeSet, rc::Rc, vec::Vec}; macro_rules! event { ($x:expr) => {}; @@ -99,6 +98,10 @@ macro_rules! emit_exit { }}; } +// Default call stack capacity that can be used to +// execute the stack without reallocating. +const DEFAULT_CALL_STACK_CAPACITY: usize = 4; + pub enum StackExitKind { Succeeded, Reverted, @@ -309,12 +312,7 @@ impl<'config> StackSubstateMetadata<'config> { } } -pub trait CustomStackState { - fn code_hash_at_address(&self, address: H160) -> H256; - fn code_size_at_address(&self, address: H160) -> U256; -} - -pub trait StackState<'config>: Backend + CustomStackState { +pub trait StackState<'config>: Backend { fn metadata(&self) -> &StackSubstateMetadata<'config>; fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config>; @@ -328,7 +326,7 @@ pub trait StackState<'config>: Backend + CustomStackState { fn is_cold(&self, address: H160) -> bool; fn is_storage_cold(&self, address: H160, key: H256) -> bool; - fn inc_nonce(&mut self, address: H160); + fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError>; fn set_storage(&mut self, address: H160, key: H256, value: H256); fn reset_storage(&mut self, address: H160); fn log(&mut self, address: H160, topics: Vec, data: Vec); @@ -337,95 +335,51 @@ pub trait StackState<'config>: Backend + CustomStackState { fn transfer(&mut self, transfer: Transfer) -> Result<(), ExitError>; fn reset_balance(&mut self, address: H160); fn touch(&mut self, address: H160); -} - -/// Data returned by a precompile on success. -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PrecompileOutput { - pub exit_status: ExitSucceed, - pub cost: u64, - pub output: Vec, - pub logs: Vec, -} - -/// Data returned by a precompile in case of failure. -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum PrecompileFailure { - /// Reverts the state changes and consume all the gas. - Error { exit_status: ExitError }, - /// Reverts the state changes and consume the provided `cost`. - /// Returns the provided error message. - Revert { - exit_status: ExitRevert, - output: Vec, - cost: u64, - }, - /// Mark this failure as fatal, and all EVM execution stacks must be exited. - Fatal { exit_status: ExitFatal }, -} - -/// A precompile result. -pub type PrecompileResult = Result; - -/// A set of precompiles. -/// Checks of the provided address being in the precompile set should be -/// as cheap as possible since it may be called often. -pub trait PrecompileSet { - /// Tries to execute a precompile in the precompile set. - /// If the provided address is not a precompile, returns None. - fn execute( - &self, - address: H160, - input: &[u8], - gas_limit: Option, - context: &Context, - is_static: bool, - ) -> Option; - - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool; -} -impl PrecompileSet for () { - fn execute(&self, _: H160, _: &[u8], _: Option, _: &Context, _: bool) -> Option { - None + /// Fetch the code size of an address. + /// Provide a default implementation by fetching the code, but + /// can be customized to use a more performant approach that don't need to + /// fetch the code. + fn code_size(&self, address: H160) -> U256 { + U256::from(self.code(address).len()) } - fn is_precompile(&self, _: H160) -> bool { - false + /// Fetch the code hash of an address. + /// Provide a default implementation by fetching the code, but + /// can be customized to use a more performant approach that don't need to + /// fetch the code. + fn code_hash(&self, address: H160) -> H256 { + H256::from_slice(Keccak256::digest(self.code(address)).as_slice()) } -} -/// Precompiles function signature. Expected input arguments are: -/// * Input -/// * Gas limit -/// * Context -/// * Is static -pub type PrecompileFn = fn(&[u8], Option, &Context, bool) -> PrecompileResult; + fn record_external_operation(&mut self, _op: crate::ExternalOperation) -> Result<(), ExitError> { + Ok(()) + } -impl PrecompileSet for BTreeMap { - fn execute( - &self, - address: H160, - input: &[u8], - gas_limit: Option, - context: &Context, - is_static: bool, - ) -> Option { - self.get(&address) - .map(|precompile| (*precompile)(input, gas_limit, context, is_static)) + fn record_external_dynamic_opcode_cost( + &mut self, + _opcode: Opcode, + _gas_cost: gasometer::GasCost, + _target: StorageTarget, + ) -> Result<(), ExitError> { + Ok(()) } - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool { - self.contains_key(&address) + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + _storage_growth: Option, + ) -> Result<(), ExitError> { + Ok(()) } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} } +/// A precompile result. +pub type PrecompileResult = Result; + /// Stack-based executor. pub struct StackExecutor<'config, 'precompiles, S, P> { config: &'config Config, @@ -481,9 +435,83 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu /// Execute the runtime until it returns. pub fn execute(&mut self, runtime: &mut Runtime) -> ExitReason { - match runtime.run(self) { - Capture::Exit(s) => s, - Capture::Trap(_) => unreachable!("Trap is Infallible"), + let mut call_stack = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + call_stack.push(TaggedRuntime { + kind: RuntimeKind::Execute, + inner: MaybeBorrowed::Borrowed(runtime), + }); + let (reason, _, _) = self.execute_with_call_stack(&mut call_stack); + reason + } + + /// Execute using Runtimes on the call_stack until it returns. + fn execute_with_call_stack( + &mut self, + call_stack: &mut Vec>, + ) -> (ExitReason, Option, Vec) { + // This `interrupt_runtime` is used to pass the runtime obtained from the + // `Capture::Trap` branch in the match below back to the top of the call stack. + // The reason we can't simply `push` the runtime directly onto the stack in the + // `Capture::Trap` branch is because the borrow-checker complains that the stack + // is already borrowed as long as we hold a pointer on the last element + // (i.e. the currently executing runtime). + let mut interrupt_runtime = None; + loop { + if let Some(rt) = interrupt_runtime.take() { + call_stack.push(rt); + } + let runtime = match call_stack.last_mut() { + Some(runtime) => runtime, + None => { + return (ExitReason::Fatal(ExitFatal::UnhandledInterrupt), None, Vec::new()); + } + }; + let reason = { + let inner_runtime = &mut runtime.inner; + match inner_runtime.run(self) { + Capture::Exit(reason) => reason, + Capture::Trap(Resolve::Call(rt, _)) => { + interrupt_runtime = Some(rt.0); + continue; + } + Capture::Trap(Resolve::Create(rt, _)) => { + interrupt_runtime = Some(rt.0); + continue; + } + } + }; + let runtime_kind = runtime.kind; + let (reason, maybe_address, return_data) = match runtime_kind { + RuntimeKind::Create(created_address) => { + let (reason, maybe_address, return_data) = + self.cleanup_for_create(created_address, reason, runtime.inner.machine().return_value()); + (reason, maybe_address, return_data) + } + RuntimeKind::Call(code_address) => { + let return_data = + self.cleanup_for_call(code_address, &reason, runtime.inner.machine().return_value()); + (reason, None, return_data) + } + RuntimeKind::Execute => (reason, None, runtime.inner.machine().return_value()), + }; + // We're done with that runtime now, so can pop it off the call stack + call_stack.pop(); + // Now pass the results from that runtime on to the next one in the stack + let runtime = match call_stack.last_mut() { + Some(r) => r, + None => return (reason, None, return_data), + }; + emit_exit!(&reason, &return_data); + let inner_runtime = &mut runtime.inner; + let maybe_error = match runtime_kind { + RuntimeKind::Create(_) => inner_runtime.finish_create(reason, maybe_address, return_data), + RuntimeKind::Call(_) => inner_runtime.finish_call(reason, return_data), + RuntimeKind::Execute => inner_runtime.finish_call(reason, return_data), + }; + // Early exit if passing on the result caused an error + if let Err(e) = maybe_error { + return (e, None, Vec::new()); + } } } @@ -502,6 +530,22 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gasometer.record_transaction(transaction_cost) } + fn maybe_record_init_code_cost(&mut self, init_code: &[u8]) -> Result<(), ExitError> { + if let Some(limit) = self.config.max_initcode_size { + // EIP-3860 + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return Err(ExitError::CreateContractLimit); + } + return self + .state + .metadata_mut() + .gasometer + .record_cost(gasometer::init_code_cost(init_code)); + } + Ok(()) + } + /// Execute a `CREATE` transaction. pub fn transact_create( &mut self, @@ -519,6 +563,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu address: self.create_address(CreateScheme::Legacy { caller }), }); + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); + } + } + if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) { return emit_exit!(e.into(), Vec::new()); } @@ -533,7 +584,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu false, ) { Capture::Exit((s, _, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -547,6 +603,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 ) -> (ExitReason, Vec) { + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); + } + } + let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); event!(TransactCreate2 { caller, @@ -579,7 +642,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu false, ) { Capture::Exit((s, _, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -593,6 +661,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, ) -> (ExitReason, Vec) { + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); + } + } + event!(TransactCreate { caller, value, @@ -615,7 +690,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu false, ) { Capture::Exit((s, _, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -634,7 +714,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, ) -> (ExitReason, Vec) { - self.state.inc_nonce(caller); + if let Err(e) = self.state.inc_nonce(caller) { + return (e.into(), Vec::new()); + } event!(TransactCall { caller, @@ -655,12 +737,24 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu // Initialize initial addresses for EIP-2929 if self.config.increase_state_access_gas { - let addresses = core::iter::once(caller).chain(core::iter::once(address)); - self.state.metadata_mut().access_addresses(addresses); + if self.config.warm_coinbase_address { + // Warm coinbase address for EIP-3651 + let addresses = core::iter::once(caller) + .chain(core::iter::once(address)) + .chain(core::iter::once(self.block_coinbase())); + self.state.metadata_mut().access_addresses(addresses); + } else { + let addresses = core::iter::once(caller).chain(core::iter::once(address)); + self.state.metadata_mut().access_addresses(addresses); + } self.initialize_with_access_list(access_list); } + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AccountBasicRead) { + return (e.into(), Vec::new()); + } + let context = Context { caller, address, @@ -682,7 +776,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu context, ) { Capture::Exit((s, v)) => emit_exit!(s, v), - Capture::Trap(_) => unreachable!(), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } } } @@ -698,7 +797,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu /// Get fee needed for the current executor, given the price. pub fn fee(&self, price: U256) -> U256 { let used_gas = self.used_gas(); - U256::from(used_gas) * price + U256::from(used_gas).saturating_mul(price) } /// Get account nonce. @@ -761,7 +860,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu init_code: Vec, target_gas: Option, take_l64: bool, - ) -> Capture<(ExitReason, Option, Vec), Infallible> { + ) -> Capture<(ExitReason, Option, Vec), StackExecutorCreateInterrupt<'static>> { macro_rules! try_or_fail { ( $e:expr ) => { match $e { @@ -771,13 +870,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu }; } - fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { - if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() { - return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); - } - Ok(()) - } - fn l64(gas: u64) -> u64 { gas - gas / 64 } @@ -814,7 +906,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu return Capture::Exit((ExitError::OutOfFund.into(), None, Vec::new())); } - self.state.inc_nonce(caller); + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AccountBasicRead) { + return Capture::Exit((ExitReason::Error(e), None, Vec::new())); + } + + if let Err(e) = self.state.inc_nonce(caller) { + return Capture::Exit((e.into(), None, Vec::new())); + } let after_gas = if take_l64 && self.config.call_l64_after_gas { if self.config.estimate { @@ -837,7 +935,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu self.enter_substate(gas_limit, false); { - if self.code_size(address) != U256::zero() { + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AddressCodeRead(address)) { + let _ = self.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitReason::Error(e), None, Vec::new())); + } + let code_size = self.code_size(address); + if code_size != U256::zero() { let _ = self.exit_substate(StackExitKind::Failed); return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())); } @@ -871,61 +974,27 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu } if self.config.create_increase_nonce { - self.state.inc_nonce(address); - } - - let mut runtime = Runtime::new(Rc::new(init_code), Rc::new(Vec::new()), context, self.config); - - let reason = self.execute(&mut runtime); - log::debug!(target: "evm", "Create execution using address {}: {:?}", address, reason); - - match reason { - ExitReason::Succeed(s) => { - let out = runtime.machine().return_value(); - - // As of EIP-3541 code starting with 0xef cannot be deployed - if let Err(e) = check_first_byte(self.config, &out) { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((e.into(), None, Vec::new())); - } - - if let Some(limit) = self.config.create_contract_limit { - if out.len() > limit { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - return Capture::Exit((ExitError::CreateContractLimit.into(), None, Vec::new())); - } - } - - match self.state.metadata_mut().gasometer.record_deposit(out.len()) { - Ok(()) => { - self.state.set_code(address, out); - let e = self.exit_substate(StackExitKind::Succeeded); - try_or_fail!(e); - Capture::Exit((ExitReason::Succeed(s), Some(address), Vec::new())) - } - Err(e) => { - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) - } - } - } - ExitReason::Error(e) => { - self.state.metadata_mut().gasometer.fail(); + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AccountBasicRead) { let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), None, Vec::new())) + return Capture::Exit((ExitReason::Error(e), None, Vec::new())); } - ExitReason::Revert(e) => { - let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit((ExitReason::Revert(e), None, runtime.machine().return_value())) - } - ExitReason::Fatal(e) => { - self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), None, Vec::new())) + if let Err(e) = self.state.inc_nonce(address) { + return Capture::Exit((e.into(), None, Vec::new())); } } + + let runtime = Runtime::new( + Rc::new(init_code), + Rc::new(Vec::new()), + context, + self.config.stack_limit, + self.config.memory_limit, + ); + + Capture::Trap(StackExecutorCreateInterrupt(TaggedRuntime { + kind: RuntimeKind::Create(address), + inner: MaybeBorrowed::Owned(runtime), + })) } #[allow(clippy::too_many_arguments)] @@ -939,7 +1008,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu take_l64: bool, take_stipend: bool, context: Context, - ) -> Capture<(ExitReason, Vec), Infallible> { + ) -> Capture<(ExitReason, Vec), StackExecutorCallInterrupt<'static>> { macro_rules! try_or_fail { ( $e:expr ) => { match $e { @@ -989,11 +1058,15 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu } } - let code = self.code(code_address); - self.enter_substate(gas_limit, is_static); self.state.touch(context.address); + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AddressCodeRead(code_address)) { + let _ = self.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitReason::Error(e), Vec::new())); + } + let code = self.code(code_address); + if let Some(depth) = self.state.metadata().depth { if depth > self.config.call_stack_limit { let _ = self.exit_substate(StackExitKind::Reverted); @@ -1002,6 +1075,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu } if let Some(transfer) = transfer { + if let Err(e) = self.record_external_operation(crate::ExternalOperation::AccountBasicRead) { + let _ = self.exit_substate(StackExitKind::Failed); + return Capture::Exit((ExitReason::Error(e), Vec::new())); + } match self.state.transfer(transfer) { Ok(()) => (), Err(e) => { @@ -1015,27 +1092,17 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu // reflect both the is_static parameter of this call and the is_static // of the caller context. let precompile_is_static = self.state.metadata().is_static(); - if let Some(result) = - self.precompile_set - .execute(code_address, &input, Some(gas_limit), &context, precompile_is_static) - { + if let Some(result) = self.precompile_set.execute(&mut StackExecutorHandle { + executor: self, + code_address, + input: &input, + gas_limit: Some(gas_limit), + context: &context, + is_static: precompile_is_static, + }) { return match result { - Ok(PrecompileOutput { - exit_status, - output, - cost, - logs, - }) => { - for Log { address, topics, data } in logs { - match self.log(address, topics, data) { - Ok(_) => continue, - Err(error) => { - return Capture::Exit((ExitReason::Error(error), output)); - } - } - } - - let _ = self.state.metadata_mut().gasometer.record_cost(cost); + Ok(PrecompileOutput { exit_status, output }) => { + // let _ = self.exit_substate(StackExitKind::Succeeded); let e = self.exit_substate(StackExitKind::Succeeded); try_or_fail!(e); Capture::Exit((ExitReason::Succeed(exit_status), output)) @@ -1044,13 +1111,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu let _ = self.exit_substate(StackExitKind::Failed); Capture::Exit((ExitReason::Error(exit_status), Vec::new())) } - Err(PrecompileFailure::Revert { - exit_status, - output, - cost, - }) => { - let _ = self.state.metadata_mut().gasometer.record_cost(cost); + Err(PrecompileFailure::Revert { exit_status, output }) => { let _ = self.exit_substate(StackExitKind::Reverted); + // Capture::Exit((ExitReason::Revert(exit_status), output)) Capture::Exit((ExitReason::Revert(exit_status), encode_revert_message(&output))) } Err(PrecompileFailure::Fatal { exit_status }) => { @@ -1061,47 +1124,125 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu }; } - let mut runtime = Runtime::new(Rc::new(code), Rc::new(input), context, self.config); + let runtime = Runtime::new( + Rc::new(code), + Rc::new(input), + context, + self.config.stack_limit, + self.config.memory_limit, + ); - #[cfg(not(feature = "tracing"))] - let reason = self.execute(&mut runtime); - #[cfg(feature = "tracing")] - //let reason = module_evm_utility::evm::tracing::using(&mut Tracer, || self.execute(&mut runtime)); - let reason = module_evm_utility::evm_runtime::tracing::using(&mut Tracer, || self.execute(&mut runtime)); - //let reason = module_evm_utility::evm_gasometer::tracing::using(&mut Tracer, || self.execute(&mut - // runtime)); + Capture::Trap(StackExecutorCallInterrupt(TaggedRuntime { + kind: RuntimeKind::Call(code_address), + inner: MaybeBorrowed::Owned(runtime), + })) + } - log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); + fn cleanup_for_create( + &mut self, + created_address: H160, + reason: ExitReason, + return_data: Vec, + ) -> (ExitReason, Option, Vec) { + fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { + if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() { + return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); + } + Ok(()) + } + + log::debug!(target: "evm", "Create execution using address {}: {:?}", created_address, reason); match reason { ExitReason::Succeed(s) => { - let e = self.exit_substate(StackExitKind::Succeeded); - try_or_fail!(e); - Capture::Exit((ExitReason::Succeed(s), runtime.machine().return_value())) + let out = return_data; + let address = created_address; + // As of EIP-3541 code starting with 0xef cannot be deployed + if let Err(e) = check_first_byte(self.config, &out) { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return (e.into(), None, Vec::new()); + } + + if let Some(limit) = self.config.create_contract_limit { + if out.len() > limit { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + return (ExitError::CreateContractLimit.into(), None, Vec::new()); + } + } + + match self.state.metadata_mut().gasometer.record_deposit(out.len()) { + Ok(()) => { + let code_len = out.len(); + self.state.set_code(address, out); + let exit_result = self.exit_substate(StackExitKind::Succeeded); + if let Err(e) = + self.record_external_operation(crate::ExternalOperation::Write(U256::from(code_len))) + { + return (e.into(), None, Vec::new()); + } + if let Err(e) = exit_result { + return (e.into(), None, Vec::new()); + } + (ExitReason::Succeed(s), Some(address), Vec::new()) + } + Err(e) => { + let _ = self.exit_substate(StackExitKind::Failed); + (ExitReason::Error(e), None, Vec::new()) + } + } } ExitReason::Error(e) => { + self.state.metadata_mut().gasometer.fail(); let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Error(e), Vec::new())) + (ExitReason::Error(e), None, Vec::new()) } ExitReason::Revert(e) => { let _ = self.exit_substate(StackExitKind::Reverted); - Capture::Exit((ExitReason::Revert(e), runtime.machine().return_value())) + (ExitReason::Revert(e), None, return_data) } ExitReason::Fatal(e) => { self.state.metadata_mut().gasometer.fail(); let _ = self.exit_substate(StackExitKind::Failed); - Capture::Exit((ExitReason::Fatal(e), Vec::new())) + (ExitReason::Fatal(e), None, Vec::new()) + } + } + } + + fn cleanup_for_call(&mut self, code_address: H160, reason: &ExitReason, return_data: Vec) -> Vec { + log::debug!(target: "evm", "Call execution using address {}: {:?}", code_address, reason); + match reason { + ExitReason::Succeed(_) => { + let _ = self.exit_substate(StackExitKind::Succeeded); + return_data + } + ExitReason::Error(_) => { + let _ = self.exit_substate(StackExitKind::Failed); + Vec::new() + } + ExitReason::Revert(_) => { + let _ = self.exit_substate(StackExitKind::Reverted); + return_data + } + ExitReason::Fatal(_) => { + self.state.metadata_mut().gasometer.fail(); + let _ = self.exit_substate(StackExitKind::Failed); + Vec::new() } } } } +pub struct StackExecutorCallInterrupt<'borrow>(TaggedRuntime<'borrow>); +pub struct StackExecutorCreateInterrupt<'borrow>(TaggedRuntime<'borrow>); + impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler for StackExecutor<'config, 'precompiles, S, P> { - type CreateInterrupt = Infallible; + type CreateInterrupt = StackExecutorCreateInterrupt<'static>; type CreateFeedback = Infallible; - type CallInterrupt = Infallible; + type CallInterrupt = StackExecutorCallInterrupt<'static>; type CallFeedback = Infallible; fn balance(&self, address: H160) -> U256 { @@ -1109,7 +1250,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler } fn code_size(&self, address: H160) -> U256 { - self.state.code_size_at_address(address) + self.state.code_size(address) } fn code_hash(&self, address: H160) -> H256 { @@ -1117,17 +1258,22 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler return H256::default(); } - self.state.code_hash_at_address(address) + self.state.code_hash(address) } fn code(&self, address: H160) -> Vec { let code = self.state.code(address); - if code.len().is_zero() && !self.precompile_set.is_precompile(address) { - log::debug!( - target: "evm", - "contract does not exist, address: {:?}", - address - ); + if code.len().is_zero() { + if let IsPrecompileResult::Answer { + is_precompile: false, .. + } = self.precompile_set.is_precompile(address, u64::zero()) + { + log::debug!( + target: "evm", + "contract does not exist, address: {:?}", + address + ); + } } code } @@ -1148,11 +1294,27 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler } } - fn is_cold(&self, address: H160, maybe_index: Option) -> bool { - match maybe_index { - None => !self.precompile_set.is_precompile(address) && self.state.is_cold(address), + fn is_cold(&mut self, address: H160, maybe_index: Option) -> Result { + Ok(match maybe_index { + None => { + let is_precompile = match self + .precompile_set + .is_precompile(address, self.state.metadata().gasometer.gas()) + { + IsPrecompileResult::Answer { + is_precompile, + extra_cost, + } => { + self.state.metadata_mut().gasometer.record_cost(extra_cost)?; + is_precompile + } + IsPrecompileResult::OutOfGas => return Err(ExitError::OutOfGas), + }; + + !is_precompile && self.state.is_cold(address) + } Some(index) => self.state.is_storage_cold(address, index), - } + }) } fn gas_left(&self) -> U256 { @@ -1180,6 +1342,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler fn block_difficulty(&self) -> U256 { self.state.block_difficulty() } + fn block_randomness(&self) -> Option { + self.state.block_randomness() + } fn block_gas_limit(&self) -> U256 { self.state.block_gas_limit() } @@ -1233,6 +1398,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + if let Err(e) = self.maybe_record_init_code_cost(&init_code) { + let reason: ExitReason = e.into(); + emit_exit!(reason.clone()); + return Capture::Exit((reason, None, Vec::new())); + } + self.create_inner(caller, scheme, value, init_code, target_gas, true) } @@ -1245,6 +1416,12 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + if let Err(e) = self.maybe_record_init_code_cost(&init_code) { + let reason: ExitReason = e.into(); + emit_exit!(reason.clone()); + return Capture::Exit((reason, None, Vec::new())); + } + let capture = self.create_inner(caller, scheme, value, init_code, target_gas, true); if let Capture::Exit((ref reason, _, ref return_value)) = capture { @@ -1318,6 +1495,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler let gasometer = &mut self.state.metadata_mut().gasometer; gasometer.record_dynamic_cost(gas_cost, memory_cost)?; + + self.state + .record_external_dynamic_opcode_cost(opcode, gas_cost, target)?; + match target { StorageTarget::Address(address) => self.state.metadata_mut().access_address(address), StorageTarget::Slot(address, key) => self.state.metadata_mut().access_storage(address, key), @@ -1327,4 +1508,157 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler Ok(()) } + + fn record_external_operation(&mut self, op: crate::ExternalOperation) -> Result<(), ExitError> { + self.state.record_external_operation(op) + } +} + +pub struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { + executor: &'inner mut StackExecutor<'config, 'precompiles, S, P>, + code_address: H160, + input: &'inner [u8], + gas_limit: Option, + context: &'inner Context, + is_static: bool, +} + +impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> PrecompileHandle + for StackExecutorHandle<'inner, 'config, 'precompiles, S, P> +{ + // Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + code_address: H160, + transfer: Option, + input: Vec, + gas_limit: Option, + is_static: bool, + context: &Context, + ) -> (ExitReason, Vec) { + // For normal calls the cost is recorded at opcode level. + // Since we don't go through opcodes we need manually record the call + // cost. Not doing so will make the code panic as recording the call stipend + // will do an underflow. + let target_is_cold = match self.executor.is_cold(code_address, None) { + Ok(x) => x, + Err(err) => return (ExitReason::Error(err), Vec::new()), + }; + + let target_exists = self.executor.exists(code_address); + + let gas_cost = gasometer::GasCost::Call { + value: transfer.clone().map(|x| x.value).unwrap_or_else(U256::zero), + gas: U256::from(gas_limit.unwrap_or(u64::MAX)), + target_is_cold, + target_exists, + }; + + // We record the length of the input. + let memory_cost = Some(gasometer::MemoryCost { + offset: U256::zero(), + len: input.len().into(), + }); + + if let Err(error) = self + .executor + .state + .metadata_mut() + .gasometer + .record_dynamic_cost(gas_cost, memory_cost) + { + return (ExitReason::Error(error), Vec::new()); + } + + event!(PrecompileSubcall { + code_address: code_address.clone(), + transfer: &transfer, + input: &input, + target_gas: gas_limit, + is_static, + context + }); + + // Perform the subcall + match Handler::call( + self.executor, + code_address, + transfer, + input, + gas_limit, + is_static, + context.clone(), + ) { + Capture::Exit((s, v)) => (s, v), + Capture::Trap(rt) => { + // Ideally this would pass the interrupt back to the executor so it could be + // handled like any other call, however the type signature of this function does + // not allow it. For now we'll make a recursive call instead of making a breaking + // change to the precompile API. But this means a custom precompile could still + // potentially cause a stack overflow if you're not careful. + let mut call_stack = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + call_stack.push(rt.0); + let (reason, _, return_data) = self.executor.execute_with_call_stack(&mut call_stack); + emit_exit!(reason, return_data) + } + } + } + + /// Record cost to the Runtime gasometer. + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.executor.state.metadata_mut().gasometer.record_cost(cost) + } + + /// Record Substrate specific cost. + fn record_external_cost( + &mut self, + ref_time: Option, + proof_size: Option, + storage_growth: Option, + ) -> Result<(), ExitError> { + self.executor + .state + .record_external_cost(ref_time, proof_size, storage_growth) + } + + /// Refund Substrate specific cost. + fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { + self.executor.state.refund_external_cost(ref_time, proof_size); + } + + /// Retreive the remaining gas. + fn remaining_gas(&self) -> u64 { + self.executor.state.metadata().gasometer.gas() + } + + /// Record a log. + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + Handler::log(self.executor, address, topics, data) + } + + /// 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 { + self.gas_limit + } } diff --git a/modules/evm/src/runner/tagged_runtime.rs b/modules/evm/src/runner/tagged_runtime.rs new file mode 100644 index 0000000000..aa0c58fe79 --- /dev/null +++ b/modules/evm/src/runner/tagged_runtime.rs @@ -0,0 +1,33 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2024 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use module_evm_utility::evm::{maybe_borrowed::MaybeBorrowed, Runtime}; +use sp_core::H160; + +pub struct TaggedRuntime<'borrow> { + pub kind: RuntimeKind, + pub inner: MaybeBorrowed<'borrow, Runtime>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RuntimeKind { + Create(H160), + Call(H160), + /// Special variant used only in `StackExecutor::execute` + Execute, +} diff --git a/modules/evm/src/tests.rs b/modules/evm/src/tests.rs index b1f914d523..412d66140b 100644 --- a/modules/evm/src/tests.rs +++ b/modules/evm/src/tests.rs @@ -115,13 +115,13 @@ fn should_calculate_contract_address() { Ok(H160::from_str("d654cB21c05cb14895baae28159b1107e9DbD6E4").unwrap()) ); - executor.state_mut().inc_nonce(addr); + assert_ok!(executor.state_mut().inc_nonce(addr)); assert_eq!( executor.create_address(evm::CreateScheme::Legacy { caller: addr }), Ok(H160::from_str("97784910F057B07bFE317b0552AE23eF34644Aed").unwrap()) ); - executor.state_mut().inc_nonce(addr); + assert_ok!(executor.state_mut().inc_nonce(addr)); assert_eq!( executor.create_address(evm::CreateScheme::Legacy { caller: addr }), Ok(H160::from_str("82155a21E0Ccaee9D4239a582EB2fDAC1D9237c5").unwrap()) @@ -777,7 +777,7 @@ fn publish_factory() { ) .unwrap(); assert_eq!(result.exit_reason, ExitReason::Succeed(ExitSucceed::Returned)); - assert_eq!(result.used_gas.as_u64(), 155_879u64); + assert_eq!(result.used_gas.as_u64(), 155925); assert_eq!(result.used_storage, 461); assert_eq!( balance(alice()), @@ -818,7 +818,7 @@ fn create_nft_contract_works() { from: NetworkContractSource::get(), contract: MIRRORED_TOKENS_ADDRESS_START | H160::from_low_u64_be(MIRRORED_NFT_ADDRESS_START), logs: vec![], - used_gas: 93183, + used_gas: 93197, used_storage: 284, })); assert_eq!(EVM::network_contract_index(), MIRRORED_NFT_ADDRESS_START + 1); @@ -888,7 +888,7 @@ fn create_predeploy_contract_works() { from: NetworkContractSource::get(), contract: addr, logs: vec![], - used_gas: 93183, + used_gas: 93197, used_storage: 284, })); @@ -1877,7 +1877,7 @@ fn evm_execute_mode_should_work() { CallInfo { exit_reason: ExitReason::Succeed(ExitSucceed::Stopped), value: vec![], - used_gas: U256::from(142_445), + used_gas: U256::from(142451), used_storage: expected_used_storage, logs: vec![] } @@ -1904,7 +1904,7 @@ fn evm_execute_mode_should_work() { CallInfo { exit_reason: ExitReason::Succeed(ExitSucceed::Stopped), value: vec![], - used_gas: U256::from(259_561), + used_gas: U256::from(259573), used_storage: expected_used_storage, logs: vec![] } @@ -1964,7 +1964,7 @@ fn evm_execute_mode_should_work() { CallInfo { exit_reason: ExitReason::Succeed(ExitSucceed::Stopped), value: vec![], - used_gas: U256::from(110_469), + used_gas: U256::from(110475), used_storage: expected_used_storage, logs: vec![] } @@ -2007,7 +2007,7 @@ fn evm_execute_mode_should_work() { CallInfo { exit_reason: ExitReason::Succeed(ExitSucceed::Stopped), value: vec![], - used_gas: U256::from(93_369), + used_gas: U256::from(93375), used_storage: expected_used_storage, logs: vec![] } @@ -2293,7 +2293,7 @@ fn auto_publish_works() { from: alice(), contract: factory, logs: vec![], - used_gas: 593209, + used_gas: 593369, used_storage: 2609, })); @@ -2328,7 +2328,7 @@ fn auto_publish_works() { data: vec![], }, ], - used_gas: 387664, + used_gas: 387768, used_storage: 1530, })); @@ -2398,7 +2398,7 @@ fn auto_publish_works() { data: vec![], }, ], - used_gas: 370564, + used_gas: 370668, used_storage: 1466, })); @@ -2455,7 +2455,7 @@ fn auto_publish_works() { ], data: vec![], }], - used_gas: 147214, + used_gas: 147228, used_storage: 407, })); diff --git a/modules/honzon-bridge/src/mock.rs b/modules/honzon-bridge/src/mock.rs index 5a5b6cb0d4..710c2a0d88 100644 --- a/modules/honzon-bridge/src/mock.rs +++ b/modules/honzon-bridge/src/mock.rs @@ -262,7 +262,7 @@ pub fn deploy_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 5131, })); diff --git a/primitives/src/evm.rs b/primitives/src/evm.rs index 9eb096969e..3df77f3e3c 100644 --- a/primitives/src/evm.rs +++ b/primitives/src/evm.rs @@ -73,6 +73,8 @@ pub struct Vicinity { pub block_difficulty: Option, /// Environmental base fee per gas. pub block_base_fee_per_gas: Option, + /// Environmental randomness. + pub block_randomness: Option, } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] diff --git a/runtime/common/src/bench/mod.rs b/runtime/common/src/bench/mod.rs index 52d64ce98e..f0de8a9cc4 100644 --- a/runtime/common/src/bench/mod.rs +++ b/runtime/common/src/bench/mod.rs @@ -22,7 +22,10 @@ pub use crate::{precompile::mock::*, DEXPrecompile, EVMPrecompile, OraclePrecompile}; use frame_support::assert_ok; use hex_literal::hex; -use module_evm::{precompiles::Precompile, Context}; +use module_evm::{ + precompiles::{tests::MockPrecompileHandle, Precompile}, + Context, +}; use module_support::AddressMapping; use orml_traits::DataFeeder; use primitives::currency::{AssetMetadata, TokenInfo}; @@ -121,7 +124,7 @@ fn oracle_get_price(b: &mut Bencher) { "}; let resp = b - .bench(|| OraclePrecompile::::execute(&input, None, &context, false)) + .bench(|| OraclePrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); @@ -148,7 +151,7 @@ fn evm_query_new_contract_extra_bytes(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); @@ -175,7 +178,7 @@ fn evm_query_storage_deposit_per_byte(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); @@ -216,7 +219,7 @@ fn evm_query_maintainer(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); @@ -243,7 +246,7 @@ fn evm_query_developer_deposit(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); } @@ -269,7 +272,7 @@ fn evm_query_publication_fee(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); } @@ -297,7 +300,7 @@ fn evm_query_developer_status(b: &mut Bencher) { "}; let resp = b - .bench(|| EVMPrecompile::::execute(&input, None, &context, false)) + .bench(|| EVMPrecompile::::execute(&mut MockPrecompileHandle::new(&input, None, &context, false))) .unwrap(); assert_eq!(resp.output, expected_output); } diff --git a/runtime/common/src/precompile/dex.rs b/runtime/common/src/precompile/dex.rs index 56f02e1ad8..2c29989c4e 100644 --- a/runtime/common/src/precompile/dex.rs +++ b/runtime/common/src/precompile/dex.rs @@ -16,17 +16,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use frame_support::traits::Get; use module_dex::WeightInfo; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::{DEXManager, SwapLimit}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -64,23 +60,16 @@ where Runtime: module_evm::Config + module_dex::Config + module_prices::Config, module_dex::Pallet: DEXManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::< Action, Runtime::AccountId, Runtime::AddressMapping, ::Erc20InfoMapping, - >::new(input, target_gas_limit(target_gas)); - - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } + >::new(handle.input()); let action = input.action()?; @@ -102,9 +91,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint_tuple(vec![balance_a, balance_b]), - logs: Default::default(), }) } Action::GetLiquidityTokenAddress => { @@ -122,9 +109,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address(address), - logs: Default::default(), }) } Action::GetSwapTargetAmount => { @@ -148,9 +133,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(target), - logs: Default::default(), }) } Action::GetSwapSupplyAmount => { @@ -174,9 +157,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(supply), - logs: Default::default(), }) } Action::SwapWithExactSupply => { @@ -198,17 +179,15 @@ where let (_, value) = as DEXManager>::swap_with_specific_path(&who, &path, SwapLimit::ExactSupply(supply_amount, min_target_amount)) .map_err(|e| - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: Into::<&str>::into(e).as_bytes().to_vec(), - cost: target_gas_limit(target_gas).unwrap_or_default(), - })?; + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: Into::<&str>::into(e).as_bytes().to_vec(), + } + )?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(value), - logs: Default::default(), }) } Action::SwapWithExactTarget => { @@ -230,17 +209,15 @@ where let (value, _) = as DEXManager>::swap_with_specific_path(&who, &path, SwapLimit::ExactTarget(max_supply_amount, target_amount)) .map_err(|e| - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: Output::encode_error_msg("DEX SwapWithExactTarget failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), - })?; + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: Output::encode_error_msg("DEX SwapWithExactTarget failed", e), + } + )?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(value), - logs: Default::default(), }) } Action::AddLiquidity => { @@ -269,14 +246,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("DEX AddLiquidity failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::RemoveLiquidity => { @@ -305,14 +279,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("DEX RemoveLiquidity failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } } @@ -327,14 +298,13 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input< + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::< Action, Runtime::AccountId, Runtime::AddressMapping, ::Erc20InfoMapping, - >, - ) -> Result { + >::new(handle.input()); let action = input.action()?; let cost: u64 = match action { @@ -483,7 +453,7 @@ mod tests { use crate::precompile::mock::{alice_evm_addr, new_test_ext, DexModule, RuntimeOrigin, Test, ALICE, AUSD, DOT}; use frame_support::{assert_noop, assert_ok}; use hex_literal::hex; - use module_evm::ExitRevert; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context, ExitRevert}; type DEXPrecompile = crate::DEXPrecompile; @@ -525,7 +495,7 @@ mod tests { 00000000000000000000000000000000 000000000000000000000000000f4240 "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -567,7 +537,7 @@ mod tests { 000000000000000000000000 0000000000000000000200000000010000000002 "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -581,11 +551,10 @@ mod tests { "}; assert_noop!( - DEXPrecompile::execute(&input, Some(10_000), &context, false), + DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currency id".into(), - cost: target_gas_limit(Some(10_000)).unwrap(), } ); }); @@ -633,7 +602,7 @@ mod tests { 00000000000000000000000000000000 000000000000000000000000000003dd "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -681,7 +650,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000001 "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -733,7 +702,7 @@ mod tests { 00000000000000000000000000000000 000000000000000000000000000003dd "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -785,7 +754,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000001 "}; - let resp = DEXPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = DEXPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); diff --git a/runtime/common/src/precompile/evm.rs b/runtime/common/src/precompile/evm.rs index 68e8bf7802..f2bc434f51 100644 --- a/runtime/common/src/precompile/evm.rs +++ b/runtime/common/src/precompile/evm.rs @@ -18,14 +18,12 @@ use super::{ input::{Input, InputPricer, InputT, Output}, - target_gas_limit, weights::PrecompileWeights, }; use crate::WeightToGas; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, WeightInfo, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, WeightInfo, }; use module_support::EVMManager; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -67,22 +65,14 @@ where Runtime: module_evm::Config + module_prices::Config, module_evm::Pallet: EVMManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -90,18 +80,14 @@ where let output = module_evm::Pallet::::query_new_contract_extra_bytes(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(output), - logs: Default::default(), }) } Action::QueryStorageDepositPerByte => { let deposit = module_evm::Pallet::::query_storage_deposit_per_byte(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(deposit), - logs: Default::default(), }) } Action::QueryMaintainer => { @@ -111,33 +97,26 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Into::<&str>::into(e).as_bytes().to_vec(), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address(maintainer), - logs: Default::default(), }) } Action::QueryDeveloperDeposit => { let deposit = module_evm::Pallet::::query_developer_deposit(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(deposit), - logs: Default::default(), }) } Action::QueryPublicationFee => { let fee = module_evm::Pallet::::query_publication_fee(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(fee), - logs: Default::default(), }) } Action::TransferMaintainer => { @@ -159,14 +138,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Evm TransferMaintainer failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::PublishContract => { @@ -176,15 +152,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Evm PublishContract failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::DisableDeveloperAccount => { @@ -193,15 +166,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Evm DisableDeveloperAccount failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::EnableDeveloperAccount => { @@ -210,15 +180,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Evm EnableDeveloperAccount failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::QueryDeveloperStatus => { @@ -226,9 +193,7 @@ where let developer_status = >::query_developer_status(who); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bool(developer_status), - logs: Default::default(), }) } } @@ -243,9 +208,10 @@ where { const BASE_COST: u64 = 50; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost = match action { Action::QueryNewContractExtraBytes => { @@ -315,7 +281,7 @@ mod tests { }; use frame_support::assert_ok; use hex_literal::hex; - use module_evm::{ExitReason, Runner}; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context, ExitError, ExitReason, Runner}; use sp_core::H160; type EVMPrecompile = crate::EVMPrecompile; @@ -341,7 +307,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -352,7 +318,7 @@ mod tests { 000000000000000000000000 1000000000000000000000000000000000000001 "}; - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, [0u8; 0].to_vec()); @@ -370,7 +336,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000001 "}; - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -383,7 +349,7 @@ mod tests { 000000000000000000000000 1000000000000000000000000000000000000001 "}; - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, [0u8; 0].to_vec()); @@ -401,7 +367,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -492,7 +458,7 @@ mod tests { "}; // publish contract with precompile - let resp = EVMPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = EVMPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, [0u8; 0].to_vec()); diff --git a/runtime/common/src/precompile/evm_accounts.rs b/runtime/common/src/precompile/evm_accounts.rs index ed005122b1..62dad55be4 100644 --- a/runtime/common/src/precompile/evm_accounts.rs +++ b/runtime/common/src/precompile/evm_accounts.rs @@ -16,16 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputT, Output}; use crate::WeightToGas; use frame_support::{pallet_prelude::IsType, traits::Get}; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_evm_accounts::WeightInfo; use module_support::EVMAccountsManager; @@ -57,22 +53,14 @@ where Runtime: module_evm_accounts::Config + module_prices::Config, module_evm_accounts::Pallet: EVMAccountsManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -82,9 +70,7 @@ where let output = module_evm_accounts::Pallet::::get_account_id(&address); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_fixed_bytes(output.into().as_ref()), - logs: Default::default(), }) } Action::GetEvmAddress => { @@ -100,9 +86,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address(address), - logs: Default::default(), }) } Action::ClaimDefaultEvmAddress => { @@ -118,15 +102,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("EvmAccounts ClaimDefaultEvmAddress failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address(address), - logs: Default::default(), }) } } @@ -141,9 +122,10 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost = match action { Action::GetAccountId => { @@ -172,6 +154,7 @@ mod tests { use crate::precompile::mock::{alice_evm_addr, new_test_ext, EvmAddress, Test, ALICE}; use frame_support::assert_noop; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use parity_scale_codec::Encode; use sp_core::blake2_256; use std::str::FromStr; @@ -199,7 +182,8 @@ mod tests { 65766d3a 1000000000000000000000000000000000000001 0000000000000000 "}; - let resp = EVMAccountsPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + EVMAccountsPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -226,7 +210,8 @@ mod tests { 000000000000000000000000 1000000000000000000000000000000000000001 "}; - let resp = EVMAccountsPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + EVMAccountsPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -242,7 +227,8 @@ mod tests { 000000000000000000000000 0000000000000000000000000000000000000000 "}; - let resp = EVMAccountsPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + EVMAccountsPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -276,17 +262,17 @@ mod tests { 000000000000000000000000 8f2703bbe0abeaf09b384374959ffac5f7d0d69f "}; - let resp = EVMAccountsPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + EVMAccountsPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); // call again, the evm address already mapped assert_noop!( - EVMAccountsPrecompile::execute(&input, Some(100_000), &context, false), + EVMAccountsPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(100_000), &context, false)), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "EvmAccounts ClaimDefaultEvmAddress failed: AccountIdHasMapped".into(), - cost: target_gas_limit(Some(100_000)).unwrap(), } ); }); diff --git a/runtime/common/src/precompile/homa.rs b/runtime/common/src/precompile/homa.rs index 74d3b4d4f6..880cc7633a 100644 --- a/runtime/common/src/precompile/homa.rs +++ b/runtime/common/src/precompile/homa.rs @@ -16,16 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use frame_support::traits::Get; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::HomaManager; @@ -66,23 +62,16 @@ where Runtime: module_evm::Config + module_homa::Config + module_prices::Config, module_homa::Pallet: HomaManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::< Action, Runtime::AccountId, ::AddressMapping, Runtime::Erc20InfoMapping, - >::new(input, target_gas_limit(target_gas)); - - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } + >::new(handle.input()); let action = input.action()?; @@ -101,15 +90,12 @@ where |e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Homa Mint failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), }, )?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::RequestRedeem => { @@ -129,14 +115,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Homa RequestRedeem failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::GetExchangeRate => { @@ -144,18 +127,14 @@ where as HomaManager>::get_exchange_rate(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(rate.into_inner()), - logs: Default::default(), }) } Action::GetEstimatedRewardRate => { let rate = as HomaManager>::get_estimated_reward_rate(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(rate.into_inner()), - logs: Default::default(), }) } Action::GetCommissionRate => { @@ -163,9 +142,7 @@ where as HomaManager>::get_commission_rate(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(rate.into_inner()), - logs: Default::default(), }) } Action::GetFastMatchFee => { @@ -173,9 +150,7 @@ where as HomaManager>::get_fast_match_fee(); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(rate.into_inner()), - logs: Default::default(), }) } } @@ -190,9 +165,10 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost: u64 = match action { @@ -240,6 +216,7 @@ mod tests { }; use frame_support::assert_ok; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use sp_runtime::{FixedPointNumber, FixedU128}; type HomaPrecompile = super::HomaPrecompile; @@ -283,7 +260,7 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000003b9aca00 "}; - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); }); @@ -332,7 +309,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); }); @@ -355,7 +332,7 @@ mod tests { // encoded value of FixedU128::saturating_from_rational(1,10); let expected_output = hex! {"00000000000000000000000000000000 0000000000000000016345785d8a0000"}.to_vec(); - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output); }); @@ -386,7 +363,7 @@ mod tests { // encoded value of FixedU128::saturating_from_rational(1,10); let expected_output = hex! {"00000000000000000000000000000000 0000000000000000016345785d8a0000"}.to_vec(); - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output); }); @@ -415,7 +392,7 @@ mod tests { // encoded value of FixedU128::saturating_from_rational(1,10); let expected_output = hex! {"00000000000000000000000000000000 0000000000000000016345785d8a0000"}.to_vec(); - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output); }); @@ -444,7 +421,7 @@ mod tests { // encoded value of FixedU128::saturating_from_rational(1,10); let expected_output = hex! {"00000000000000000000000000000000 0000000000000000016345785d8a0000"}.to_vec(); - let res = HomaPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HomaPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output); }); diff --git a/runtime/common/src/precompile/honzon.rs b/runtime/common/src/precompile/honzon.rs index 4ae00436ca..dfff83f196 100644 --- a/runtime/common/src/precompile/honzon.rs +++ b/runtime/common/src/precompile/honzon.rs @@ -16,16 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use frame_support::traits::Get; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_honzon::WeightInfo; use module_support::HonzonManager; @@ -64,22 +60,14 @@ where Runtime: module_evm::Config + module_honzon::Config + module_prices::Config, module_honzon::Pallet: HonzonManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -104,15 +92,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Honzon AdjustLoan failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } )?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::CloseLoanByDex => { @@ -135,15 +120,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Honzon CloseLoanByDex failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } )?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::GetPosition => { @@ -159,9 +141,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint_tuple(vec![collateral, debit]), - logs: Default::default(), }) } Action::GetCollateralParameters => { @@ -175,9 +155,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint_array(params), - logs: Default::default(), }) } Action::GetCurrentCollateralRatio => { @@ -193,9 +171,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(ratio.into_inner()), - logs: Default::default(), }) } Action::GetDebitExchangeRate => { @@ -209,9 +185,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(exchange_rate.into_inner()), - logs: Default::default(), }) } } @@ -226,9 +200,10 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost: u64 = match action { @@ -311,6 +286,7 @@ mod tests { }; use frame_support::assert_ok; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use module_support::{Rate, Ratio}; use orml_traits::Change; use sp_runtime::FixedPointNumber; @@ -354,7 +330,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000001000 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(Loans::positions(DOT, alice()).collateral, 268435456); assert_eq!(Loans::positions(DOT, alice()).debit, 4096) @@ -432,7 +408,7 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000100000000 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(Loans::positions(DOT, alice()).debit, 0); @@ -485,7 +461,7 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000174876e800 00000000000000000000000000000000 000000000000000000000000000f4240 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); @@ -533,7 +509,7 @@ mod tests { 00000000000000000000000000000000 000000000000000018fae27693b40000 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); @@ -582,7 +558,7 @@ mod tests { let expected_output = hex! {" 00000000000000000000000000000000 000000000000152d02c7e14af6800000 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); @@ -617,7 +593,7 @@ mod tests { let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000de0b6b3a7640000 "}; - let res = HonzonPrecompile::execute(&input, None, &context, false).unwrap(); + let res = HonzonPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }) diff --git a/runtime/common/src/precompile/incentives.rs b/runtime/common/src/precompile/incentives.rs index 827dc71446..62adb03e62 100644 --- a/runtime/common/src/precompile/incentives.rs +++ b/runtime/common/src/precompile/incentives.rs @@ -16,16 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use frame_support::traits::Get; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_incentives::WeightInfo; use module_support::{IncentivesManager, PoolId}; @@ -65,22 +61,14 @@ where Runtime: module_evm::Config + module_incentives::Config + module_prices::Config, module_incentives::Pallet: IncentivesManager, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -88,7 +76,7 @@ where let pool = input.u32_at(1)?; let pool_currency_id = input.currency_id_at(2)?; let reward_currency_id = input.currency_id_at(3)?; - let pool_id = init_pool_id(pool, pool_currency_id, target_gas)?; + let pool_id = init_pool_id(pool, pool_currency_id)?; let value = as IncentivesManager< Runtime::AccountId, @@ -99,9 +87,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(value), - logs: Default::default(), }) } Action::DepositDexShare => { @@ -118,14 +104,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Incentives DepositDexShare failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::WithdrawDexShare => { @@ -142,21 +125,18 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Incentives WithdrawDexShare failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::ClaimRewards => { let who = input.account_id_at(1)?; let pool = input.u32_at(2)?; let pool_currency_id = input.currency_id_at(3)?; - let pool_id = init_pool_id(pool, pool_currency_id, target_gas)?; + let pool_id = init_pool_id(pool, pool_currency_id)?; as IncentivesManager< Runtime::AccountId, @@ -167,20 +147,17 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Incentives ClaimRewards failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::GetClaimRewardDeductionRate => { let pool = input.u32_at(1)?; let pool_currency_id = input.currency_id_at(2)?; - let pool_id = init_pool_id(pool, pool_currency_id, target_gas)?; + let pool_id = init_pool_id(pool, pool_currency_id)?; let value = as IncentivesManager< Runtime::AccountId, @@ -191,16 +168,14 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(value.into_inner()), - logs: Default::default(), }) } Action::GetPendingRewards => { // solidity abi encode array will add an offset at input[1] let pool = input.u32_at(2)?; let pool_currency_id = input.currency_id_at(3)?; - let pool_id = init_pool_id(pool, pool_currency_id, target_gas)?; + let pool_id = init_pool_id(pool, pool_currency_id)?; let who = input.account_id_at(4)?; let reward_currency_ids_len = input.u32_at(5)?; let mut reward_currency_ids = vec![]; @@ -217,9 +192,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint_array(value), - logs: Default::default(), }) } } @@ -234,9 +207,10 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost: u64 = match action { @@ -322,11 +296,7 @@ where } } -fn init_pool_id( - pool_id_number: u32, - pool_currency_id: CurrencyId, - target_gas: Option, -) -> Result { +fn init_pool_id(pool_id_number: u32, pool_currency_id: CurrencyId) -> Result { match pool_id_number { 0 => Ok(PoolId::Loans(pool_currency_id)), 1 => Ok(PoolId::Dex(pool_currency_id)), @@ -334,7 +304,6 @@ fn init_pool_id( _ => Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Incentives: Invalid enum value".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), }), } } @@ -348,6 +317,7 @@ mod tests { }; use frame_support::assert_ok; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use module_support::Rate; use orml_rewards::PoolInfo; use orml_traits::MultiCurrency; @@ -385,7 +355,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000064 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); @@ -413,7 +384,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000100000 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!( @@ -457,7 +429,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000100 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!( @@ -524,7 +497,8 @@ mod tests { 000000000000000000000000 0000000000000000000100000000000000000000 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!( @@ -569,7 +543,8 @@ mod tests { 00000000000000000000000000000000 0000000000000000016345785d8a0000 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); @@ -644,7 +619,8 @@ mod tests { 00000000000000000000000000000000 000000000000000000000000000001f4 "}; - let res = IncentivesPrecompile::execute(&input, None, &context, false).unwrap(); + let res = + IncentivesPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }) diff --git a/runtime/common/src/precompile/input.rs b/runtime/common/src/precompile/input.rs index 9ae6bc4744..56527e1708 100644 --- a/runtime/common/src/precompile/input.rs +++ b/runtime/common/src/precompile/input.rs @@ -22,7 +22,7 @@ use sp_std::{marker::PhantomData, result::Result, vec::Vec}; use crate::WeightToGas; use ethabi::Token; use frame_support::traits::Get; -use module_evm::{runner::state::PrecompileFailure, ExitRevert}; +use module_evm::{ExitRevert, PrecompileFailure}; use module_support::{AddressMapping as AddressMappingT, Erc20InfoMapping as Erc20InfoMappingT}; use primitives::{Balance, CurrencyId, DexShare}; use sp_core::{H160, U256}; @@ -61,16 +61,14 @@ pub trait InputT { pub struct Input<'a, Action, AccountId, AddressMapping, Erc20InfoMapping> { content: &'a [u8], - target_gas: Option, _marker: PhantomData<(Action, AccountId, AddressMapping, Erc20InfoMapping)>, } impl<'a, Action, AccountId, AddressMapping, Erc20InfoMapping> Input<'a, Action, AccountId, AddressMapping, Erc20InfoMapping> { - pub fn new(content: &'a [u8], target_gas: Option) -> Self { + pub fn new(content: &'a [u8]) -> Self { Self { content, - target_gas, _marker: PhantomData, } } @@ -104,7 +102,6 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid input".into(), - cost: self.target_gas.unwrap_or_default(), } ); @@ -116,13 +113,11 @@ where let action = u32::from_be_bytes(param.try_into().map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid action".into(), - cost: self.target_gas.unwrap_or_default(), })?); action.try_into().map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid action".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -150,7 +145,6 @@ where Erc20InfoMapping::decode_evm_address(address).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currency id".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -159,7 +153,6 @@ where decode_i128(param).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to decode i128".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -173,7 +166,6 @@ where param.try_into().map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into Balance".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -182,7 +174,6 @@ where param.try_into().map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into u64".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -191,7 +182,6 @@ where param.try_into().map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into u32".into(), - cost: self.target_gas.unwrap_or_default(), }) } @@ -222,7 +212,6 @@ where Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to decode bool".into(), - cost: self.target_gas.unwrap_or_default(), }) } } @@ -368,7 +357,7 @@ mod tests { 00000000 ffffffffffffffffffffffffffffffff00000000000000000000000000000001 "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!( input.nth_param(1, None), &hex!("ffffffffffffffffffffffffffffffff00000000000000000000000000000001")[..] @@ -378,29 +367,27 @@ mod tests { PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid input".into(), - cost: 10, } ); } #[test] fn action_works() { - let input = TestInput::new(&hex!("00000000")[..], None); + let input = TestInput::new(&hex!("00000000")[..]); assert_ok!(input.action(), Action::QueryBalance); - let input = TestInput::new(&hex!("00000001")[..], None); + let input = TestInput::new(&hex!("00000001")[..]); assert_ok!(input.action(), Action::Transfer); - let input = TestInput::new(&hex!("00000002")[..], None); + let input = TestInput::new(&hex!("00000002")[..]); assert_ok!(input.action(), Action::Unknown); - let input = TestInput::new(&hex!("00000003")[..], Some(10)); + let input = TestInput::new(&hex!("00000003")[..]); assert_eq!( input.action(), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid action".into(), - cost: 10, }) ); } @@ -415,7 +402,7 @@ mod tests { ffffffffffffffffffffffff ff00000000000000000000000000000000000003 "}; - let input = TestInput::new(&data[..], None); + let input = TestInput::new(&data[..]); assert_ok!( input.account_id_at(1), MockAddressMapping::get_account_id(&H160::from_str("ff00000000000000000000000000000000000001").unwrap()) @@ -439,7 +426,7 @@ mod tests { ffffffffffffffffffffffff 0000000000000000000000000000000000000002 ffffffffffffffffffffffff ff00000000000000000000000000000000000003 "}; - let input = TestInput::new(&data[..], None); + let input = TestInput::new(&data[..]); assert_ok!( input.evm_address_at(1), H160::from_str("ff00000000000000000000000000000000000001").unwrap() @@ -456,13 +443,12 @@ mod tests { #[test] fn currency_id_works() { - let input = TestInput::new(&[0u8; 100][..], Some(10)); + let input = TestInput::new(&[0u8; 100][..]); assert_err!( input.currency_id_at(1), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currency id".into(), - cost: 10, } ); @@ -474,7 +460,7 @@ mod tests { ffffffffffffffffffffffff 0000000000000000000100000000000000000002 "}; - let input = TestInput::new(&data[..], None); + let input = TestInput::new(&data[..]); assert_ok!(input.currency_id_at(1), CurrencyId::Token(TokenSymbol::ACA)); assert_ok!(input.currency_id_at(2), CurrencyId::Token(TokenSymbol::AUSD)); assert_ok!(input.currency_id_at(3), CurrencyId::Token(TokenSymbol::DOT)); @@ -488,7 +474,7 @@ mod tests { 00000000000000000000000000000000ffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff "}; - let input = TestInput::new(&data[..], None); + let input = TestInput::new(&data[..]); assert_ok!(input.u256_at(1), U256::from(127u128)); assert_ok!(input.u256_at(2), U256::from(u128::MAX)); assert_ok!(input.u256_at(3), U256::MAX); @@ -502,7 +488,7 @@ mod tests { 00000000000000000000000000000000 ffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!(input.balance_at(1), 127u128); assert_ok!(input.balance_at(2), u128::MAX); assert_eq!( @@ -510,7 +496,6 @@ mod tests { Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into Balance".into(), - cost: 10, }) ); } @@ -523,7 +508,7 @@ mod tests { 000000000000000000000000000000000000000000000000 ffffffffffffffff 000000000000000000000000000000000000000000000001 ffffffffffffffff "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!(input.u64_at(1), 127u64); assert_ok!(input.u64_at(2), u64::MAX); assert_eq!( @@ -531,7 +516,6 @@ mod tests { Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into u64".into(), - cost: 10, }) ); } @@ -544,7 +528,7 @@ mod tests { 00000000000000000000000000000000000000000000000000000000 ffffffff 00000000000000000000000000000000000000000000000000000001 ffffffff "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!(input.u32_at(1), 127u32); assert_ok!(input.u32_at(2), u32::MAX); assert_eq!( @@ -552,7 +536,6 @@ mod tests { Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to convert uint256 into u32".into(), - cost: 10, }) ); } @@ -568,14 +551,13 @@ mod tests { ff000000000000000000000000000000 ffffffffffffffffffffffffffffffff 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!(input.i128_at(1), 127_i128); assert_eq!( input.i128_at(2), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to decode i128".into(), - cost: 10, }) ); assert_ok!(input.i128_at(3), -1_i128); @@ -585,7 +567,6 @@ mod tests { Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to decode i128".into(), - cost: 10, }) ); assert_ok!(input.i128_at(6), 0); @@ -599,7 +580,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000002 "}; - let input = TestInput::new(&data[..], Some(10)); + let input = TestInput::new(&data[..]); assert_ok!(input.bool_at(1), false); assert_ok!(input.bool_at(2), true); assert_eq!( @@ -607,7 +588,6 @@ mod tests { Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "failed to decode bool".into(), - cost: 10, }) ); } diff --git a/runtime/common/src/precompile/liquid_crowdloan.rs b/runtime/common/src/precompile/liquid_crowdloan.rs index deca49867d..88cb23fc8b 100644 --- a/runtime/common/src/precompile/liquid_crowdloan.rs +++ b/runtime/common/src/precompile/liquid_crowdloan.rs @@ -16,15 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_liquid_crowdloan::WeightInfo; use module_support::Erc20InfoMapping as _; @@ -48,22 +44,14 @@ impl Precompile for LiquidCrowdloanPrecompile where Runtime: module_evm::Config + module_prices::Config + module_liquid_crowdloan::Config, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -76,16 +64,13 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("LiquidCrowdloan redeem failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; log::debug!(target: "evm", "liuqid_crowdloan: Redeem who: {:?}, amount: {:?}, output: {:?}", who, amount, redeem_amount); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(redeem_amount), - logs: Default::default(), }) } Action::GetRedeemCurrency => { @@ -96,9 +81,7 @@ where log::debug!(target: "evm", "liuqid_crowdloan: GetRedeemCurrency output: {:?}", address); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address(address), - logs: Default::default(), }) } } @@ -113,9 +96,10 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let action = input.action()?; let cost = match action { @@ -143,6 +127,7 @@ mod tests { }; use frame_support::assert_ok; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use orml_traits::MultiCurrency; use sp_runtime::traits::AccountIdConversion; @@ -185,7 +170,8 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000003b9aca00 "}; - let res = LiquidCrowdloanPrecompile::execute(&input, None, &context, false).unwrap(); + let res = LiquidCrowdloanPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); @@ -233,7 +219,8 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000028fa6ae00 "}; - let res = LiquidCrowdloanPrecompile::execute(&input, None, &context, false).unwrap(); + let res = LiquidCrowdloanPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); @@ -259,7 +246,8 @@ mod tests { 000000000000000000000000 0000000000000000000100000000000000000002 "}; - let res = LiquidCrowdloanPrecompile::execute(&input, None, &context, false).unwrap(); + let res = LiquidCrowdloanPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); @@ -270,7 +258,8 @@ mod tests { 000000000000000000000000 0000000000000000000100000000000000000003 "}; - let res = LiquidCrowdloanPrecompile::execute(&input, None, &context, false).unwrap(); + let res = LiquidCrowdloanPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(res.exit_status, ExitSucceed::Returned); assert_eq!(res.output, expected_output.to_vec()); }); diff --git a/runtime/common/src/precompile/mod.rs b/runtime/common/src/precompile/mod.rs index 4188235f75..3203c58d3b 100644 --- a/runtime/common/src/precompile/mod.rs +++ b/runtime/common/src/precompile/mod.rs @@ -31,11 +31,11 @@ use module_evm::{ Blake2F, Bn128Add, Bn128Mul, Bn128Pairing, ECRecover, ECRecoverPublicKey, Identity, IstanbulModexp, Modexp, Precompile, Ripemd160, Sha256, Sha3FIPS256, Sha3FIPS512, }, - runner::state::{PrecompileFailure, PrecompileResult, PrecompileSet}, - Context, ExitRevert, + ExitRevert, IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileResult, PrecompileSet, }; use module_support::{PrecompileCallerFilter, PrecompilePauseFilter}; use sp_core::H160; +use sp_runtime::traits::Zero; use sp_std::{collections::btree_set::BTreeSet, marker::PhantomData}; pub mod dex; @@ -98,10 +98,6 @@ pub const INCENTIVES: H160 = H160(hex!("000000000000000000000000000000000000040a pub const XTOKENS: H160 = H160(hex!("000000000000000000000000000000000000040b")); pub const LIQUID_CROWDLOAN: H160 = H160(hex!("000000000000000000000000000000000000040c")); -pub fn target_gas_limit(target_gas: Option) -> Option { - target_gas.map(|x| x.saturating_div(10).saturating_mul(9)) // 90% -} - pub struct AllPrecompiles { set: BTreeSet, _marker: PhantomData<(R, F, E)>, @@ -236,15 +232,14 @@ where IncentivesPrecompile: Precompile, XtokensPrecompile: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option { - if !self.is_precompile(address) { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + let context = handle.context(); + let address = handle.code_address(); + + if let IsPrecompileResult::Answer { + is_precompile: false, .. + } = self.is_precompile(address, u64::zero()) + { return None; } @@ -254,7 +249,6 @@ where return Some(Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "precompile is paused".into(), - cost: target_gas.unwrap_or_default(), })); } @@ -263,43 +257,42 @@ where return Some(Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "cannot be called with DELEGATECALL or CALLCODE".into(), - cost: target_gas.unwrap_or_default(), })); } - log::trace!(target: "evm", "Precompile begin, address: {:?}, input: {:?}, target_gas: {:?}, context: {:?}", address, input, target_gas, context); + log::trace!(target: "evm", "Precompile begin, address: {:?}, input: {:?}, context: {:?}", address, handle.input(), context); // https://github.com/ethereum/go-ethereum/blob/9357280fce5c5d57111d690a336cca5f89e34da6/core/vm/contracts.go#L83 let result = if address == ECRECOVER { - Some(ECRecover::execute(input, target_gas, context, is_static)) + Some(ECRecover::execute(handle)) } else if address == SHA256 { - Some(Sha256::execute(input, target_gas, context, is_static)) + Some(Sha256::execute(handle)) } else if address == RIPEMD { - Some(Ripemd160::execute(input, target_gas, context, is_static)) + Some(Ripemd160::execute(handle)) } else if address == IDENTITY { - Some(Identity::execute(input, target_gas, context, is_static)) + Some(Identity::execute(handle)) } else if address == MODEXP { if R::config().increase_state_access_gas { - Some(Modexp::execute(input, target_gas, context, is_static)) + Some(Modexp::execute(handle)) } else { - Some(IstanbulModexp::execute(input, target_gas, context, is_static)) + Some(IstanbulModexp::execute(handle)) } } else if address == BN_ADD { - Some(Bn128Add::execute(input, target_gas, context, is_static)) + Some(Bn128Add::execute(handle)) } else if address == BN_MUL { - Some(Bn128Mul::execute(input, target_gas, context, is_static)) + Some(Bn128Mul::execute(handle)) } else if address == BN_PAIRING { - Some(Bn128Pairing::execute(input, target_gas, context, is_static)) + Some(Bn128Pairing::execute(handle)) } else if address == BLAKE2F { - Some(Blake2F::execute(input, target_gas, context, is_static)) + Some(Blake2F::execute(handle)) } // Non-standard precompile starts with 128 else if address == ECRECOVER_PUBLICKEY { - Some(ECRecoverPublicKey::execute(input, target_gas, context, is_static)) + Some(ECRecoverPublicKey::execute(handle)) } else if address == SHA3_256 { - Some(Sha3FIPS256::execute(input, target_gas, context, is_static)) + Some(Sha3FIPS256::execute(handle)) } else if address == SHA3_512 { - Some(Sha3FIPS512::execute(input, target_gas, context, is_static)) + Some(Sha3FIPS512::execute(handle)) } // Acala precompile else { @@ -308,7 +301,6 @@ where return Some(Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "NoPermission".into(), - cost: target_gas.unwrap_or_default(), })); } @@ -317,56 +309,58 @@ where return Some(Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Caller is not a system contract".into(), - cost: target_gas.unwrap_or_default(), })); } if address == MULTI_CURRENCY { - Some(MultiCurrencyPrecompile::::execute( - input, target_gas, context, is_static, - )) + Some(MultiCurrencyPrecompile::::execute(handle)) } else if address == NFT { - Some(NFTPrecompile::::execute(input, target_gas, context, is_static)) + Some(NFTPrecompile::::execute(handle)) } else if address == EVM { - Some(EVMPrecompile::::execute(input, target_gas, context, is_static)) + Some(EVMPrecompile::::execute(handle)) } else if address == ORACLE { - Some(OraclePrecompile::::execute(input, target_gas, context, is_static)) + Some(OraclePrecompile::::execute(handle)) } else if address == SCHEDULER { - Some(SchedulePrecompile::::execute(input, target_gas, context, is_static)) + Some(SchedulePrecompile::::execute(handle)) } else if address == DEX { - Some(DEXPrecompile::::execute(input, target_gas, context, is_static)) + Some(DEXPrecompile::::execute(handle)) } else if address == STABLE_ASSET { - Some(StableAssetPrecompile::::execute( - input, target_gas, context, is_static, - )) + Some(StableAssetPrecompile::::execute(handle)) } else if address == HOMA { - Some(HomaPrecompile::::execute(input, target_gas, context, is_static)) + Some(HomaPrecompile::::execute(handle)) } else if address == EVM_ACCOUNTS { - Some(EVMAccountsPrecompile::::execute( - input, target_gas, context, is_static, - )) + Some(EVMAccountsPrecompile::::execute(handle)) } else if address == HONZON { - Some(HonzonPrecompile::::execute(input, target_gas, context, is_static)) + Some(HonzonPrecompile::::execute(handle)) } else if address == INCENTIVES { - Some(IncentivesPrecompile::::execute( - input, target_gas, context, is_static, - )) + Some(IncentivesPrecompile::::execute(handle)) } else if address == XTOKENS { - Some(XtokensPrecompile::::execute(input, target_gas, context, is_static)) + Some(XtokensPrecompile::::execute(handle)) } else { - E::execute(&Default::default(), address, input, target_gas, context, is_static) + E::execute(&Default::default(), handle) } }; - log::trace!(target: "evm", "Precompile end, address: {:?}, input: {:?}, target_gas: {:?}, context: {:?}, result: {:?}", address, input, target_gas, context, result); + log::trace!(target: "evm", "Precompile end, address: {:?}, input: {:?}, context: {:?}, result: {:?}", address, handle.input(), handle.context(), result); if let Some(Err(PrecompileFailure::Revert { ref output, .. })) = result { log::debug!(target: "evm", "Precompile failed: {:?}", core::str::from_utf8(output)); }; result } - fn is_precompile(&self, address: H160) -> bool { - self.set.contains(&address) || E::is_precompile(&Default::default(), address) + fn is_precompile(&self, address: H160, _remaining_gas: u64) -> IsPrecompileResult { + let is_precompile = { + self.set.contains(&address) + || match E::is_precompile(&Default::default(), address, u64::zero()) { + IsPrecompileResult::Answer { is_precompile, .. } => is_precompile, + _ => false, + } + }; + + IsPrecompileResult::Answer { + is_precompile, + extra_cost: 0, + } } } @@ -382,23 +376,20 @@ impl PrecompileSet for AcalaPrecompiles where LiquidCrowdloanPrecompile: Precompile, { - fn execute( - &self, - address: H160, - input: &[u8], - gas_limit: Option, - context: &Context, - is_static: bool, - ) -> Option { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + let address = handle.code_address(); if address == LIQUID_CROWDLOAN { - Some(LiquidCrowdloanPrecompile::execute(input, gas_limit, context, is_static)) + Some(LiquidCrowdloanPrecompile::execute(handle)) } else { None } } - fn is_precompile(&self, address: H160) -> bool { - address == LIQUID_CROWDLOAN + fn is_precompile(&self, address: H160, _remaining_gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: address == LIQUID_CROWDLOAN, + extra_cost: 0, + } } } diff --git a/runtime/common/src/precompile/multicurrency.rs b/runtime/common/src/precompile/multicurrency.rs index 93116994de..a51fecb69c 100644 --- a/runtime/common/src/precompile/multicurrency.rs +++ b/runtime/common/src/precompile/multicurrency.rs @@ -16,10 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output}; use crate::WeightToGas; use frame_support::{ pallet_prelude::IsType, @@ -27,9 +24,8 @@ use frame_support::{ }; use module_currencies::WeightInfo; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::Erc20InfoMapping as Erc20InfoMappingT; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -69,30 +65,23 @@ where Runtime::AccountId: IsType, module_currencies::Pallet: MultiCurrencyT, { - fn execute(input: &[u8], target_gas: Option, context: &Context, _is_static: bool) -> PrecompileResult { - let input = Input::< - Action, - Runtime::AccountId, - ::AddressMapping, - Runtime::Erc20InfoMapping, - >::new(input, target_gas_limit(target_gas)); - + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let context = handle.context(); let currency_id = Runtime::Erc20InfoMapping::decode_evm_address(context.caller).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currency id".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; - let gas_cost = Pricer::::cost(&input, currency_id)?; + let gas_cost = Pricer::::cost(handle, currency_id)?; + handle.record_cost(gas_cost)?; - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } + let input = Input::< + Action, + Runtime::AccountId, + ::AddressMapping, + Runtime::Erc20InfoMapping, + >::new(handle.input()); let action = input.action()?; @@ -103,15 +92,12 @@ where let name = Runtime::Erc20InfoMapping::name(currency_id).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Get name failed".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!(target: "evm", "multicurrency: name: {:?}", name); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes(&name), - logs: Default::default(), }) } Action::QuerySymbol => { @@ -119,15 +105,12 @@ where Runtime::Erc20InfoMapping::symbol(currency_id).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Get symbol failed".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!(target: "evm", "multicurrency: symbol: {:?}", symbol); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes(&symbol), - logs: Default::default(), }) } Action::QueryDecimals => { @@ -135,15 +118,12 @@ where Runtime::Erc20InfoMapping::decimals(currency_id).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Get decimals failed".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!(target: "evm", "multicurrency: decimals: {:?}", decimals); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(decimals), - logs: Default::default(), }) } Action::QueryTotalIssuance => { @@ -153,9 +133,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(total_issuance), - logs: Default::default(), }) } Action::QueryBalance => { @@ -170,9 +148,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(balance), - logs: Default::default(), }) } Action::Transfer => { @@ -190,14 +166,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Multicurrency Transfer failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } Action::TransferToAccountId => { @@ -219,14 +192,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Multicurrency TransferToAccountId failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: vec![], - logs: Default::default(), }) } } @@ -242,15 +212,14 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input< + fn cost(handle: &mut impl PrecompileHandle, currency_id: CurrencyId) -> Result { + let input = Input::< Action, Runtime::AccountId, ::AddressMapping, Runtime::Erc20InfoMapping, - >, - currency_id: CurrencyId, - ) -> Result { + >::new(handle.input()); + let action = input.action()?; // Decode CurrencyId from EvmAddress @@ -317,6 +286,7 @@ mod tests { }; use frame_support::assert_noop; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; type MultiCurrencyPrecompile = crate::MultiCurrencyPrecompile; @@ -336,11 +306,10 @@ mod tests { "}; assert_noop!( - MultiCurrencyPrecompile::execute(&input, Some(10_000), &context, false), + MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currency id".into(), - cost: target_gas_limit(Some(10_000)).unwrap(), } ); }); @@ -369,7 +338,8 @@ mod tests { 4163616c61000000000000000000000000000000000000000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -382,7 +352,8 @@ mod tests { 4c50204163616c61202d204163616c6120446f6c6c6172000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -411,7 +382,8 @@ mod tests { 4143410000000000000000000000000000000000000000000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -424,7 +396,8 @@ mod tests { 4c505f4143415f41555344000000000000000000000000000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -451,14 +424,16 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000000000000c "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); // DexShare context.caller = lp_aca_ausd_evm_address(); - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -486,7 +461,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000077359400 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -497,7 +473,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -527,7 +504,8 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000e8d4a51000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -538,7 +516,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }) @@ -570,7 +549,8 @@ mod tests { // Token context.caller = aca_evm_address(); - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, [0u8; 0].to_vec()); @@ -580,11 +560,15 @@ mod tests { // DexShare context.caller = lp_aca_ausd_evm_address(); assert_noop!( - MultiCurrencyPrecompile::execute(&input, Some(100_000), &context, false), + MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &context, + false + )), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Multicurrency Transfer failed: BalanceTooLow".into(), - cost: target_gas_limit(Some(100_000)).unwrap(), } ); }) @@ -616,7 +600,8 @@ mod tests { // Token context.caller = aca_evm_address(); - let resp = MultiCurrencyPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)) + .unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, [0u8; 0].to_vec()); @@ -626,11 +611,15 @@ mod tests { // DexShare context.caller = lp_aca_ausd_evm_address(); assert_noop!( - MultiCurrencyPrecompile::execute(&input, Some(100_000), &context, false), + MultiCurrencyPrecompile::execute(&mut MockPrecompileHandle::new( + &input, + Some(100_000), + &context, + false + )), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Multicurrency TransferToAccountId failed: BalanceTooLow".into(), - cost: target_gas_limit(Some(100_000)).unwrap(), } ); }) diff --git a/runtime/common/src/precompile/nft.rs b/runtime/common/src/precompile/nft.rs index 0b96bc8958..5ac58c2272 100644 --- a/runtime/common/src/precompile/nft.rs +++ b/runtime/common/src/precompile/nft.rs @@ -16,15 +16,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputT, Output}; use frame_support::traits::tokens::nonfungibles::{Inspect, Transfer}; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::AddressMapping; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -60,22 +56,14 @@ where + Inspect + Transfer, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -88,9 +76,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: Output::encode_uint(balance), - logs: Default::default(), }) } Action::QueryOwner => { @@ -108,9 +94,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: Output::encode_address(owner), - logs: Default::default(), }) } Action::Transfer => { @@ -126,14 +110,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("NFT Transfer failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: vec![], - logs: Default::default(), }) } } @@ -148,9 +129,10 @@ where { pub const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); let _action = input.action()?; // TODO: gas cost Ok(Self::BASE_COST) diff --git a/runtime/common/src/precompile/oracle.rs b/runtime/common/src/precompile/oracle.rs index 98f1c90264..4086f8bacb 100644 --- a/runtime/common/src/precompile/oracle.rs +++ b/runtime/common/src/precompile/oracle.rs @@ -18,14 +18,11 @@ use super::{ input::{Input, InputPricer, InputT, Output}, - target_gas_limit, weights::PrecompileWeights, }; use crate::{Weight, WeightToGas}; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitSucceed, + precompiles::Precompile, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use module_support::{Erc20InfoMapping as Erc20InfoMappingT, PriceProvider as PriceProviderT}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -52,22 +49,14 @@ impl Precompile for OraclePrecompile where Runtime: module_evm::Config + module_prices::Config, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -103,9 +92,7 @@ where log::debug!(target: "evm", "oracle: getPrice currency_id: {:?}, price: {:?}, adjustment_multiplier: {:?}, output: {:?}", currency_id, price, adjustment_multiplier, output); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(output), - logs: Default::default(), }) } } @@ -120,9 +107,11 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); + let action = input.action()?; let cost = match action { @@ -144,7 +133,7 @@ mod tests { use crate::precompile::mock::{alice_evm_addr, new_test_ext, Oracle, Price, Test, ALICE, DOT}; use frame_support::{assert_noop, assert_ok}; use hex_literal::hex; - use module_evm::ExitRevert; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context, ExitRevert}; use orml_traits::DataFeeder; type OraclePrecompile = crate::OraclePrecompile; @@ -172,7 +161,8 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 "}; - let resp = OraclePrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + OraclePrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); @@ -190,7 +180,8 @@ mod tests { 00000000000000000000000000000000 000000000000065a4da25d3016c00000 "}; - let resp = OraclePrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + OraclePrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output.to_vec()); }); @@ -200,7 +191,7 @@ mod tests { fn oracle_precompile_should_handle_invalid_input() { new_test_ext().execute_with(|| { assert_noop!( - OraclePrecompile::execute( + OraclePrecompile::execute(&mut MockPrecompileHandle::new( &[0u8; 0], Some(1000), &Context { @@ -209,16 +200,15 @@ mod tests { apparent_value: Default::default() }, false - ), + )), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid input".into(), - cost: target_gas_limit(Some(1000)).unwrap(), } ); assert_noop!( - OraclePrecompile::execute( + OraclePrecompile::execute(&mut MockPrecompileHandle::new( &[0u8; 3], Some(1000), &Context { @@ -227,16 +217,15 @@ mod tests { apparent_value: Default::default() }, false - ), + )), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid input".into(), - cost: target_gas_limit(Some(1000)).unwrap(), } ); assert_noop!( - OraclePrecompile::execute( + OraclePrecompile::execute(&mut MockPrecompileHandle::new( &[1u8; 32], Some(1000), &Context { @@ -245,11 +234,10 @@ mod tests { apparent_value: Default::default() }, false - ), + )), PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid action".into(), - cost: target_gas_limit(Some(1000)).unwrap(), } ); }); diff --git a/runtime/common/src/precompile/schedule.rs b/runtime/common/src/precompile/schedule.rs index 4f188fc236..767a06fd2b 100644 --- a/runtime/common/src/precompile/schedule.rs +++ b/runtime/common/src/precompile/schedule.rs @@ -19,10 +19,7 @@ // Disable the following lints #![allow(clippy::type_complexity)] -use super::{ - input::{Input, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputT, Output}; use frame_support::{ ensure, parameter_types, traits::{ @@ -31,9 +28,8 @@ use frame_support::{ }, }; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::{AddressMapping, TransactionPayment}; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -105,22 +101,14 @@ where Address = TaskAddress, >, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -166,7 +154,6 @@ where output: "Scheduler charge failed".into(), // TODO: upgrade schedule::v3::Named // output: Output::encode_error_msg("Scheduler charge failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; } @@ -185,7 +172,6 @@ where let next_id = current_id.checked_add(1).ok_or_else(|| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Scheduler next id overflow".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; EvmSchedulerNextID::set(&next_id); @@ -222,14 +208,11 @@ where output: "Scheduler schedule failed".into(), // TODO: upgrade schedule::v3::Named // output: Output::encode_error_msg("Scheduler schedule failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: Output::encode_bytes(&task_id), - logs: Default::default(), }) } Action::Cancel => { @@ -246,14 +229,12 @@ where let task_info = TaskInfo::decode(&mut &task_id[..]).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Decode task_id failed".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; ensure!( task_info.sender == from, PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "NoPermission".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), } ); @@ -267,7 +248,6 @@ where output: "Scheduler cancel failed".into(), // TODO: upgrade schedule::v3::Named // output: Output::encode_error_msg("Scheduler cancel failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; #[cfg(not(feature = "with-ethereum-compatibility"))] @@ -283,9 +263,7 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: vec![], - logs: Default::default(), }) } Action::Reschedule => { @@ -304,14 +282,12 @@ where let task_info = TaskInfo::decode(&mut &task_id[..]).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Decode task_id failed".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; ensure!( task_info.sender == from, PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "NoPermission".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), } ); @@ -323,14 +299,11 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Scheduler reschedule failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: 0, output: vec![], - logs: Default::default(), }) } } @@ -345,9 +318,11 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); + let _action = input.action()?; // TODO: gas cost Ok(Self::BASE_COST) @@ -362,6 +337,7 @@ mod tests { alice_evm_addr, bob_evm_addr, new_test_ext, run_to_block, Balances, RuntimeEvent as TestEvent, System, Test, }; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; use sp_core::H160; type SchedulePrecompile = crate::SchedulePrecompile; @@ -403,7 +379,7 @@ mod tests { 00000000000000000000000000000000000000000000000000000000 "}; - let resp = SchedulePrecompile::execute(&input, None, &context, false).unwrap(); + let resp = SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(sp_core::bytes::to_hex(&resp.output[..], false), "0x\ 0000000000000000000000000000000000000000000000000000000000000020\ @@ -428,16 +404,14 @@ mod tests { 0000000001824f12000000000000000000000000000000000000000000000000 "}; - let resp = SchedulePrecompile::execute(&cancel_input, None, &context, false).unwrap(); + let resp = SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&cancel_input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); - assert_eq!(resp.cost, 0); let event = TestEvent::Scheduler(pallet_scheduler::Event::::Canceled { when: 3, index: 0 }); assert!(System::events().iter().any(|record| record.event == event)); // schedule call again - let resp = SchedulePrecompile::execute(&input, None, &context, false).unwrap(); + let resp = SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); - assert_eq!(resp.cost, 0); assert_eq!(sp_core::bytes::to_hex(&resp.output[..], false), "0x\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000029\ @@ -462,9 +436,8 @@ mod tests { 0000000001824f12000000000000000000000000000000000000000000000000 "}; - let resp = SchedulePrecompile::execute(&reschedule_input, None, &context, false).unwrap(); + let resp = SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&reschedule_input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); - assert_eq!(resp.cost, 0); assert_eq!(resp.output, [0u8; 0].to_vec()); let event = TestEvent::Scheduler(pallet_scheduler::Event::::Scheduled { when: 5, index: 0 }); @@ -534,9 +507,8 @@ mod tests { 1200000000000000000000000000000000000000000000000000000000000000 "}; - let resp = SchedulePrecompile::execute(&input, None, &context, false).unwrap(); + let resp = SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); - assert_eq!(resp.cost, 0); assert_eq!(sp_core::bytes::to_hex(&resp.output[..], false), "0x\ 0000000000000000000000000000000000000000000000000000000000000020\ 0000000000000000000000000000000000000000000000000000000000000029\ @@ -572,11 +544,10 @@ mod tests { 0000000001824f12000000000000000000000000000000000000000000000000 "}; assert_eq!( - SchedulePrecompile::execute(&cancel_input, Some(10_000), &context, false), + SchedulePrecompile::execute(&mut MockPrecompileHandle::new(&cancel_input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "NoPermission".into(), - cost: target_gas_limit(Some(10_000)).unwrap() }) ); diff --git a/runtime/common/src/precompile/stable_asset.rs b/runtime/common/src/precompile/stable_asset.rs index d6b6623c48..b5c89e0807 100644 --- a/runtime/common/src/precompile/stable_asset.rs +++ b/runtime/common/src/precompile/stable_asset.rs @@ -16,17 +16,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputT, Output}, - target_gas_limit, -}; +use super::input::{Input, InputT, Output}; use crate::{precompile::input::InputPricer, WeightToGas}; use frame_support::traits::Get; use frame_system::pallet_prelude::*; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use module_support::Erc20InfoMapping; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -67,22 +63,14 @@ where BlockNumber = BlockNumberFor, >, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let mut gas_cost = Pricer::::cost(&input)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -92,21 +80,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { // dynamic gas cost calculation // cost of reading asset currencies - gas_cost = gas_cost.saturating_add( - pool_info - .assets - .iter() - .map(|x| InputPricer::::read_currency(*x)) - .sum::(), - ); - // make sure there's enough gas - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } + let cost = pool_info + .assets + .iter() + .map(|x| InputPricer::::read_currency(*x)) + .sum::(); + handle.record_cost(cost)?; let assets: Vec = pool_info .assets @@ -116,16 +95,12 @@ where Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_address_array(assets), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -135,16 +110,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(pool_info.total_supply), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -154,16 +125,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(pool_info.precision), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -173,16 +140,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(pool_info.mint_fee), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -192,16 +155,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(pool_info.swap_fee), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -211,16 +170,12 @@ where if let Some(pool_info) = as StableAsset>::pool(pool_id) { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint(pool_info.redeem_fee), - logs: Default::default(), }) } else { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -245,13 +200,10 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("StableAsset StableAssetSwap failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_uint_tuple(vec![input, output]), - logs: Default::default(), }) } Action::StableAssetMint => { @@ -274,13 +226,10 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("StableAsset StableAssetMint failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } Action::StableAssetRedeem => { @@ -303,13 +252,10 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("StableAsset StableAssetRedeem failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } Action::StableAssetRedeemSingle => { @@ -331,13 +277,10 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("StableAsset StableAssetRedeemSingle failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } Action::StableAssetRedeemMulti => { @@ -360,13 +303,10 @@ where .map_err(|e| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("StableAsset StableAssetRedeemMulti failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Default::default(), - logs: Default::default(), }) } } @@ -381,14 +321,11 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input< - Action, - Runtime::AccountId, - Runtime::AddressMapping, - ::Erc20InfoMapping, - >, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); + let action = input.action()?; let cost: u64 = match action { @@ -458,6 +395,7 @@ mod tests { use crate::precompile::mock::{alice_evm_addr, new_test_ext, RuntimeOrigin, StableAsset, Test, ALICE, AUSD, DOT}; use frame_support::assert_ok; use hex_literal::hex; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context}; type StableAssetPrecompile = crate::StableAssetPrecompile; @@ -488,7 +426,8 @@ mod tests { fb0f0f34 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000000000000000020 00000000000000000000000000000000 00000000000000000000000000000002 @@ -506,7 +445,8 @@ mod tests { fb0f0f34 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -545,7 +485,8 @@ mod tests { 7172c6aa 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 000000000000000000000000001e8480 "}; @@ -560,7 +501,8 @@ mod tests { 7172c6aa 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -593,7 +535,8 @@ mod tests { 9ccdcf91 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000000000000000001 "}; @@ -608,7 +551,8 @@ mod tests { 9ccdcf91 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -641,7 +585,8 @@ mod tests { 62ff9875 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000000000000000002 "}; @@ -656,7 +601,8 @@ mod tests { 62ff9875 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -689,7 +635,8 @@ mod tests { 68410f61 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000000000000000003 "}; @@ -704,7 +651,8 @@ mod tests { 68410f61 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -737,7 +685,8 @@ mod tests { 7f2f11ca 00000000000000000000000000000000000000000000000000000000 00000000 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); let expected_output = hex! {" 00000000000000000000000000000000 00000000000000000000000000000004 "}; @@ -752,7 +701,8 @@ mod tests { 7f2f11ca 00000000000000000000000000000000000000000000000000000000 00000001 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert!(resp.output.is_empty()); }); @@ -797,7 +747,9 @@ mod tests { 00000000000000000000000000000000 000000000000000000000000000f4240 00000000000000000000000000000000 000000000000000000000000000f4240 "}; - let mint_resp = StableAssetPrecompile::execute(&mint_input, None, &context, false).unwrap(); + let mint_resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&mint_input, None, &context, false)) + .unwrap(); assert_eq!(mint_resp.exit_status, ExitSucceed::Returned); assert!(mint_resp.output.is_empty()); @@ -819,7 +771,9 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000000 00000000000000000000000000000002 "}; - let redeem_resp = StableAssetPrecompile::execute(&redeem_input, None, &context, false).unwrap(); + let redeem_resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&redeem_input, None, &context, false)) + .unwrap(); assert_eq!(redeem_resp.exit_status, ExitSucceed::Returned); assert!(redeem_resp.output.is_empty()); @@ -839,8 +793,13 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000002 "}; - let redeem_single_resp = - StableAssetPrecompile::execute(&redeem_single_input, None, &context, false).unwrap(); + let redeem_single_resp = StableAssetPrecompile::execute(&mut MockPrecompileHandle::new( + &redeem_single_input, + None, + &context, + false, + )) + .unwrap(); assert_eq!(redeem_single_resp.exit_status, ExitSucceed::Returned); assert!(redeem_single_resp.output.is_empty()); @@ -859,7 +818,13 @@ mod tests { 000000000000000000000000000000000000000000000000000000000000c350 000000000000000000000000000000000000000000000000000000000000c350 "}; - let redeem_multi_resp = StableAssetPrecompile::execute(&redeem_multi_input, None, &context, false).unwrap(); + let redeem_multi_resp = StableAssetPrecompile::execute(&mut MockPrecompileHandle::new( + &redeem_multi_input, + None, + &context, + false, + )) + .unwrap(); assert_eq!(redeem_multi_resp.exit_status, ExitSucceed::Returned); assert!(redeem_multi_resp.output.is_empty()); }); @@ -917,7 +882,8 @@ mod tests { 00000000000000000000000000000000 0000000000000000000000000007a120 00000000000000000000000000000000 00000000000000000000000000079ab3 "}; - let resp = StableAssetPrecompile::execute(&input, None, &context, false).unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, None, &context, false)).unwrap(); assert_eq!(resp.exit_status, ExitSucceed::Returned); assert_eq!(resp.output, expected_output); @@ -941,15 +907,15 @@ mod tests { 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000 00000002 "}; - let resp = StableAssetPrecompile::execute(&input, Some(200_000), &context, false) - .err() - .unwrap(); + let resp = + StableAssetPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(200_000), &context, false)) + .err() + .unwrap(); assert_eq!( resp, PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "StableAsset StableAssetSwap failed: PoolNotFound".into(), - cost: target_gas_limit(Some(200_000)).unwrap_or_default() } ); }); diff --git a/runtime/common/src/precompile/tests.rs b/runtime/common/src/precompile/tests.rs index bddc523208..2823dad7be 100644 --- a/runtime/common/src/precompile/tests.rs +++ b/runtime/common/src/precompile/tests.rs @@ -20,6 +20,7 @@ #![cfg(test)] use super::*; use crate::precompile::mock::{new_test_ext, PrecompilesValue}; +use module_evm::precompiles::tests::MockPrecompileHandle; use module_evm::{Context, ExitRevert}; use primitives::evm::{PRECOMPILE_ADDRESS_START, PREDEPLOY_ADDRESS_START}; @@ -36,12 +37,19 @@ fn precompile_filter_works_on_acala_precompiles() { caller: non_system.into(), apparent_value: 0.into(), }; + let mut handle = MockPrecompileHandle { + input: &[0u8; 1], + code_address: precompile, + gas_limit: Some(10), + gas_used: 0, + context: &non_system_caller_context, + is_static: false, + }; assert_eq!( - PrecompilesValue::get().execute(precompile, &[0u8; 1], Some(10), &non_system_caller_context, false), + PrecompilesValue::get().execute(&mut handle), Some(Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "NoPermission".into(), - cost: 10, })), ); }); @@ -60,9 +68,15 @@ fn precompile_filter_does_not_work_on_system_contracts() { caller: non_system.into(), apparent_value: 0.into(), }; - assert!(PrecompilesValue::get() - .execute(non_system.into(), &[0u8; 1], None, &non_system_caller_context, false) - .is_none()); + let mut handle = MockPrecompileHandle { + input: &[0u8; 1], + code_address: non_system.into(), + gas_limit: None, + gas_used: 0, + context: &non_system_caller_context, + is_static: false, + }; + assert!(PrecompilesValue::get().execute(&mut handle).is_none()); }); } @@ -79,8 +93,14 @@ fn precompile_filter_does_not_work_on_non_system_contracts() { caller: another_non_system.into(), apparent_value: 0.into(), }; - assert!(PrecompilesValue::get() - .execute(non_system.into(), &[0u8; 1], None, &non_system_caller_context, false) - .is_none()); + let mut handle = MockPrecompileHandle { + input: &[0u8; 1], + code_address: non_system.into(), + gas_limit: None, + gas_used: 0, + context: &non_system_caller_context, + is_static: false, + }; + assert!(PrecompilesValue::get().execute(&mut handle).is_none()); }); } diff --git a/runtime/common/src/precompile/xtokens.rs b/runtime/common/src/precompile/xtokens.rs index a4ca1438db..70ea64d247 100644 --- a/runtime/common/src/precompile/xtokens.rs +++ b/runtime/common/src/precompile/xtokens.rs @@ -16,16 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use super::{ - input::{Input, InputPricer, InputT, Output, PER_PARAM_BYTES}, - target_gas_limit, -}; +use super::input::{Input, InputPricer, InputT, Output, PER_PARAM_BYTES}; use crate::WeightToGas; use frame_support::pallet_prelude::{Decode, Encode, IsType}; use module_evm::{ - precompiles::Precompile, - runner::state::{PrecompileFailure, PrecompileOutput, PrecompileResult}, - Context, ExitError, ExitRevert, ExitSucceed, + precompiles::Precompile, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use num_enum::{IntoPrimitive, TryFromPrimitive}; use orml_traits::{XcmTransfer, XtokensWeightInfo}; @@ -73,22 +69,14 @@ where ::CurrencyId: IsType, ::Balance: IsType, { - fn execute(input: &[u8], target_gas: Option, _context: &Context, _is_static: bool) -> PrecompileResult { + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let gas_cost = Pricer::::cost(handle)?; + handle.record_cost(gas_cost)?; + let input = Input::::new( - input, - target_gas_limit(target_gas), + handle.input(), ); - let gas_cost = Pricer::::cost(&input, target_gas)?; - - if let Some(gas_limit) = target_gas { - if gas_limit < gas_cost { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - let action = input.action()?; match action { @@ -101,14 +89,12 @@ where let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(5)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -131,15 +117,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken Transfer failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } Action::TransferMultiAsset => { @@ -149,21 +132,18 @@ where let asset: MultiAsset = decode_multi_asset(asset_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let dest_bytes: &[u8] = &input.bytes_at(3)?[..]; let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(4)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -186,15 +166,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken TransferMultiAsset failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } Action::TransferWithFee => { @@ -207,14 +184,12 @@ where let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(6)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -237,15 +212,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken TransferWithFee failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } Action::TransferMultiAssetWithFee => { @@ -255,28 +227,24 @@ where let asset: MultiAsset = decode_multi_asset(asset_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let fee_bytes: &[u8] = &input.bytes_at(3)?[..]; let fee: MultiAsset = decode_multi_asset(fee_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid fee asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let dest_bytes: &[u8] = &input.bytes_at(4)?[..]; let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(5)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -299,15 +267,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken TransferMultiAssetWithFee failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } Action::TransferMultiCurrencies => { @@ -322,7 +287,6 @@ where return Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currencies size".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), }); } @@ -341,14 +305,12 @@ where let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(5)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -371,15 +333,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken TransferMultiCurrencies failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } Action::TransferMultiAssets => { @@ -389,28 +348,24 @@ where let assets: MultiAssets = decode_multi_assets(assets_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi assets".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let fee_item = input.u32_at(3)?; let fee: &MultiAsset = assets.get(fee_item as usize).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid fee index".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let dest_bytes: &[u8] = &input.bytes_at(4)?[..]; let dest: MultiLocation = decode_multi_location(dest_bytes).ok_or(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut weight_bytes: &[u8] = &input.bytes_at(5)?[..]; let weight = WeightLimit::decode(&mut weight_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid weight".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; log::debug!( @@ -433,15 +388,12 @@ where PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: Output::encode_error_msg("Xtoken TransferMultiAssets failed", e), - cost: target_gas_limit(target_gas).unwrap_or_default(), } })?; Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: Output::encode_bytes_tuple(vec![&transferred.assets.encode(), &transferred.fee.encode()]), - logs: Default::default(), }) } } @@ -470,10 +422,11 @@ where { const BASE_COST: u64 = 200; - fn cost( - input: &Input, - target_gas: Option, - ) -> Result { + fn cost(handle: &mut impl PrecompileHandle) -> Result { + let input = Input::::new( + handle.input(), + ); + let action = input.action()?; let cost: u64 = match action { @@ -487,7 +440,6 @@ where let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = XtokensWeight::::weight_of_transfer(currency_id.into(), amount.into(), &dest); @@ -501,14 +453,12 @@ where let asset = VersionedMultiAsset::decode(&mut asset_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut dest_bytes: &[u8] = &input.bytes_at(3)?[..]; let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = XtokensWeight::::weight_of_transfer_multiasset(&asset, &dest); @@ -525,7 +475,6 @@ where let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = XtokensWeight::::weight_of_transfer(currency_id.into(), amount.into(), &dest); @@ -539,14 +488,12 @@ where let asset = VersionedMultiAsset::decode(&mut asset_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let mut dest_bytes: &[u8] = &input.bytes_at(4)?[..]; let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = XtokensWeight::::weight_of_transfer_multiasset(&asset, &dest); @@ -564,7 +511,6 @@ where return Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid currencies size".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), }); } @@ -586,7 +532,6 @@ where let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = @@ -602,7 +547,6 @@ where VersionedMultiAssets::decode(&mut assets_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid multi asset".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let fee_item = input.u32_at(3)?; @@ -611,7 +555,6 @@ where let dest = VersionedMultiLocation::decode(&mut dest_bytes).map_err(|_| PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "invalid dest".into(), - cost: target_gas_limit(target_gas).unwrap_or_default(), })?; let weight = XtokensWeight::::weight_of_transfer_multiassets(&assets, &fee_item, &dest); @@ -630,7 +573,8 @@ mod tests { use crate::precompile::mock::{alice_evm_addr, new_test_ext, Test, BOB}; use frame_support::weights::Weight; use hex_literal::hex; - use module_evm::ExitRevert; + use module_evm::{precompiles::tests::MockPrecompileHandle, Context, ExitRevert}; + use orml_utilities::with_transaction_result; type XtokensPrecompile = crate::precompile::XtokensPrecompile; @@ -690,11 +634,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken Transfer failed: NotCrossChainTransferableCurrency".into(), - cost: 9000, }) ); Ok(()) @@ -756,11 +699,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken TransferMultiAsset failed: InvalidDest".into(), - cost: 9000, }) ); Ok(()) @@ -819,11 +761,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken TransferWithFee failed: NotCrossChainTransferableCurrency".into(), - cost: 9000, }) ); Ok(()) @@ -894,11 +835,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken TransferMultiAssetWithFee failed: InvalidDest".into(), - cost: 9000, }) ); Ok(()) @@ -968,11 +908,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken TransferMultiCurrencies failed: NotCrossChainTransferableCurrency".into(), - cost: 9000, }) ); Ok(()) @@ -1037,11 +976,10 @@ mod tests { let _ = with_transaction_result(|| { assert_eq!( - XtokensPrecompile::execute(&input, Some(10_000), &context, false), + XtokensPrecompile::execute(&mut MockPrecompileHandle::new(&input, Some(10_000), &context, false)), Err(PrecompileFailure::Revert { exit_status: ExitRevert::Reverted, output: "Xtoken TransferMultiAssets failed: InvalidDest".into(), - cost: 9000, }) ); Ok(()) diff --git a/runtime/integration-tests/src/evm.rs b/runtime/integration-tests/src/evm.rs index 78761e947c..92427c0e66 100644 --- a/runtime/integration-tests/src/evm.rs +++ b/runtime/integration-tests/src/evm.rs @@ -111,7 +111,7 @@ pub fn deploy_erc20_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 15130, })); @@ -147,7 +147,7 @@ pub fn deploy_erc20_contracts() { H256::from_slice(&buf).as_bytes().to_vec() }, }], - used_gas: 1235081, + used_gas: 1235455, used_storage: 15130, })); @@ -353,7 +353,7 @@ fn test_evm_module() { from: alice_address, contract, logs: vec![], - used_gas: 132199, + used_gas: 132225, used_storage: 10367, })); @@ -1140,7 +1140,7 @@ fn create_contract_use_none_native_token_to_charge_storage() { from: EvmAddress::from_str("0x414d1f1c39e8357acfa07e8aac63cc5da8f9ca4d").unwrap(), contract: EvmAddress::from_str("0xa764c25fe7641aeb21ac08118fa343093b9cb30d").unwrap(), logs: vec![], - used_gas: 132199, + used_gas: 132225, used_storage: 10367, })); } diff --git a/runtime/integration-tests/src/honzon.rs b/runtime/integration-tests/src/honzon.rs index 987f5eca07..0b1a093fc1 100644 --- a/runtime/integration-tests/src/honzon.rs +++ b/runtime/integration-tests/src/honzon.rs @@ -98,7 +98,7 @@ pub fn deploy_liquidation_contracts() { from: repayment_evm_addr(), contract: mock_liquidation_address_0(), logs: vec![], - used_gas: 473252, + used_gas: 473376, used_storage: 11949, })); @@ -117,7 +117,7 @@ pub fn deploy_liquidation_contracts() { from: repayment_evm_addr(), contract: mock_liquidation_address_1(), logs: vec![], - used_gas: 473252, + used_gas: 473376, used_storage: 11949, })); diff --git a/runtime/mandala/src/benchmarking/evm.rs b/runtime/mandala/src/benchmarking/evm.rs index 71623c691b..575828dc06 100644 --- a/runtime/mandala/src/benchmarking/evm.rs +++ b/runtime/mandala/src/benchmarking/evm.rs @@ -56,8 +56,8 @@ fn deploy_contract(caller: AccountId) -> Result { from: module_evm_accounts::EvmAddressMapping::::get_evm_address(&caller).unwrap(), contract: contract_addr(), logs: vec![], - used_gas: 132_199, - used_storage: 10_367, + used_gas: 132225, + used_storage: 10367, })); Ok(contract_addr()) } diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 34c0d193cf..f4621f2767 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -1644,7 +1644,7 @@ impl> frame_support::traits::Get for TxFeePerGasV2 { } #[cfg(feature = "with-ethereum-compatibility")] -static LONDON_CONFIG: module_evm_utility::evm::Config = module_evm_utility::evm::Config::london(); +static SHANGHAI_CONFIG: module_evm_utility::evm::Config = module_evm_utility::evm::Config::shanghai(); impl module_evm::Config for Runtime { type AddressMapping = EvmAddressMapping; @@ -1673,7 +1673,7 @@ impl module_evm::Config for Runtime { #[cfg(feature = "with-ethereum-compatibility")] fn config() -> &'static module_evm_utility::evm::Config { - &LONDON_CONFIG + &SHANGHAI_CONFIG } } diff --git a/ts-tests/tests/test-gas.ts b/ts-tests/tests/test-gas.ts index 37a55c64f0..e3281b84f6 100644 --- a/ts-tests/tests/test-gas.ts +++ b/ts-tests/tests/test-gas.ts @@ -27,7 +27,7 @@ describeWithAcala("Acala RPC (Gas)", (context) => { data: "0x" + Block.bytecode, }); - expect(data.usedGas.toNumber()).to.be.eq(251726); + expect(data.usedGas.toNumber()).to.be.eq(251786); expect(data.gasLimit.toNumber()).closeTo(302071, 1000); expect(data.usedStorage.toNumber()).to.be.eq(10921); });