Skip to content

Commit

Permalink
Add eth precompile whitelist (#183)
Browse files Browse the repository at this point in the history
* add eth-precompile-whitelist

* disable DELEGATECALL and CALLCODE

* update

* bump 403

* add workspace
  • Loading branch information
frank0528 authored Dec 26, 2024
1 parent 04b1c81 commit 4cafe19
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 12 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ members = [
"pallets/terminating-rental/rpc/runtime-api",
"pallets/council-reward",
"pallets/nfts",
"pallets/eth-precompile-whitelist",
"runtime/src/precompiles/macro",
]

Expand Down
26 changes: 26 additions & 0 deletions pallets/eth-precompile-whitelist/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "eth-precompile-whitelist"
version = { workspace = true }
authors = { workspace = true }
edition = "2021"

[dependencies]
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }


frame-support = { workspace = true }
frame-system = { workspace = true }
sp-core = { workspace = true }
sp-std = { workspace = true }

[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"frame-support/std",
"frame-system/std",
"sp-core/std",
"sp-std/std",
]
try-runtime = ["frame-support/try-runtime"]
61 changes: 61 additions & 0 deletions pallets/eth-precompile-whitelist/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#![recursion_limit = "256"]
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(unused_crate_dependencies)]

use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sp_core::H160;
use sp_std::prelude::*;

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
use super::*;

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type WhitelistLimit: Get<u32>;
}

#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);

#[pallet::storage]
#[pallet::getter(fn destroy_hook)]
pub type PrecompileWhitelist<T: Config> =
StorageMap<_, Blake2_128Concat, H160, BoundedVec<H160, T::WhitelistLimit>, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub fn deposit_event)]
pub enum Event<T: Config> {
PrecompileWhitelistSet(H160, Vec<H160>),
}

