Skip to content

Commit f1ee266

Browse files
author
Grigoriy Simonov
committed
feat: fake transactions
Allow outer code to inject ethereum logs, without running an ethereum transaction.
1 parent 15e9c6a commit f1ee266

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

frame/ethereum/src/lib.rs

+109
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ pub mod pallet {
340340
#[pallet::getter(fn block_hash)]
341341
pub(super) type BlockHash<T: Config> = StorageMap<_, Twox64Concat, U256, H256, ValueQuery>;
342342

343+
/// Injected transactions should have unique nonce, here we store current
344+
#[pallet::storage]
345+
pub(super) type InjectedNonce<T: Config> = StorageValue<_, U256, ValueQuery>;
346+
343347
#[pallet::genesis_config]
344348
#[derive(Default)]
345349
pub struct GenesisConfig {}
@@ -647,6 +651,69 @@ impl<T: Config> Pallet<T> {
647651
})
648652
}
649653

654+
pub fn flush_injected_transaction() {
655+
use ethereum::{
656+
EIP658ReceiptData, EnvelopedEncodable, TransactionSignature, TransactionV0,
657+
};
658+
659+
assert!(
660+
fp_consensus::find_pre_log(&frame_system::Pallet::<T>::digest()).is_err(),
661+
"this method is supposed to be called only from other pallets",
662+
);
663+
664+
let logs = <CurrentLogs<T>>::take();
665+
if logs.is_empty() {
666+
return;
667+
}
668+
669+
let nonce = <InjectedNonce<T>>::get()
670+
.checked_add(1u32.into())
671+
.expect("u256 should be enough");
672+
<InjectedNonce<T>>::set(nonce);
673+
674+
let transaction = Transaction::Legacy(TransactionV0 {
675+
nonce,
676+
gas_price: 0.into(),
677+
gas_limit: 0.into(),
678+
action: TransactionAction::Call(H160([0; 20])),
679+
value: 0.into(),
680+
// zero selector, this transaction always has same sender, so all data should be acquired from logs
681+
input: Vec::from([0, 0, 0, 0]),
682+
// if v is not 27 - then we need to pass some other validity checks
683+
signature: TransactionSignature::new(27, H256([0x88; 32]), H256([0x88; 32])).unwrap(),
684+
});
685+
686+
let transaction_hash = H256::from_slice(
687+
sp_io::hashing::keccak_256(&EnvelopedEncodable::encode(&transaction)).as_slice(),
688+
);
689+
let transaction_index = <Pending<T>>::get().len() as u32;
690+
691+
let logs_bloom = {
692+
let mut bloom: Bloom = Bloom::default();
693+
Self::logs_bloom(&logs, &mut bloom);
694+
bloom
695+
};
696+
697+
let status = TransactionStatus {
698+
transaction_hash,
699+
transaction_index,
700+
from: H160::default(),
701+
to: None,
702+
contract_address: None,
703+
logs_bloom,
704+
logs: logs.clone(),
705+
};
706+
707+
let receipt = Receipt::Legacy(EIP658ReceiptData {
708+
status_code: 1,
709+
used_gas: 0u32.into(),
710+
logs_bloom,
711+
logs,
712+
});
713+
714+
<Pending<T>>::append((transaction, status, receipt));
715+
}
716+
650717
/// Get current block hash
651718
pub fn current_block_hash() -> Option<H256> {
652719
Self::current_block().map(|block| block.header.hash())
@@ -946,3 +1013,45 @@ impl From<InvalidEvmTransactionError> for InvalidTransactionWrapper {
9461013
}
9471014
}
9481015
}
1016+
1017+
#[derive(TypeInfo, PartialEq, Eq, Clone, Debug, Encode, Decode)]
1018+
pub struct FakeTransactionFinalizer<T>(PhantomData<T>);
1019+
1020+
impl<T: Config + TypeInfo + core::fmt::Debug + Send + Sync> sp_runtime::traits::SignedExtension
1021+
for FakeTransactionFinalizer<T>
1022+
{
1023+
const IDENTIFIER: &'static str = "FakeTransactionFinalizer";
1024+
1025+
type AccountId = T::AccountId;
1026+
1027+
type Call = T::RuntimeCall;
1028+
1029+
type AdditionalSigned = ();
1030+
1031+
type Pre = ();
1032+
1033+
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
1034+
Ok(())
1035+
}
1036+
1037+
fn pre_dispatch(
1038+
self,
1039+
_who: &Self::AccountId,
1040+
_call: &Self::Call,
1041+
_info: &DispatchInfoOf<Self::Call>,
1042+
_len: usize,
1043+
) -> Result<Self::Pre, TransactionValidityError> {
1044+
Ok(())
1045+
}
1046+
1047+
fn post_dispatch(
1048+
_pre: Option<Self::Pre>,
1049+
_info: &DispatchInfoOf<Self::Call>,
1050+
_post_info: &sp_runtime::traits::PostDispatchInfoOf<Self::Call>,
1051+
_len: usize,
1052+
_result: &sp_runtime::DispatchResult,
1053+
) -> Result<(), TransactionValidityError> {
1054+
<Pallet<T>>::flush_injected_transaction();
1055+
Ok(())
1056+
}
1057+
}

template/runtime/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ pub type SignedExtra = (
463463
frame_system::CheckNonce<Runtime>,
464464
frame_system::CheckWeight<Runtime>,
465465
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
466+
pallet_ethereum::FakeTransactionFinalizer<Runtime>,
466467
);
467468
/// Unchecked extrinsic type as expected by this runtime.
468469
pub type UncheckedExtrinsic =

0 commit comments

Comments
 (0)