@@ -69,6 +69,7 @@ use sp_runtime::traits::{
69
69
} ;
70
70
use sp_runtime:: transaction_validity:: {
71
71
InvalidTransaction , TransactionSource , TransactionValidity , TransactionValidityError ,
72
+ ValidTransaction ,
72
73
} ;
73
74
use sp_runtime:: {
74
75
generic, impl_opaque_keys, ApplyExtrinsicResult , ConsensusEngineId , Digest ,
@@ -121,6 +122,7 @@ pub type SignedExtra = (
121
122
frame_system:: CheckNonce < Runtime > ,
122
123
domain_check_weight:: CheckWeight < Runtime > ,
123
124
pallet_transaction_payment:: ChargeTransactionPayment < Runtime > ,
125
+ CheckContractCreation ,
124
126
) ;
125
127
126
128
/// Custom signed extra for check_and_pre_dispatch.
@@ -134,6 +136,7 @@ type CustomSignedExtra = (
134
136
pallet_evm_nonce_tracker:: CheckNonce < Runtime > ,
135
137
domain_check_weight:: CheckWeight < Runtime > ,
136
138
pallet_transaction_payment:: ChargeTransactionPayment < Runtime > ,
139
+ CheckContractCreation ,
137
140
) ;
138
141
139
142
/// Unchecked extrinsic type as expected by this runtime.
@@ -152,6 +155,95 @@ pub type Executive = domain_pallet_executive::Executive<
152
155
AllPalletsWithSystem ,
153
156
> ;
154
157
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
+
155
247
impl fp_self_contained:: SelfContainedCall for RuntimeCall {
156
248
type SignedInfo = H160 ;
157
249
@@ -175,6 +267,11 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall {
175
267
dispatch_info : & DispatchInfoOf < RuntimeCall > ,
176
268
len : usize ,
177
269
) -> 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
+
178
275
match self {
179
276
RuntimeCall :: Ethereum ( call) => {
180
277
// Ensure the caller can pay for the consensus chain storage fee
@@ -199,6 +296,11 @@ impl fp_self_contained::SelfContainedCall for RuntimeCall {
199
296
dispatch_info : & DispatchInfoOf < RuntimeCall > ,
200
297
len : usize ,
201
298
) -> 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
+
202
304
match self {
203
305
RuntimeCall :: Ethereum ( call) => {
204
306
// Withdraw the consensus chain storage fee from the caller and record
@@ -1009,6 +1111,7 @@ fn check_transaction_and_do_pre_dispatch_inner(
1009
1111
pallet_evm_nonce_tracker:: CheckNonce :: from ( extra. 5 . 0 ) ,
1010
1112
extra. 6 ,
1011
1113
extra. 7 ,
1114
+ extra. 8 ,
1012
1115
) ;
1013
1116
1014
1117
custom_extra
0 commit comments