From d654bbb0e975d54724b38a62f0ae4fbe85d6276c Mon Sep 17 00:00:00 2001 From: nanocryk <6422796+nanocryk@users.noreply.github.com> Date: Wed, 4 May 2022 13:18:20 +0000 Subject: [PATCH 1/3] update to PrecompileHandle --- Cargo.lock | 13 ++--- Cargo.toml | 3 ++ client/rpc/Cargo.toml | 2 +- frame/ethereum/Cargo.toml | 2 +- frame/evm/Cargo.toml | 2 +- frame/evm/precompile/blake2/src/lib.rs | 9 ++-- frame/evm/precompile/bn128/src/lib.rs | 19 +++++--- frame/evm/precompile/dispatch/src/lib.rs | 10 ++-- frame/evm/precompile/modexp/src/lib.rs | 41 ++++++++++------ frame/evm/src/lib.rs | 3 +- frame/evm/test-vector-support/Cargo.toml | 5 +- frame/evm/test-vector-support/src/lib.rs | 62 ++++++++++++++++++++++-- primitives/evm/Cargo.toml | 2 +- primitives/evm/src/lib.rs | 3 +- primitives/evm/src/precompile.rs | 16 ++++-- template/runtime/src/precompiles.rs | 45 ++++++++++------- 16 files changed, 164 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57cff95412..c7d451cc8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1487,8 +1487,7 @@ checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "evm" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8ff320c1e25e7f6d676858f16ffd9b0493d2cc67c3d900c6f2ed027b747f43" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "auto_impl", "environmental", @@ -1508,8 +1507,7 @@ dependencies = [ [[package]] name = "evm-core" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4537041d3a3438d59b2d01bd950ce89fb1ccb3cf21d9331193c10be12e849f" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "parity-scale-codec", "primitive-types", @@ -1520,8 +1518,7 @@ dependencies = [ [[package]] name = "evm-gasometer" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6181da8734c86873ac9b3f9886d4e00105361039dcfb9f621be9a0ddb8f43961" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "environmental", "evm-core", @@ -1532,8 +1529,7 @@ dependencies = [ [[package]] name = "evm-runtime" version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6157af91ca70fcf3581afaea1fa25974a71b9ef63d454c08dfba93ab0c7715d" +source = "git+https://github.com/rust-blockchain/evm?branch=master#01bcbd2205a212c34451d3b4fabc962793b057d3" dependencies = [ "auto_impl", "environmental", @@ -4540,6 +4536,7 @@ dependencies = [ "hex", "serde", "serde_json", + "sp-core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 475851df39..5d0e738873 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,6 @@ members = [ "template/runtime", ] resolver = "2" + +[patch.crates-io] +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master" } \ No newline at end of file diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 6b5904a245..900e3dddfa 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.0.0" } ethereum = { version = "0.12.0", features = ["with-codec"] } ethereum-types = "0.13.1" -evm = "0.35.0" +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master" } futures = { version = "0.3.1", features = ["compat"] } hex = "0.4" jsonrpc-core = "18.0" diff --git a/frame/ethereum/Cargo.toml b/frame/ethereum/Cargo.toml index 4b827b63e8..9072b43abf 100644 --- a/frame/ethereum/Cargo.toml +++ b/frame/ethereum/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" [dependencies] ethereum = { version = "0.12.0", default-features = false, features = ["with-codec"] } ethereum-types = { version = "0.13.1", default-features = false } -evm = { version = "0.35.0", features = ["with-codec"], default-features = false } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", features = ["with-codec"], default-features = false } rlp = { version = "0.5", default-features = false } serde = { version = "1.0.101", optional = true } sha3 = { version = "0.10", default-features = false } diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index e65e223659..4dc341aeee 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -evm = { version = "0.35.0", default-features = false, features = ["with-codec"] } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false, features = ["with-codec"] } hex = { version = "0.4", default-features = false, features = ["alloc"] } log = { version = "0.4", default-features = false } primitive-types = { version = "0.11.1", default-features = false, features = ["rlp", "byteorder"] } diff --git a/frame/evm/precompile/blake2/src/lib.rs b/frame/evm/precompile/blake2/src/lib.rs index 23e967d518..c0f6124e59 100644 --- a/frame/evm/precompile/blake2/src/lib.rs +++ b/frame/evm/precompile/blake2/src/lib.rs @@ -22,8 +22,8 @@ extern crate alloc; mod eip_152; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput, - PrecompileResult, + Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult, }; pub struct Blake2F; @@ -36,6 +36,7 @@ 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( + handle: &mut impl PrecompileHandle, input: &[u8], target_gas: Option, _context: &Context, @@ -64,6 +65,8 @@ impl Precompile for Blake2F { } } + handle.record_cost(gas_cost)?; + // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE let mut h_buf: [u8; 64] = [0; 64]; @@ -115,9 +118,7 @@ impl Precompile for Blake2F { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: output_buf.to_vec(), - logs: Default::default(), }) } } diff --git a/frame/evm/precompile/bn128/src/lib.rs b/frame/evm/precompile/bn128/src/lib.rs index d7a18182d3..ae5dff7cf9 100644 --- a/frame/evm/precompile/bn128/src/lib.rs +++ b/frame/evm/precompile/bn128/src/lib.rs @@ -21,8 +21,8 @@ extern crate alloc; use alloc::vec::Vec; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput, - PrecompileResult, + Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult, }; use sp_core::U256; @@ -77,6 +77,7 @@ impl Bn128Add { impl Precompile for Bn128Add { fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], _target_gas: Option, _context: &Context, @@ -84,6 +85,8 @@ impl Precompile for Bn128Add { ) -> PrecompileResult { use bn::AffineG1; + handle.record_cost(Bn128Add::GAS_COST)?; + let p1 = read_point(input, 0)?; let p2 = read_point(input, 64)?; @@ -108,9 +111,7 @@ impl Precompile for Bn128Add { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: Bn128Add::GAS_COST, output: buf.to_vec(), - logs: Default::default(), }) } } @@ -124,6 +125,7 @@ impl Bn128Mul { impl Precompile for Bn128Mul { fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], _target_gas: Option, _context: &Context, @@ -131,6 +133,8 @@ impl Precompile for Bn128Mul { ) -> PrecompileResult { use bn::AffineG1; + handle.record_cost(Bn128Mul::GAS_COST)?; + let p = read_point(input, 0)?; let fr = read_fr(input, 64)?; @@ -155,9 +159,7 @@ impl Precompile for Bn128Mul { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: Bn128Mul::GAS_COST, output: buf.to_vec(), - logs: Default::default(), }) } } @@ -173,6 +175,7 @@ impl Bn128Pairing { impl Precompile for Bn128Pairing { fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], target_gas: Option, _context: &Context, @@ -282,14 +285,14 @@ 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(), }) } } diff --git a/frame/evm/precompile/dispatch/src/lib.rs b/frame/evm/precompile/dispatch/src/lib.rs index 2eb4b3dfdc..7294560311 100644 --- a/frame/evm/precompile/dispatch/src/lib.rs +++ b/frame/evm/precompile/dispatch/src/lib.rs @@ -21,8 +21,8 @@ extern crate alloc; use core::marker::PhantomData; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput, - PrecompileResult, + Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult, }; use frame_support::{ codec::Decode, @@ -42,6 +42,7 @@ where ::Origin: From>, { fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], target_gas: Option, context: &Context, @@ -75,11 +76,12 @@ where let cost = T::GasWeightMapping::weight_to_gas( post_info.actual_weight.unwrap_or(info.weight), ); + + handle.record_cost(cost)?; + Ok(PrecompileOutput { exit_status: ExitSucceed::Stopped, - cost, output: Default::default(), - logs: Default::default(), }) } Err(_) => Err(PrecompileFailure::Error { diff --git a/frame/evm/precompile/modexp/src/lib.rs b/frame/evm/precompile/modexp/src/lib.rs index 2cdb6359ba..43c498f013 100644 --- a/frame/evm/precompile/modexp/src/lib.rs +++ b/frame/evm/precompile/modexp/src/lib.rs @@ -26,8 +26,8 @@ use core::{cmp::max, ops::BitAnd}; use num::{BigUint, FromPrimitive, One, ToPrimitive, Zero}; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileOutput, - PrecompileResult, + Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, + PrecompileOutput, PrecompileResult, }; pub struct Modexp; @@ -110,6 +110,7 @@ fn calculate_gas_cost( impl Precompile for Modexp { fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], target_gas: Option, _context: &Context, @@ -194,6 +195,8 @@ impl Precompile for Modexp { } }; + handle.record_cost(gas_cost)?; + // write output to given memory, left padded and same length as the modulus. let bytes = r.to_bytes_be(); @@ -202,9 +205,7 @@ impl Precompile for Modexp { if bytes.len() == mod_len { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: bytes.to_vec(), - logs: Default::default(), }) } else if bytes.len() < mod_len { let mut ret = Vec::with_capacity(mod_len); @@ -212,9 +213,7 @@ impl Precompile for Modexp { ret.extend_from_slice(&bytes[..]); Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - cost: gas_cost, output: ret.to_vec(), - logs: Default::default(), }) } else { Err(PrecompileFailure::Error { @@ -228,7 +227,7 @@ impl Precompile for Modexp { mod tests { use super::*; extern crate hex; - use pallet_evm_test_vector_support::test_precompile_test_vectors; + use pallet_evm_test_vector_support::{test_precompile_test_vectors, MockHandle}; #[test] fn process_consensus_tests() -> Result<(), String> { @@ -248,7 +247,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(_) => { panic!("Test not expected to pass"); } @@ -283,7 +284,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(_) => { panic!("Test not expected to pass"); } @@ -316,7 +319,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(_) => { panic!("Test not expected to pass"); } @@ -354,7 +359,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 1); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -389,7 +396,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 32); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -422,7 +431,9 @@ mod tests { apparent_value: From::from(0), }; - match Modexp::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 32); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -461,7 +472,9 @@ mod tests { apparent_value: From::from(0), }; - let precompile_result = Modexp::execute(&input, Some(cost), &context, false) + let mut handle = MockHandle(0); + + let precompile_result = Modexp::execute(&mut handle, &input, Some(cost), &context, false) .expect("Modexp::execute() returned error"); assert_eq!(precompile_result.output.len(), 1); // should be same length as mod diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index b2eafe609c..44f4ffb1ff 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -85,7 +85,8 @@ pub use evm::{ use fp_evm::GenesisAccount; pub use fp_evm::{ Account, CallInfo, CreateInfo, ExecutionInfo, FeeCalculator, LinearCostPrecompile, Log, - Precompile, PrecompileFailure, PrecompileOutput, PrecompileResult, PrecompileSet, Vicinity, + Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, + PrecompileSet, Vicinity, }; pub use self::{ diff --git a/frame/evm/test-vector-support/Cargo.toml b/frame/evm/test-vector-support/Cargo.toml index 5adf26a56f..453d583204 100644 --- a/frame/evm/test-vector-support/Cargo.toml +++ b/frame/evm/test-vector-support/Cargo.toml @@ -9,9 +9,10 @@ repository = "https://github.com/paritytech/frontier/" description = "Test vector support for EVM pallet." [dependencies] -evm = { version = "0.35.0", features = ["with-codec"] } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", features = ["with-codec"] } hex = "0.4.0" serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0" -fp-evm = { version = "3.0.0-dev", path = "../../../primitives/evm" } +sp-core = { version = "6.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +fp-evm = { version = "3.0.0-dev", path = "../../../primitives/evm", default-features = false } \ No newline at end of file diff --git a/frame/evm/test-vector-support/src/lib.rs b/frame/evm/test-vector-support/src/lib.rs index 95bfd40ee8..4da43efbf4 100644 --- a/frame/evm/test-vector-support/src/lib.rs +++ b/frame/evm/test-vector-support/src/lib.rs @@ -17,8 +17,9 @@ use std::fs; -use evm::{Context, ExitSucceed}; -use fp_evm::Precompile; +use evm::{Context, ExitError, ExitReason, ExitSucceed, Transfer}; +use fp_evm::{Precompile, PrecompileHandle}; +use sp_core::{H160, H256}; #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "PascalCase")] @@ -29,6 +30,57 @@ struct EthConsensusTest { gas: Option, } +pub struct MockHandle(pub u64); + +impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + _: H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &Context, + ) -> (ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.0 += cost; + Ok(()) + } + + fn log(&mut self, _: H160, _: Vec, _: Vec) -> Result<(), ExitError> { + unimplemented!() + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn code_address(&self) -> H160 { + unimplemented!() + } + + fn input(&self) -> &[u8] { + unimplemented!() + } + + fn context(&self) -> &Context { + unimplemented!() + } + + fn is_static(&self) -> bool { + unimplemented!() + } + + fn gas_limit(&self) -> Option { + unimplemented!() + } +} + /// Tests a precompile against the ethereum consensus tests defined in the given file at filepath. /// The file is expected to be in JSON format and contain an array of test vectors, where each /// vector can be deserialized into an "EthConsensusTest". @@ -48,7 +100,9 @@ pub fn test_precompile_test_vectors(filepath: &str) -> Result<(), apparent_value: From::from(0), }; - match P::execute(&input, Some(cost), &context, false) { + let mut handle = MockHandle(0); + + match P::execute(&mut handle, &input, Some(cost), &context, false) { Ok(result) => { let as_hex: String = hex::encode(result.output); assert_eq!( @@ -65,7 +119,7 @@ pub fn test_precompile_test_vectors(filepath: &str) -> Result<(), ); if let Some(expected_gas) = test.gas { assert_eq!( - result.cost, expected_gas, + handle.0, expected_gas, "test '{}' failed (different gas cost)", test.name ); diff --git a/primitives/evm/Cargo.toml b/primitives/evm/Cargo.toml index db5955715d..7f6de20473 100644 --- a/primitives/evm/Cargo.toml +++ b/primitives/evm/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-evm" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -evm = { version = "0.35.0", default-features = false, features = ["with-codec"] } +evm = { git = "https://github.com/rust-blockchain/evm", branch = "master", default-features = false, features = ["with-codec"] } serde = { version = "1.0.101", features = ["derive"], optional = true } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index 4f7c3c6f23..557a76f101 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -31,7 +31,8 @@ pub use evm::backend::{Basic as Account, Log}; pub use self::precompile::{ Context, ExitError, ExitRevert, ExitSucceed, LinearCostPrecompile, Precompile, - PrecompileFailure, PrecompileOutput, PrecompileResult, PrecompileSet, + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, + Transfer, }; #[derive(Clone, Eq, PartialEq, Encode, Decode, Default)] diff --git a/primitives/evm/src/precompile.rs b/primitives/evm/src/precompile.rs index 3a058cb9e2..545212368c 100644 --- a/primitives/evm/src/precompile.rs +++ b/primitives/evm/src/precompile.rs @@ -16,8 +16,8 @@ // limitations under the License. pub use evm::{ - executor::stack::{PrecompileFailure, PrecompileOutput, PrecompileSet}, - Context, ExitError, ExitRevert, ExitSucceed, + executor::stack::{PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet}, + Context, ExitError, ExitRevert, ExitSucceed, Transfer, }; use sp_std::vec::Vec; @@ -29,6 +29,7 @@ pub trait Precompile { /// `target_gas`. Return `Ok(status, output, gas_used)` if the execution is /// successful. Otherwise return `Err(_)`. fn execute( + handle: &mut impl PrecompileHandle, input: &[u8], target_gas: Option, context: &Context, @@ -47,15 +48,20 @@ pub trait LinearCostPrecompile { } impl Precompile for T { - fn execute(input: &[u8], target_gas: Option, _: &Context, _: bool) -> PrecompileResult { + fn execute( + handle: &mut impl PrecompileHandle, + input: &[u8], + target_gas: Option, + _: &Context, + _: bool, + ) -> PrecompileResult { let cost = ensure_linear_cost(target_gas, input.len() as u64, T::BASE, T::WORD)?; + handle.record_cost(cost)?; let (exit_status, output) = T::execute(input, cost)?; Ok(PrecompileOutput { exit_status, - cost, output, - logs: Default::default(), }) } } diff --git a/template/runtime/src/precompiles.rs b/template/runtime/src/precompiles.rs index 1e1c0845db..24aaf80cf3 100644 --- a/template/runtime/src/precompiles.rs +++ b/template/runtime/src/precompiles.rs @@ -1,4 +1,4 @@ -use pallet_evm::{Context, Precompile, PrecompileResult, PrecompileSet}; +use pallet_evm::{Precompile, PrecompileHandle, PrecompileResult, PrecompileSet}; use sp_core::H160; use sp_std::marker::PhantomData; @@ -26,27 +26,36 @@ impl PrecompileSet for FrontierPrecompiles where R: pallet_evm::Config, { - fn execute( - &self, - address: H160, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> Option { + fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { + let address = handle.code_address(); + let input = &handle.input().to_vec(); + let target_gas = handle.gas_limit(); + let context = &handle.context().clone(); + let is_static = handle.is_static(); + match address { // Ethereum precompiles : - a if a == hash(1) => Some(ECRecover::execute(input, target_gas, context, is_static)), - a if a == hash(2) => Some(Sha256::execute(input, target_gas, context, is_static)), - a if a == hash(3) => Some(Ripemd160::execute(input, target_gas, context, is_static)), - a if a == hash(4) => Some(Identity::execute(input, target_gas, context, is_static)), - a if a == hash(5) => Some(Modexp::execute(input, target_gas, context, is_static)), + a if a == hash(1) => Some(ECRecover::execute( + handle, input, target_gas, context, is_static, + )), + a if a == hash(2) => Some(Sha256::execute( + handle, input, target_gas, context, is_static, + )), + a if a == hash(3) => Some(Ripemd160::execute( + handle, input, target_gas, context, is_static, + )), + a if a == hash(4) => Some(Identity::execute( + handle, input, target_gas, context, is_static, + )), + a if a == hash(5) => Some(Modexp::execute( + handle, input, target_gas, context, is_static, + )), // Non-Frontier specific nor Ethereum precompiles : - a if a == hash(1024) => { - Some(Sha3FIPS256::execute(input, target_gas, context, is_static)) - } + a if a == hash(1024) => Some(Sha3FIPS256::execute( + handle, input, target_gas, context, is_static, + )), a if a == hash(1025) => Some(ECRecoverPublicKey::execute( - input, target_gas, context, is_static, + handle, input, target_gas, context, is_static, )), _ => None, } From 52cdc6747442c9dce518eeaefb5d3f103336cb66 Mon Sep 17 00:00:00 2001 From: nanocryk <6422796+nanocryk@users.noreply.github.com> Date: Thu, 19 May 2022 08:35:45 +0000 Subject: [PATCH 2/3] remove patch --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5d0e738873..3827572e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,4 @@ members = [ "template/node", "template/runtime", ] -resolver = "2" - -[patch.crates-io] -evm = { git = "https://github.com/rust-blockchain/evm", branch = "master" } \ No newline at end of file +resolver = "2" \ No newline at end of file From 7713dc69a973d44aa25b364602da585deee4d5eb Mon Sep 17 00:00:00 2001 From: nanocryk <6422796+nanocryk@users.noreply.github.com> Date: Thu, 19 May 2022 09:10:33 +0000 Subject: [PATCH 3/3] update Precompile trait --- frame/evm/precompile/blake2/src/lib.rs | 16 ++++---- frame/evm/precompile/bn128/src/lib.rs | 35 ++++++----------- frame/evm/precompile/dispatch/src/lib.rs | 16 ++++---- frame/evm/precompile/modexp/src/lib.rs | 48 ++++++++++++------------ frame/evm/test-vector-support/src/lib.rs | 36 +++++++++++++----- primitives/evm/src/precompile.rs | 26 ++++--------- template/runtime/src/precompiles.rs | 36 ++++-------------- 7 files changed, 91 insertions(+), 122 deletions(-) diff --git a/frame/evm/precompile/blake2/src/lib.rs b/frame/evm/precompile/blake2/src/lib.rs index c0f6124e59..139ef13e3d 100644 --- a/frame/evm/precompile/blake2/src/lib.rs +++ b/frame/evm/precompile/blake2/src/lib.rs @@ -22,8 +22,8 @@ extern crate alloc; mod eip_152; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; pub struct Blake2F; @@ -35,15 +35,12 @@ impl Blake2F { 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( - handle: &mut impl PrecompileHandle, - 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(); + let target_gas = handle.gas_limit(); + if input.len() != BLAKE2_F_ARG_LEN { return Err(PrecompileFailure::Error { exit_status: ExitError::Other( @@ -66,6 +63,7 @@ impl Precompile for Blake2F { } 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 diff --git a/frame/evm/precompile/bn128/src/lib.rs b/frame/evm/precompile/bn128/src/lib.rs index ae5dff7cf9..1fcae3cd82 100644 --- a/frame/evm/precompile/bn128/src/lib.rs +++ b/frame/evm/precompile/bn128/src/lib.rs @@ -21,8 +21,8 @@ extern crate alloc; use alloc::vec::Vec; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use sp_core::U256; @@ -76,17 +76,13 @@ impl Bn128Add { } impl Precompile for Bn128Add { - fn execute( - handle: &mut impl PrecompileHandle, - 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)?; @@ -124,17 +120,13 @@ impl Bn128Mul { } impl Precompile for Bn128Mul { - fn execute( - handle: &mut impl PrecompileHandle, - 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)?; @@ -174,15 +166,12 @@ impl Bn128Pairing { } impl Precompile for Bn128Pairing { - fn execute( - handle: &mut impl PrecompileHandle, - 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}; + let input = handle.input(); + let target_gas = handle.gas_limit(); + let (ret_val, gas_cost) = if input.is_empty() { (U256::one(), Bn128Pairing::BASE_GAS_COST) } else { diff --git a/frame/evm/precompile/dispatch/src/lib.rs b/frame/evm/precompile/dispatch/src/lib.rs index 7294560311..7eedab5f75 100644 --- a/frame/evm/precompile/dispatch/src/lib.rs +++ b/frame/evm/precompile/dispatch/src/lib.rs @@ -21,8 +21,8 @@ extern crate alloc; use core::marker::PhantomData; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; use frame_support::{ codec::Decode, @@ -41,13 +41,11 @@ where T::Call: Dispatchable + GetDispatchInfo + Decode, ::Origin: From>, { - fn execute( - handle: &mut impl PrecompileHandle, - 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(); + let context = handle.context(); + let call = T::Call::decode(&mut &*input).map_err(|_| PrecompileFailure::Error { exit_status: ExitError::Other("decode failed".into()), })?; diff --git a/frame/evm/precompile/modexp/src/lib.rs b/frame/evm/precompile/modexp/src/lib.rs index 43c498f013..7f2f8573b1 100644 --- a/frame/evm/precompile/modexp/src/lib.rs +++ b/frame/evm/precompile/modexp/src/lib.rs @@ -26,8 +26,8 @@ use core::{cmp::max, ops::BitAnd}; use num::{BigUint, FromPrimitive, One, ToPrimitive, Zero}; use fp_evm::{ - Context, ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, + PrecompileResult, }; pub struct Modexp; @@ -109,13 +109,10 @@ fn calculate_gas_cost( // see: https://eips.ethereum.org/EIPS/eip-198 impl Precompile for Modexp { - fn execute( - handle: &mut impl PrecompileHandle, - 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() < 96 { return Err(PrecompileFailure::Error { exit_status: ExitError::Other("input must contain at least 96 bytes".into()), @@ -227,6 +224,7 @@ impl Precompile for Modexp { mod tests { use super::*; extern crate hex; + use fp_evm::Context; use pallet_evm_test_vector_support::{test_precompile_test_vectors, MockHandle}; #[test] @@ -237,7 +235,7 @@ mod tests { #[test] fn test_empty_input() -> Result<(), PrecompileFailure> { - let input: [u8; 0] = []; + let input = Vec::new(); let cost: u64 = 1; @@ -247,9 +245,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(_) => { panic!("Test not expected to pass"); } @@ -284,9 +282,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(_) => { panic!("Test not expected to pass"); } @@ -319,9 +317,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(_) => { panic!("Test not expected to pass"); } @@ -359,9 +357,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 1); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -396,9 +394,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 32); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -431,9 +429,9 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match Modexp::execute(&mut handle, &input, Some(cost), &context, false) { + match Modexp::execute(&mut handle) { Ok(precompile_result) => { assert_eq!(precompile_result.output.len(), 32); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); @@ -472,10 +470,10 @@ mod tests { apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - let precompile_result = Modexp::execute(&mut handle, &input, Some(cost), &context, false) - .expect("Modexp::execute() returned error"); + let precompile_result = + Modexp::execute(&mut handle).expect("Modexp::execute() returned error"); assert_eq!(precompile_result.output.len(), 1); // should be same length as mod let result = BigUint::from_bytes_be(&precompile_result.output[..]); diff --git a/frame/evm/test-vector-support/src/lib.rs b/frame/evm/test-vector-support/src/lib.rs index 4da43efbf4..96fa107941 100644 --- a/frame/evm/test-vector-support/src/lib.rs +++ b/frame/evm/test-vector-support/src/lib.rs @@ -30,7 +30,25 @@ struct EthConsensusTest { gas: Option, } -pub struct MockHandle(pub u64); +pub struct MockHandle { + pub input: Vec, + pub gas_limit: Option, + pub context: Context, + pub is_static: bool, + pub gas_used: u64, +} + +impl MockHandle { + pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { + Self { + input, + gas_limit, + context, + is_static: false, + gas_used: 0, + } + } +} impl PrecompileHandle for MockHandle { /// Perform subcall in provided context. @@ -48,7 +66,7 @@ impl PrecompileHandle for MockHandle { } fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { - self.0 += cost; + self.gas_used += cost; Ok(()) } @@ -65,19 +83,19 @@ impl PrecompileHandle for MockHandle { } fn input(&self) -> &[u8] { - unimplemented!() + &self.input } fn context(&self) -> &Context { - unimplemented!() + &self.context } fn is_static(&self) -> bool { - unimplemented!() + self.is_static } fn gas_limit(&self) -> Option { - unimplemented!() + self.gas_limit } } @@ -100,9 +118,9 @@ pub fn test_precompile_test_vectors(filepath: &str) -> Result<(), apparent_value: From::from(0), }; - let mut handle = MockHandle(0); + let mut handle = MockHandle::new(input, Some(cost), context); - match P::execute(&mut handle, &input, Some(cost), &context, false) { + match P::execute(&mut handle) { Ok(result) => { let as_hex: String = hex::encode(result.output); assert_eq!( @@ -119,7 +137,7 @@ pub fn test_precompile_test_vectors(filepath: &str) -> Result<(), ); if let Some(expected_gas) = test.gas { assert_eq!( - handle.0, expected_gas, + handle.gas_used, expected_gas, "test '{}' failed (different gas cost)", test.name ); diff --git a/primitives/evm/src/precompile.rs b/primitives/evm/src/precompile.rs index 545212368c..4996445cb6 100644 --- a/primitives/evm/src/precompile.rs +++ b/primitives/evm/src/precompile.rs @@ -25,16 +25,9 @@ pub type PrecompileResult = Result; /// One single precompile used by EVM engine. 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( - handle: &mut impl PrecompileHandle, - input: &[u8], - target_gas: Option, - context: &Context, - is_static: bool, - ) -> PrecompileResult; + /// Try to execute the precompile with given `handle` which provides all call data + /// and allow to register costs and logs. + fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult; } pub trait LinearCostPrecompile { @@ -48,17 +41,12 @@ pub trait LinearCostPrecompile { } impl Precompile for T { - fn execute( - handle: &mut impl PrecompileHandle, - 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(input, cost)?; + let (exit_status, output) = T::execute(handle.input(), cost)?; Ok(PrecompileOutput { exit_status, output, diff --git a/template/runtime/src/precompiles.rs b/template/runtime/src/precompiles.rs index 24aaf80cf3..93b54c030f 100644 --- a/template/runtime/src/precompiles.rs +++ b/template/runtime/src/precompiles.rs @@ -27,36 +27,16 @@ where R: pallet_evm::Config, { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - let address = handle.code_address(); - let input = &handle.input().to_vec(); - let target_gas = handle.gas_limit(); - let context = &handle.context().clone(); - let is_static = handle.is_static(); - - match address { + match handle.code_address() { // Ethereum precompiles : - a if a == hash(1) => Some(ECRecover::execute( - handle, input, target_gas, context, is_static, - )), - a if a == hash(2) => Some(Sha256::execute( - handle, input, target_gas, context, is_static, - )), - a if a == hash(3) => Some(Ripemd160::execute( - handle, input, target_gas, context, is_static, - )), - a if a == hash(4) => Some(Identity::execute( - handle, input, target_gas, context, is_static, - )), - a if a == hash(5) => Some(Modexp::execute( - handle, input, target_gas, context, is_static, - )), + a if a == hash(1) => Some(ECRecover::execute(handle)), + a if a == hash(2) => Some(Sha256::execute(handle)), + a if a == hash(3) => Some(Ripemd160::execute(handle)), + a if a == hash(4) => Some(Identity::execute(handle)), + a if a == hash(5) => Some(Modexp::execute(handle)), // Non-Frontier specific nor Ethereum precompiles : - a if a == hash(1024) => Some(Sha3FIPS256::execute( - handle, input, target_gas, context, is_static, - )), - a if a == hash(1025) => Some(ECRecoverPublicKey::execute( - handle, input, target_gas, context, is_static, - )), + a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), + a if a == hash(1025) => Some(ECRecoverPublicKey::execute(handle)), _ => None, } }