#[pallet::error]
pub enum Error<T> {
WhitelistExceedsLimit,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(frame_support::weights::Weight::from_parts(10000, 0))]
pub fn set_precompile_whitelist(
origin: OriginFor<T>,
precompile: H160,
whitelist: Vec<H160>,
) -> DispatchResultWithPostInfo {
ensure_root(origin)?;

let bounded_whitelist: BoundedVec<H160, T::WhitelistLimit> =
whitelist.clone().try_into().map_err(|_| Error::<T>::WhitelistExceedsLimit)?;

PrecompileWhitelist::<T>::insert(precompile, bounded_whitelist);
Self::deposit_event(Event::PrecompileWhitelistSet(precompile, whitelist));
Ok(().into())
}
}
}
3 changes: 3 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ online-profile = { path = "../pallets/online-profile", default-features = false
rent-machine = { path = "../pallets/rent-machine", default-features = false }
simple-rpc = { package = "simple-rpc", path = "../pallets/simple-rpc", default-features = false }
terminating-rental = { path = "../pallets/terminating-rental", default-features = false }
eth-precompile-whitelist = { path = "../pallets/eth-precompile-whitelist", default-features = false }

committee-runtime-api = { path = "../pallets/committee/rpc/runtime-api", default-features = false }
online-committee-runtime-api = { path = "../pallets/online-committee/rpc/runtime-api", default-features = false }
Expand Down Expand Up @@ -223,6 +224,7 @@ std = [
"rent-machine/std",
"simple-rpc/std",
"terminating-rental/std",
"eth-precompile-whitelist/std",

"committee-runtime-api/std",
"online-committee-runtime-api/std",
Expand Down Expand Up @@ -340,6 +342,7 @@ try-runtime = [
"rent-machine/try-runtime",
"simple-rpc/try-runtime",
"terminating-rental/try-runtime",
"eth-precompile-whitelist/try-runtime",
"fp-self-contained/try-runtime",
"pallet-ethereum/try-runtime",
"pallet-evm/try-runtime",
Expand Down
8 changes: 7 additions & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to 0. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 402,
spec_version: 403,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down Expand Up @@ -1544,6 +1544,11 @@ impl pallet_ethereum::Config for Runtime {
type ExtraDataLength = ConstU32<30>;
}

impl eth_precompile_whitelist::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WhitelistLimit = ConstU32<10>;
}

parameter_types! {
pub DefaultBaseFeePerGas: U256 = U256::from(10_000_000_000u128);
// No gas price adjustment for now. default is 125_000 (12.5%)
Expand Down Expand Up @@ -1641,6 +1646,7 @@ construct_runtime!(
RentMachine: rent_machine = 111,
MaintainCommittee: maintain_committee = 112,
TerminatingRental: terminating_rental = 113,
EthPrecompileWhitelist: eth_precompile_whitelist = 114,
}
);

Expand Down
24 changes: 16 additions & 8 deletions runtime/src/precompiles/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct Bridge<T>(PhantomData<T>);
#[derive(RuntimeDebug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Selector {
Transfer = "transfer(string,uint256)",
Transfer = "transfer(address,string,uint256)",
}

type BalanceOf<T> = <T as pallet_balances::Config>::Balance;
Expand All @@ -36,7 +36,6 @@ where
{
fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
let input = handle.input();
let context = handle.context();

ensure!(
input.len() >= 4,
Expand All @@ -54,22 +53,31 @@ where

match selector {
Selector::Transfer => {
let from = T::AddressMapping::into_account_id(context.caller);

let param = ethabi::decode(
&[ethabi::ParamType::String, ethabi::ParamType::Uint(256)],
&[
ethabi::ParamType::Address,
ethabi::ParamType::String,
ethabi::ParamType::Uint(256),
],
&input.get(4..).unwrap_or_default(),
)
.map_err(|e| PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: format!("decode param failed: {:?}", e).into(),
})?;

let to =
param[0].clone().into_string().ok_or_else(|| PrecompileFailure::Revert {
let from =
param[0].clone().into_address().ok_or_else(|| PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: "decode param[0] failed".into(),
})?;
let from: T::AccountId = T::AddressMapping::into_account_id(from);

let to =
param[1].clone().into_string().ok_or_else(|| PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: "decode param[1] failed".into(),
})?;

let to = to.strip_prefix("0x").unwrap_or(&to);
ensure!(
Expand All @@ -88,7 +96,7 @@ where
let to: T::AccountId = T::AccountId::from(to_hex.into());

let origin_amount =
param[1].clone().into_uint().ok_or_else(|| PrecompileFailure::Revert {
param[2].clone().into_uint().ok_or_else(|| PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: "decode param[1] failed".into(),
})?;
Expand Down
44 changes: 41 additions & 3 deletions runtime/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use fp_evm::{ExitRevert, PrecompileFailure};
use pallet_evm::{
IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, PrecompileSet,
};
use scale_info::prelude::format;
use sp_core::H160;
use sp_std::marker::PhantomData;

Expand Down Expand Up @@ -29,7 +31,7 @@ where
pub fn new() -> Self {
Self(Default::default())
}
pub fn used_addresses() -> [H160; 9] {
pub fn used_addresses() -> [H160; 11] {
[
hash(1),
hash(2),
Expand All @@ -40,19 +42,55 @@ where
hash(1025),
hash(1026),
hash(2048),
hash(2049),
hash(2051),
]
}
}
impl<T> PrecompileSet for DBCPrecompiles<T>
where
T: pallet_evm::Config,
T: pallet_evm::Config + eth_precompile_whitelist::Config,
Dispatch<T>: Precompile,
Bridge<T>: Precompile,
DBCPrice<T>: Precompile,
MachineInfo<T>: Precompile,
{
fn execute(&self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
match handle.code_address() {
let address = handle.code_address();
let context = handle.context();
log::debug!(target: LOG_TARGET, "PrecompileSet execute address: {:?}, context: {:?}", address, handle.context());

if let IsPrecompileResult::Answer { is_precompile: true, extra_cost: _ } =
self.is_precompile(address, handle.remaining_gas())
{
if address > hash(9) && context.address != address {
return Some(Err(PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: "cannot be called with DELEGATECALL or CALLCODE".into(),
}))
}

// check if the context.caller in the precompile whitelist
let precompile_whitelist =
eth_precompile_whitelist::PrecompileWhitelist::<T>::get(address);

match address {
a if a == hash(2048) => {
if !precompile_whitelist.contains(&context.caller) {
log::debug!(target: LOG_TARGET, "caller {:?} not in the {:?} whitelist", context.caller, address);

return Some(Err(PrecompileFailure::Revert {
exit_status: ExitRevert::Reverted,
output: format!("caller {:?} not in the whitelist", context.caller)
.into(),
}))
}
},
_ => {},
}
}

match address {
// Ethereum precompiles :
a if a == hash(1) => Some(ECRecover::execute(handle)),
a if a == hash(2) => Some(Sha256::execute(handle)),
Expand Down

0 comments on commit 4cafe19

Please sign in to comment.