pallet_evm_precompile_dispatch_lockdrop/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
20
21extern crate alloc;
22
23use core::marker::PhantomData;
24use fp_evm::PrecompileHandle;
25use frame_support::pallet_prelude::IsType;
26use frame_support::traits::Get;
27use frame_support::weights::Weight;
28use frame_support::{
29 dispatch::{GetDispatchInfo, PostDispatchInfo},
30 traits::ConstU32,
31};
32use frame_system::Config;
33use pallet_evm::GasWeightMapping;
34use pallet_evm_precompile_dispatch::DispatchValidateT;
35use parity_scale_codec::DecodeLimit;
36use precompile_utils::prelude::{revert, BoundedBytes, RuntimeHelper};
37use precompile_utils::EvmResult;
38use sp_core::{crypto::AccountId32, H160, H256};
39use sp_io::hashing::keccak_256;
40use sp_runtime::traits::Dispatchable;
41use sp_std::vec::Vec;
42
43#[cfg(test)]
44mod mock;
45#[cfg(test)]
46mod tests;
47
48pub const LOG_TARGET: &str = "precompile::dispatch-lockdrop";
49
50type ECDSAPublic = ConstU32<64>;
52
53pub struct DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit = ConstU32<8>>(
57 PhantomData<(Runtime, DispatchValidator, DecodeLimit)>,
58);
59
60type CallLengthLimit = ConstU32<2048>;
61
62#[precompile_utils::precompile]
63impl<Runtime, DispatchValidator, DecodeLimit>
64 DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit>
65where
66 Runtime: pallet_evm::Config,
67 <Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
68 Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
69 <Runtime as Config>::AccountId: IsType<AccountId32>,
70 <Runtime as Config>::AccountId: From<[u8; 32]>,
71 DispatchValidator:
72 DispatchValidateT<<Runtime as Config>::AccountId, <Runtime as Config>::RuntimeCall>,
73 DecodeLimit: Get<u32>,
74{
75 #[precompile::public("dispatch_lockdrop_call(bytes,bytes)")]
76 fn dispatch_lockdrop_call(
77 handle: &mut impl PrecompileHandle,
78 call: BoundedBytes<CallLengthLimit>,
79 pubkey: BoundedBytes<ECDSAPublic>,
80 ) -> EvmResult<bool> {
81 log::trace!(
82 target: LOG_TARGET,
83 "raw arguments: call: {call:?}, pubkey: {pubkey:?}",
84 );
85
86 let caller: H160 = handle.context().caller;
87 let input: Vec<u8> = call.into();
88
89 handle.record_cost(Runtime::GasWeightMapping::weight_to_gas(
91 Weight::from_parts(1_000_000_000u64, 0),
92 ))?;
93
94 if caller != Self::get_evm_address_from_pubkey(pubkey.as_bytes()) {
96 let message: &str = "caller does not match the public key";
97 log::trace!(target: LOG_TARGET, "{message}");
98 return Err(revert(message));
99 }
100
101 let origin = Self::get_account_id_from_pubkey(pubkey.as_bytes())
103 .ok_or(revert("could not derive AccountId from pubkey"))?;
104
105 let call = Runtime::RuntimeCall::decode_with_depth_limit(DecodeLimit::get(), &mut &*input)
107 .map_err(|_| revert("could not decode call"))?;
108
109 DispatchValidator::validate_before_dispatch(&origin, &call)
111 .map_or_else(|| Ok(()), |_| Err(revert("invalid Call")))?;
112
113 RuntimeHelper::<Runtime>::try_dispatch::<Runtime::RuntimeCall>(
115 handle,
116 Some(origin).into(),
117 call,
118 0,
119 )?;
120
121 Ok(true)
122 }
123
124 fn get_account_id_from_pubkey(pubkey: &[u8]) -> Option<<Runtime as Config>::AccountId> {
125 libsecp256k1::PublicKey::parse_slice(pubkey, None)
126 .map(|k| sp_io::hashing::blake2_256(k.serialize_compressed().as_ref()).into())
127 .ok()
128 }
129
130 fn get_evm_address_from_pubkey(pubkey: &[u8]) -> H160 {
131 H160::from(H256::from_slice(&keccak_256(pubkey)))
132 }
133}