@@ -118,78 +118,26 @@ where
118118 }
119119 }
120120
121- pub fn validate_self_contained ( & self , origin : & H160 ) -> Option < TransactionValidity > {
121+ pub fn pre_dispatch_self_contained (
122+ & self ,
123+ origin : & H160 ,
124+ ) -> Option < Result < ( ) , TransactionValidityError > > {
122125 if let Call :: transact( transaction) = self {
123- let validate = || {
124- // We must ensure a transaction can pay the cost of its data bytes.
125- // If it can't it should not be included in a block.
126- let mut gasometer = evm:: gasometer:: Gasometer :: new (
127- transaction. gas_limit . low_u64 ( ) ,
128- <T as pallet_evm:: Config >:: config ( ) ,
129- ) ;
130- let transaction_cost = match transaction. action {
131- TransactionAction :: Call ( _) => {
132- evm:: gasometer:: call_transaction_cost ( & transaction. input )
133- }
134- TransactionAction :: Create => {
135- evm:: gasometer:: create_transaction_cost ( & transaction. input )
136- }
137- } ;
138- if gasometer. record_transaction ( transaction_cost) . is_err ( ) {
139- return InvalidTransaction :: Custom (
140- TransactionValidationError :: InvalidGasLimit as u8 ,
141- )
142- . into ( ) ;
143- }
144-
145- if let Some ( chain_id) = transaction. signature . chain_id ( ) {
146- if chain_id != T :: ChainId :: get ( ) {
147- return InvalidTransaction :: Custom (
148- TransactionValidationError :: InvalidChainId as u8 ,
149- )
150- . into ( ) ;
151- }
152- }
153-
154- if transaction. gas_limit >= T :: BlockGasLimit :: get ( ) {
155- return InvalidTransaction :: Custom (
156- TransactionValidationError :: InvalidGasLimit as u8 ,
157- )
158- . into ( ) ;
159- }
160-
161- let account_data = pallet_evm:: Pallet :: < T > :: account_basic ( & origin) ;
162-
163- if transaction. nonce < account_data. nonce {
164- return InvalidTransaction :: Stale . into ( ) ;
165- }
166-
167- let fee = transaction. gas_price . saturating_mul ( transaction. gas_limit ) ;
168- let total_payment = transaction. value . saturating_add ( fee) ;
169- if account_data. balance < total_payment {
170- return InvalidTransaction :: Payment . into ( ) ;
171- }
172-
173- let min_gas_price = T :: FeeCalculator :: min_gas_price ( ) ;
174-
175- if transaction. gas_price < min_gas_price {
176- return InvalidTransaction :: Payment . into ( ) ;
177- }
178-
179- let mut builder = ValidTransactionBuilder :: default ( )
180- . and_provides ( ( origin, transaction. nonce ) )
181- . priority ( transaction. gas_price . unique_saturated_into ( ) ) ;
182-
183- if transaction. nonce > account_data. nonce {
184- if let Some ( prev_nonce) = transaction. nonce . checked_sub ( 1 . into ( ) ) {
185- builder = builder. and_requires ( ( origin, prev_nonce) )
186- }
187- }
188-
189- builder. build ( )
190- } ;
126+ Some ( Pallet :: < T > :: validate_transaction_in_block (
127+ * origin,
128+ & transaction,
129+ ) )
130+ } else {
131+ None
132+ }
133+ }
191134
192- Some ( validate ( ) )
135+ pub fn validate_self_contained ( & self , origin : & H160 ) -> Option < TransactionValidity > {
136+ if let Call :: transact( transaction) = self {
137+ Some ( Pallet :: < T > :: validate_transaction_in_pool (
138+ * origin,
139+ transaction,
140+ ) )
193141 } else {
194142 None
195143 }
@@ -259,9 +207,12 @@ pub mod pallet {
259207 "pre-block transaction signature invalid; the block cannot be built" ,
260208 ) ;
261209
262- Self :: do_transact ( source, transaction) . expect (
210+ Self :: validate_transaction_in_block ( source, & transaction) . expect (
263211 "pre-block transaction verification failed; the block cannot be built" ,
264212 ) ;
213+ Self :: apply_validated_transaction ( source, transaction) . expect (
214+ "pre-block transaction execution failed; the block cannot be built" ,
215+ ) ;
265216 }
266217 }
267218
@@ -287,7 +238,7 @@ pub mod pallet {
287238 Error :: <T >:: PreLogExists ,
288239 ) ;
289240
290- Self :: do_transact ( source, transaction)
241+ Self :: apply_validated_transaction ( source, transaction)
291242 }
292243 }
293244
@@ -418,7 +369,98 @@ impl<T: Config> Pallet<T> {
418369 }
419370 }
420371
421- fn do_transact ( source : H160 , transaction : Transaction ) -> DispatchResultWithPostInfo {
372+ // Common controls to be performed in the same way by the pool and the
373+ // State Transition Function (STF).
374+ // This is the case for all controls except those concerning the nonce.
375+ fn validate_transaction_common (
376+ origin : H160 ,
377+ transaction : & Transaction ,
378+ ) -> Result < U256 , TransactionValidityError > {
379+ // We must ensure a transaction can pay the cost of its data bytes.
380+ // If it can't it should not be included in a block.
381+ let mut gasometer = evm:: gasometer:: Gasometer :: new (
382+ transaction. gas_limit . low_u64 ( ) ,
383+ <T as pallet_evm:: Config >:: config ( ) ,
384+ ) ;
385+ let transaction_cost = match transaction. action {
386+ TransactionAction :: Call ( _) => evm:: gasometer:: call_transaction_cost ( & transaction. input ) ,
387+ TransactionAction :: Create => {
388+ evm:: gasometer:: create_transaction_cost ( & transaction. input )
389+ }
390+ } ;
391+ if gasometer. record_transaction ( transaction_cost) . is_err ( ) {
392+ return Err ( InvalidTransaction :: Custom (
393+ TransactionValidationError :: InvalidGasLimit as u8 ,
394+ )
395+ . into ( ) ) ;
396+ }
397+
398+ if let Some ( chain_id) = transaction. signature . chain_id ( ) {
399+ if chain_id != T :: ChainId :: get ( ) {
400+ return Err ( InvalidTransaction :: Custom (
401+ TransactionValidationError :: InvalidChainId as u8 ,
402+ )
403+ . into ( ) ) ;
404+ }
405+ }
406+
407+ if transaction. gas_limit >= T :: BlockGasLimit :: get ( ) {
408+ return Err ( InvalidTransaction :: Custom (
409+ TransactionValidationError :: InvalidGasLimit as u8 ,
410+ )
411+ . into ( ) ) ;
412+ }
413+
414+ let account_data = pallet_evm:: Pallet :: < T > :: account_basic ( & origin) ;
415+
416+ let fee = transaction. gas_price . saturating_mul ( transaction. gas_limit ) ;
417+ let total_payment = transaction. value . saturating_add ( fee) ;
418+ if account_data. balance < total_payment {
419+ return Err ( InvalidTransaction :: Payment . into ( ) ) ;
420+ }
421+
422+ let min_gas_price = T :: FeeCalculator :: min_gas_price ( ) ;
423+
424+ if transaction. gas_price < min_gas_price {
425+ return Err ( InvalidTransaction :: Payment . into ( ) ) ;
426+ }
427+
428+ Ok ( account_data. nonce )
429+ }
430+
431+ // Controls that must be performed by the pool.
432+ // The controls common with the State Transition Function (STF) are in
433+ // the function `validate_transaction_common`.
434+ fn validate_transaction_in_pool (
435+ origin : H160 ,
436+ transaction : & Transaction ,
437+ ) -> TransactionValidity {
438+ let account_nonce = Self :: validate_transaction_common ( origin, transaction) ?;
439+
440+ if transaction. nonce < account_nonce {
441+ return Err ( InvalidTransaction :: Stale . into ( ) ) ;
442+ }
443+
444+ // The tag provides and requires must be filled correctly according to the nonce.
445+ let mut builder = ValidTransactionBuilder :: default ( )
446+ . and_provides ( ( origin, transaction. nonce ) )
447+ . priority ( transaction. gas_price . unique_saturated_into ( ) ) ;
448+
449+ // In the context of the pool, a transaction with
450+ // too high a nonce is still considered valid
451+ if transaction. nonce > account_nonce {
452+ if let Some ( prev_nonce) = transaction. nonce . checked_sub ( 1 . into ( ) ) {
453+ builder = builder. and_requires ( ( origin, prev_nonce) )
454+ }
455+ }
456+
457+ builder. build ( )
458+ }
459+
460+ fn apply_validated_transaction (
461+ source : H160 ,
462+ transaction : Transaction ,
463+ ) -> DispatchResultWithPostInfo {
422464 let transaction_hash =
423465 H256 :: from_slice ( Keccak256 :: digest ( & rlp:: encode ( & transaction) ) . as_slice ( ) ) ;
424466 let transaction_index = Pending :: < T > :: get ( ) . len ( ) as u32 ;
@@ -565,6 +607,29 @@ impl<T: Config> Pallet<T> {
565607 }
566608 }
567609 }
610+
611+ /// Validate an Ethereum transaction already in block
612+ ///
613+ /// This function must be called during the pre-dispatch phase
614+ /// (just before applying the extrinsic).
615+ pub fn validate_transaction_in_block (
616+ origin : H160 ,
617+ transaction : & ethereum:: TransactionV0 ,
618+ ) -> Result < ( ) , TransactionValidityError > {
619+ let account_nonce = Self :: validate_transaction_common ( origin, transaction) ?;
620+
621+ // In the context of the block, a transaction with a nonce that is
622+ // too high should be considered invalid and make the whole block invalid.
623+ if transaction. nonce > account_nonce {
624+ Err ( TransactionValidityError :: Invalid (
625+ InvalidTransaction :: Future ,
626+ ) )
627+ } else if transaction. nonce < account_nonce {
628+ Err ( TransactionValidityError :: Invalid ( InvalidTransaction :: Stale ) )
629+ } else {
630+ Ok ( ( ) )
631+ }
632+ }
568633}
569634
570635#[ derive( Eq , PartialEq , Clone , sp_runtime:: RuntimeDebug ) ]
0 commit comments