#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use core::marker::PhantomData;
use fp_evm::PrecompileHandle;
use frame_support::pallet_prelude::IsType;
use frame_support::traits::Get;
use frame_support::weights::Weight;
use frame_support::{
dispatch::{GetDispatchInfo, PostDispatchInfo},
traits::ConstU32,
};
use frame_system::Config;
use pallet_evm::GasWeightMapping;
use pallet_evm_precompile_dispatch::DispatchValidateT;
use parity_scale_codec::DecodeLimit;
use precompile_utils::prelude::{revert, BoundedBytes, RuntimeHelper};
use precompile_utils::EvmResult;
use sp_core::{crypto::AccountId32, H160, H256};
use sp_io::hashing::keccak_256;
use sp_runtime::traits::Dispatchable;
use sp_std::vec::Vec;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub const LOG_TARGET: &str = "precompile::dispatch-lockdrop";
type ECDSAPublic = ConstU32<64>;
pub struct DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit = ConstU32<8>>(
PhantomData<(Runtime, DispatchValidator, DecodeLimit)>,
);
type CallLengthLimit = ConstU32<2048>;
#[precompile_utils::precompile]
impl<Runtime, DispatchValidator, DecodeLimit>
DispatchLockdrop<Runtime, DispatchValidator, DecodeLimit>
where
Runtime: pallet_evm::Config,
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<Runtime as Config>::AccountId: IsType<AccountId32>,
<Runtime as Config>::AccountId: From<[u8; 32]>,
DispatchValidator:
DispatchValidateT<<Runtime as Config>::AccountId, <Runtime as Config>::RuntimeCall>,
DecodeLimit: Get<u32>,
{
#[precompile::public("dispatch_lockdrop_call(bytes,bytes)")]
fn dispatch_lockdrop_call(
handle: &mut impl PrecompileHandle,
call: BoundedBytes<CallLengthLimit>,
pubkey: BoundedBytes<ECDSAPublic>,
) -> EvmResult<bool> {
log::trace!(
target: LOG_TARGET,
"raw arguments: call: {:?}, pubkey: {:?}",
call,
pubkey
);
let caller: H160 = handle.context().caller.into();
let input: Vec<u8> = call.into();
handle.record_cost(Runtime::GasWeightMapping::weight_to_gas(
Weight::from_parts(1_000_000_000u64, 0),
))?;
if caller != Self::get_evm_address_from_pubkey(pubkey.as_bytes()) {
let message: &str = "caller does not match the public key";
log::trace!(target: LOG_TARGET, "{}", message);
return Err(revert(message));
}
let origin = Self::get_account_id_from_pubkey(pubkey.as_bytes())
.ok_or(revert("could not derive AccountId from pubkey"))?;
let call = Runtime::RuntimeCall::decode_with_depth_limit(DecodeLimit::get(), &mut &*input)
.map_err(|_| revert("could not decode call"))?;
DispatchValidator::validate_before_dispatch(&origin, &call)
.map_or_else(|| Ok(()), |_| Err(revert("invalid Call")))?;
RuntimeHelper::<Runtime>::try_dispatch::<Runtime::RuntimeCall>(
handle,
Some(origin).into(),
call,
)?;
Ok(true)
}
fn get_account_id_from_pubkey(pubkey: &[u8]) -> Option<<Runtime as Config>::AccountId> {
libsecp256k1::PublicKey::parse_slice(pubkey, None)
.map(|k| sp_io::hashing::blake2_256(k.serialize_compressed().as_ref()).into())
.ok()
}
fn get_evm_address_from_pubkey(pubkey: &[u8]) -> H160 {
H160::from(H256::from_slice(&keccak_256(pubkey)))
}
}