Skip to content

Commit

Permalink
feat: fake transactions
Browse files Browse the repository at this point in the history
Allow outer code to inject ethereum logs, without running an ethereum
transaction.
  • Loading branch information
Grigoriy Simonov committed Apr 14, 2023
1 parent 15e9c6a commit 6886070
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ pub mod pallet {
#[pallet::getter(fn block_hash)]
pub(super) type BlockHash<T: Config> = StorageMap<_, Twox64Concat, U256, H256, ValueQuery>;

/// Injected transactions should have unique nonce, here we store current
#[pallet::storage]
pub(super) type InjectedNonce<T: Config> = StorageValue<_, U256, ValueQuery>;

#[pallet::genesis_config]
#[derive(Default)]
pub struct GenesisConfig {}
Expand Down Expand Up @@ -647,6 +651,69 @@ impl<T: Config> Pallet<T> {
})
}

pub fn flush_injected_transaction() {
use ethereum::{
EIP658ReceiptData, EnvelopedEncodable, TransactionSignature, TransactionV0,
};

assert!(
fp_consensus::find_pre_log(&frame_system::Pallet::<T>::digest()).is_err(),
"this method is supposed to be called only from other pallets",
);

let logs = <CurrentLogs<T>>::take();
if logs.is_empty() {
return;
}

let nonce = <InjectedNonce<T>>::get()
.checked_add(1u32.into())
.expect("u256 should be enough");
<InjectedNonce<T>>::set(nonce);

let transaction = Transaction::Legacy(TransactionV0 {
nonce,
gas_price: 0.into(),
gas_limit: 0.into(),
action: TransactionAction::Call(H160([0; 20])),
value: 0.into(),
// zero selector, this transaction always has same sender, so all data should be acquired from logs
input: Vec::from([0, 0, 0, 0]),
// if v is not 27 - then we need to pass some other validity checks
signature: TransactionSignature::new(27, H256([0x88; 32]), H256([0x88; 32])).unwrap(),
});

let transaction_hash = H256::from_slice(
sp_io::hashing::keccak_256(&EnvelopedEncodable::encode(&transaction)).as_slice(),
);
let transaction_index = <Pending<T>>::get().len() as u32;

let logs_bloom = {
let mut bloom: Bloom = Bloom::default();
Self::logs_bloom(&logs, &mut bloom);
bloom
};

let status = TransactionStatus {
transaction_hash,
transaction_index,
from: H160::default(),
to: None,
contract_address: None,
logs_bloom,
logs: logs.clone(),
};

let receipt = Receipt::Legacy(EIP658ReceiptData {
status_code: 1,
used_gas: 0u32.into(),
logs_bloom,
logs,
});

<Pending<T>>::append((transaction, status, receipt));
}

/// Get current block hash
pub fn current_block_hash() -> Option<H256> {
Self::current_block().map(|block| block.header.hash())
Expand Down Expand Up @@ -946,3 +1013,45 @@ impl From<InvalidEvmTransactionError> for InvalidTransactionWrapper {
}
}
}

#[derive(TypeInfo, PartialEq, Eq, Clone, Debug, Encode, Decode)]
pub struct FakeTransactionFinalizer<T>(PhantomData<T>);

impl<T: Config + TypeInfo + core::fmt::Debug + Send + Sync> sp_runtime::traits::SignedExtension
for FakeTransactionFinalizer<T>
{
const IDENTIFIER: &'static str = "FakeTransactionFinalizer";

type AccountId = T::AccountId;

type Call = T::RuntimeCall;

type AdditionalSigned = ();

type Pre = ();

fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
Ok(())
}

fn pre_dispatch(
self,
_who: &Self::AccountId,
_call: &Self::Call,
_info: &DispatchInfoOf<Self::Call>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(())
}

fn post_dispatch(
_pre: Option<Self::Pre>,
_info: &DispatchInfoOf<Self::Call>,
_post_info: &sp_runtime::traits::PostDispatchInfoOf<Self::Call>,
_len: usize,
_result: &sp_runtime::DispatchResult,
) -> Result<(), TransactionValidityError> {
<Pallet<T>>::flush_injected_transaction();
Ok(())
}
}

0 comments on commit 6886070

Please sign in to comment.