Skip to content

Commit a6d5348

Browse files
committed
Add a contract creation filter to the EVM domain
1 parent ca1eb09 commit a6d5348

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

domains/runtime/evm/src/lib.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ use sp_runtime::traits::{
6969
};
7070
use sp_runtime::transaction_validity::{
7171
InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
72+
ValidTransaction,
7273
};
7374
use sp_runtime::{
7475
generic, impl_opaque_keys, ApplyExtrinsicResult, ConsensusEngineId, Digest,
@@ -121,6 +122,7 @@ pub type SignedExtra = (
121122
frame_system::CheckNonce<Runtime>,
122123
domain_check_weight::CheckWeight<Runtime>,
123124
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
125+
CheckContractCreation,
124126
);
125127

126128
/// Custom signed extra for check_and_pre_dispatch.
@@ -134,6 +136,7 @@ type CustomSignedExtra = (
134136
pallet_evm_nonce_tracker::CheckNonce<Runtime>,
135137
domain_check_weight::CheckWeight<Runtime>,
136138
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
139+
CheckContractCreation,
137140
);
138141

139142
/// Unchecked extrinsic type as expected by this runtime.
@@ -152,6 +155,95 @@ pub type Executive = domain_pallet_executive::Executive<
152155
AllPalletsWithSystem,
153156
>;
154157

158+
/// Rejects contracts that can't be created under the current allow list.
159+
/// Returns false if the call is a contract call, and the account is *not* allowed to call it.
160+
/// Otherwise, returns true.
161+
pub fn is_create_contract_allowed(call: &RuntimeCall, signer: &AccountId) -> bool {
162+
if is_create_contract(call)
163+
&& !pallet_evm_nonce_tracker::Pallet::<Runtime>::is_allowed_to_create_contracts(signer)
164+
{
165+
return false;
166+
}
167+
168+
// If it's not a contract call, or the account is allowed to create contracts, return true.
169+
true
170+
}
171+
172+
/// Returns true if the call is a contract creation call.
173+
pub fn is_create_contract(call: &RuntimeCall) -> bool {
174+
match call {
175+
RuntimeCall::EVM(pallet_evm::Call::create { .. })
176+
| RuntimeCall::EVM(pallet_evm::Call::create2 { .. }) => true,
177+
RuntimeCall::Ethereum(pallet_ethereum::Call::transact {
178+
transaction: EthereumTransaction::Legacy(transaction),
179+
..
180+
}) => transaction.action == TransactionAction::Create,
181+
RuntimeCall::Ethereum(pallet_ethereum::Call::transact {
182+
transaction: EthereumTransaction::EIP2930(transaction),
183+
..
184+
}) => transaction.action == TransactionAction::Create,
185+
RuntimeCall::Ethereum(pallet_ethereum::Call::transact {
186+
transaction: EthereumTransaction::EIP1559(transaction),
187+
..
188+
}) => transaction.action == TransactionAction::Create,
189+
// TODO: does this need a recursion limit?
190+
RuntimeCall::Utility(utility_call) => match utility_call {
191+
pallet_utility::Call::batch { calls }
192+
| pallet_utility::Call::batch_all { calls }
193+
| pallet_utility::Call::force_batch { calls } => calls.iter().any(is_create_contract),
194+
pallet_utility::Call::as_derivative { call, .. }
195+
| pallet_utility::Call::dispatch_as { call, .. }
196+
| pallet_utility::Call::with_weight { call, .. } => is_create_contract(call),
197+
pallet_utility::Call::__Ignore(..) => false,
198+
},
199+
_ => false,
200+
}
201+
}
202+
203+
/// Reject contract creation, unless the account is in the current evm contract allow list.
204+
#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
205+
pub struct CheckContractCreation;
206+
207+
impl SignedExtension for CheckContractCreation {
208+
const IDENTIFIER: &'static str = "CheckContractCreation";
209+
type AccountId = <Runtime as frame_system::Config>::AccountId;
210+
type Call = <Runtime as frame_system::Config>::RuntimeCall;
211+
type AdditionalSigned = ();
212+
type Pre = ();
213+
214+
fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> {
215+
Ok(())
216+
}
217+
218+
fn validate(
219+
&self,
220+
who: &Self::AccountId,
221+
call: &Self::Call,
222+
_info: &DispatchInfoOf<Self::Call>,
223+
_len: usize,
224+
) -> TransactionValidity {
225+
// Reject contract creation unless the account is in the allow list.
226+
if !is_create_contract_allowed(call, who) {
227+
InvalidTransaction::Call.into()
228+
} else {
229+
Ok(ValidTransaction::default())
230+
}
231+
}
232+
233+
fn pre_dispatch(
234+
self,
235+
who: &Self::AccountId,
236+
call: &Self::Call,
237+
info: &DispatchInfoOf<Self::Call>,
238+
len: usize,
239+
) -> Result<Self::Pre, TransactionValidityError> {
240+
self.validate(who, call, info, len)?;
241+
Ok(())
242+
}
243+
244+
// TODO: can unsigned calls create contracts?
245+
}
246+
155247
impl fp_self_contained::SelfContainedCall for RuntimeCall {
156248
type SignedInfo = H160;
157249

@@ -175,6 +267,11 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall {
175267
dispatch_info: &DispatchInfoOf<RuntimeCall>,
176268
len: usize,
177269
) -> Option<TransactionValidity> {
270+
if !is_create_contract_allowed(self, &(*info).into()) {
271+
// TODO: should this be Custom() instead?
272+
return Some(Err(InvalidTransaction::Call.into()));
273+
}
274+
178275
match self {
179276
RuntimeCall::Ethereum(call) => {
180277
// Ensure the caller can pay for the consensus chain storage fee
@@ -199,6 +296,11 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall {
199296
dispatch_info: &DispatchInfoOf<RuntimeCall>,
200297
len: usize,
201298
) -> Option<Result<(), TransactionValidityError>> {
299+
if !is_create_contract_allowed(self, &(*info).into()) {
300+
// TODO: should this be Custom() instead?
301+
return Some(Err(InvalidTransaction::Call.into()));
302+
}
303+
202304
match self {
203305
RuntimeCall::Ethereum(call) => {
204306
// Withdraw the consensus chain storage fee from the caller and record
@@ -1009,6 +1111,7 @@ fn check_transaction_and_do_pre_dispatch_inner(
10091111
pallet_evm_nonce_tracker::CheckNonce::from(extra.5 .0),
10101112
extra.6,
10111113
extra.7,
1114+
extra.8,
10121115
);
10131116

10141117
custom_extra

0 commit comments

Comments
 (0)