diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 72e47a4c6a..cc9f3cfc96 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -55,7 +55,7 @@ use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchErrorWithPostInfo, RuntimeDebug, + DispatchErrorWithPostInfo, RuntimeDebug, SaturatedConversion, }; use sp_std::{marker::PhantomData, prelude::*}; @@ -196,6 +196,8 @@ pub mod pallet { type StateRoot: Get; /// What's included in the PostLog. type PostLogContent: Get; + /// The maximum length of the extra data in the Executed event. + type ExtraDataLength: Get; } #[pallet::hooks] @@ -302,6 +304,7 @@ pub mod pallet { to: H160, transaction_hash: H256, exit_reason: ExitReason, + extra_data: Vec, }, } @@ -545,9 +548,9 @@ impl Pallet { let transaction_hash = transaction.hash(); let transaction_index = pending.len() as u32; - let (reason, status, used_gas, dest) = match info { + let (reason, status, used_gas, dest, extra_data) = match info { CallOrCreateInfo::Call(info) => ( - info.exit_reason, + info.exit_reason.clone(), TransactionStatus { transaction_hash, transaction_index, @@ -563,6 +566,31 @@ impl Pallet { }, info.used_gas, to, + match info.exit_reason { + ExitReason::Revert(_) => { + const LEN_START: usize = 36; + const MESSAGE_START: usize = 68; + + let data = info.value; + let data_len = data.len(); + if data_len > MESSAGE_START { + let message_len = U256::from(&data[LEN_START..MESSAGE_START]) + .saturated_into::(); + let message_end = MESSAGE_START.saturating_add( + message_len.min(T::ExtraDataLength::get() as usize), + ); + + if data_len >= message_end { + data[MESSAGE_START..message_end].to_vec() + } else { + data + } + } else { + data + } + } + _ => vec![], + }, ), CallOrCreateInfo::Create(info) => ( info.exit_reason, @@ -581,6 +609,7 @@ impl Pallet { }, info.used_gas, Some(info.value), + Vec::new(), ), }; @@ -629,6 +658,7 @@ impl Pallet { to: dest.unwrap_or_default(), transaction_hash, exit_reason: reason, + extra_data, }); Ok(PostDispatchInfo { diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index 5e41224e9d..4452cfc1b2 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -179,6 +179,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type StateRoot = IntermediateStateRoot; type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; } impl fp_self_contained::SelfContainedCall for RuntimeCall { diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index af8315eb8f..b283bd1493 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -18,6 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use evm::{ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::ValidatedTransaction; use frame_support::{dispatch::DispatchClass, traits::Get, weights::Weight}; use pallet_evm::{AddressMapping, GasWeightMapping}; @@ -340,17 +341,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -362,7 +352,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), } .sign(&alice.private_key, None); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -411,6 +401,80 @@ fn call_should_handle_errors() { }); } +#[test] +fn event_extra_data_should_be_handle_properly() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + System::set_block_number(1); + + let t = EIP1559UnsignedTransaction { + nonce: U256::zero(), + max_priority_fee_per_gas: U256::from(1), + max_fee_per_gas: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), + } + .sign(&alice.private_key, None); + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let t2 = EIP1559UnsignedTransaction { + nonce: U256::from(1), + max_priority_fee_per_gas: U256::from(1), + max_fee_per_gas: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: foo, + } + .sign(&alice.private_key, None); + + // calling foo + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t2)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0x66036e60bafa1fabb847679d86d1f0a027ae52afde7690aaadf6f81c7e8ecda2", + ) + .unwrap(), + exit_reason: ExitReason::Succeed(ExitSucceed::Returned), + extra_data: vec![], + })); + + let t3 = EIP1559UnsignedTransaction { + nonce: U256::from(2), + max_priority_fee_per_gas: U256::from(1), + max_fee_per_gas: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: bar, + } + .sign(&alice.private_key, None); + + // calling bar revert + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t3)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0xf14cd5e6d0535a8407339dabce26616a3f19c2048937450859ea65d5201fb7e4", + ) + .unwrap(), + exit_reason: ExitReason::Revert(ExitRevert::Reverted), + extra_data: b"very_long_error_msg_that_we_ex".to_vec(), + })); + }); +} + #[test] fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_dispatch() { let (pairs, mut ext) = new_test_ext(1); diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index 8a27739e7b..7feb7aaf8d 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -18,6 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use evm::{ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::ValidatedTransaction; use frame_support::{ dispatch::{DispatchClass, GetDispatchInfo}, @@ -267,17 +268,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -288,7 +278,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), } .sign(&alice.private_key, None); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -335,6 +325,77 @@ fn call_should_handle_errors() { }); } +#[test] +fn event_extra_data_should_be_handle_properly() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + System::set_block_number(1); + + let t = EIP2930UnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), + } + .sign(&alice.private_key, None); + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let t2 = EIP2930UnsignedTransaction { + nonce: U256::from(1), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: foo, + } + .sign(&alice.private_key, None); + + // calling foo + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t2)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0xd953990ba4a99074c5b8c7a9f97546ffbf781815acc2dcc2719ac3d0c8955a00", + ) + .unwrap(), + exit_reason: ExitReason::Succeed(ExitSucceed::Returned), + extra_data: vec![], + })); + + let t3 = EIP2930UnsignedTransaction { + nonce: U256::from(2), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: bar, + } + .sign(&alice.private_key, None); + + // calling bar revert + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t3)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0xbcb3fd14507b3aba8a124b007d959bf18d59f87c6d6c036e8feac224ec5fabeb", + ) + .unwrap(), + exit_reason: ExitReason::Revert(ExitRevert::Reverted), + extra_data: b"very_long_error_msg_that_we_ex".to_vec(), + })); + }); +} + #[test] fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_dispatch() { let (pairs, mut ext) = new_test_ext(1); diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index c4317fdfb2..25fc17a0f9 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -18,6 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use evm::{ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::ValidatedTransaction; use frame_support::{ dispatch::{DispatchClass, GetDispatchInfo}, @@ -267,17 +268,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -288,7 +278,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), } .sign(&alice.private_key); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -335,6 +325,77 @@ fn call_should_handle_errors() { }); } +#[test] +fn event_extra_data_should_be_handle_properly() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + System::set_block_number(1); + + let t = LegacyUnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(TEST_CONTRACT_CODE).unwrap(), + } + .sign(&alice.private_key); + assert_ok!(Ethereum::execute(alice.address, &t, None,)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let t2 = LegacyUnsignedTransaction { + nonce: U256::from(1), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: foo, + } + .sign(&alice.private_key); + + // calling foo + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t2)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0xc256587e4b2718d2798e9e895821a72e6aa751b8fcc03ce754246cc0d8a541c0", + ) + .unwrap(), + exit_reason: ExitReason::Succeed(ExitSucceed::Returned), + extra_data: vec![], + })); + + let t3 = LegacyUnsignedTransaction { + nonce: U256::from(2), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: TransactionAction::Call(H160::from_slice(&contract_address)), + value: U256::zero(), + input: bar, + } + .sign(&alice.private_key); + + // calling bar revert + assert_ok!(Ethereum::apply_validated_transaction(alice.address, t3)); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: H160::from_slice(&contract_address), + transaction_hash: H256::from_str( + "0x27a75747783eb8959f1fe7b23e8b1152a9ec945d9b90354582cb7c3ea1481287", + ) + .unwrap(), + exit_reason: ExitReason::Revert(ExitRevert::Reverted), + extra_data: b"very_long_error_msg_that_we_ex".to_vec(), + })); + }); +} + #[test] fn self_contained_transaction_with_extra_gas_should_adjust_weight_with_post_dispatch() { let (pairs, mut ext) = new_test_ext(1); diff --git a/frame/ethereum/src/tests/mod.rs b/frame/ethereum/src/tests/mod.rs index dafc119df4..cd5499da69 100644 --- a/frame/ethereum/src/tests/mod.rs +++ b/frame/ethereum/src/tests/mod.rs @@ -25,7 +25,7 @@ use sp_runtime::{ use std::str::FromStr; use crate::{ - mock::*, CallOrCreateInfo, RawOrigin, Transaction, TransactionAction, H160, H256, U256, + mock::*, CallOrCreateInfo, Event, RawOrigin, Transaction, TransactionAction, H160, H256, U256, }; use fp_self_contained::CheckedExtrinsic; @@ -40,3 +40,15 @@ mod legacy; // constructor() public { _mint(msg.sender, 2**256 - 1); } // } pub const ERC20_CONTRACT_BYTECODE: &str = include_str!("./res/erc20_contract_bytecode.txt"); + +// pragma solidity ^0.6.6; +// contract Test { +// function foo() external pure returns (bool) { +// return true; +// } +// +// function bar() external pure { +// require(false, "very_long_error_msg_that_we_expect_to_be_trimmed_away"); +// } +// } +pub const TEST_CONTRACT_CODE: &str = "608060405234801561001057600080fd5b50610129806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146055575b600080fd5b603d605d565b60405180821515815260200191505060405180910390f35b605b6066565b005b60006001905090565b600060bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260358152602001806100bf6035913960400191505060405180910390fd5b56fe766572795f6c6f6e675f6572726f725f6d73675f746861745f77655f6578706563745f746f5f62655f7472696d6d65645f61776179a26469706673582212207af96dd688d3a3adc999c619e6073d5b6056c72c79ace04a90ea4835a77d179364736f6c634300060c0033"; diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index b196b4d97f..815a4653f1 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -355,6 +355,7 @@ impl pallet_ethereum::Config for Runtime { type RuntimeEvent = RuntimeEvent; type StateRoot = pallet_ethereum::IntermediateStateRoot; type PostLogContent = PostBlockAndTxnHashes; + type ExtraDataLength = ConstU32<30>; } parameter_types! {