@@ -94,7 +94,7 @@ impl<P: Platform> Executable<P> {
9494 ///
9595 /// - Transactions that fail gracefully (revert or halt) will produce an
9696 /// execution result and state changes. It is up to higher levels of the
97- /// system to decide what to do with such transactions, e.g. whether to
97+ /// system to decide what to do with such transactions, e.g., whether to
9898 /// remove them from the payload or not (see [`RevertProtection`]).
9999 fn execute_transaction < DB > (
100100 tx : Recovered < types:: Transaction < P > > ,
@@ -123,54 +123,63 @@ impl<P: Platform> Executable<P> {
123123 } )
124124 }
125125
126- /// Executes a bundle of transactions and returns the outcome of the execution
127- /// of all transactions in the bundle along with the aggregate of all state
128- /// changes.
126+ /// Executes a bundle of transactions and returns the execution outcome of all
127+ /// transactions in the bundle along with the aggregate of all state changes.
129128 ///
130129 /// Notes:
131130 /// - Bundles that are not eligible for execution in the current block are
132- /// considered invalid and no execution result will be produced.
131+ /// considered invalid, and no execution result will be produced.
133132 ///
134133 /// - All transactions in the bundle are executed in the order in which they
135134 /// were defined in the bundle.
136135 ///
137- /// - Each transaction is executed on the state that was produced by the
138- /// previous transaction in the bundle.
136+ /// - Each transaction is executed on the state produced by the previous
137+ /// transaction in the bundle.
139138 ///
140139 /// - First transaction in the bundle is executed on the state of the
141140 /// checkpoint that we are building on.
142141 ///
143- /// - Transactions that cause EVM errors will invalidate the whole bundle and
144- /// no execution result will be produced (similar behavior to invalid loose
145- /// txs),
146- /// - Unless the transaction that is failing is optional
147- /// [`Bundle::is_optional`]. In that case a new version of the bundle
148- /// will be created by removing the invalid failing transaction through
149- /// [`Bundle::without_transaction`].
142+ /// - Transactions that cause EVM errors will invalidate the bundle, and no
143+ /// execution result will be produced (similar behavior to invalid loose
144+ /// txs). Bundle transaction can be marked optional [`Bundle::is_optional`] ,
145+ /// and invalid outcomes are handled differently:
146+ /// - If the invalid transaction is optional, a new version of the bundle
147+ /// will be created without the invalid transaction by removing it
148+ /// through [`Bundle::without_transaction`].
150149 /// - If removing the invalid optional transaction results in an empty
151150 /// bundle, the bundle will be considered invalid and no execution
152151 /// result will be produced.
153152 ///
154- /// - If a transaction in the bundle fails gracefully (revert or halt):
155- /// - If the bundle allows this tx to fail [`Bundle::is_allowed_to_fail`],
156- /// the bundle will be considered valid and the execution result will be
157- /// produced including this tx. State changes from the failed
158- /// transaction will be included in the aggregate state, e.g gas used,
159- /// nonces incremented, etc. Cleaning up transactions that are allowed
160- /// to fail and are optional from a bundle is beyond the scope of this
161- /// method. This is implemented by higher levels of the system, such as
162- /// the [`RevertProtection`] step in the pipelines API.
163- /// - If the bundle does not allow this tx to fail, but the transaction is
164- /// optional, then it will be removed from the bundle.
165- /// - If the bundle does not allow this tx to fail and the transaction is
166- /// not optional, then the bundle will be considered invalid and no
167- /// execution result will be produced.
153+ /// - Transactions that fail gracefully (revert or halt) and are not optional
154+ /// will invalidate the bundle, and no execution result will be produced.
155+ /// Bundle transaction can be marked as allowed to fail
156+ /// [`Bundle::is_allowed_to_fail`], and failure outcomes are handled
157+ /// differently:
158+ /// - If the bundle allows the failing transaction to fail, the bundle
159+ /// will still be considered valid. The execution result will be
160+ /// produced, including this failed transaction. State changes from the
161+ /// failed transaction will be included in the aggregate state, e.g.,
162+ /// gas used, nonces incremented, etc. Cleaning up transactions that are
163+ /// allowed to fail and are optional from a bundle is beyond the scope
164+ /// of this method. This is implemented by higher levels of the system,
165+ /// such as the [`RevertProtection`] step in the pipelines API.
166+ /// - If the bundle does not allow this failed transaction to fail, but
167+ /// the transaction is optional, then it will be removed from the
168+ /// bundle. The bundle stays valid.
169+ ///
170+ /// See truth table:
171+ /// | success | `allowed_to_fail` | optional | Action |
172+ /// | ------: | :---------------: | :------: | :------ |
173+ /// | true | *don’t care* | *any* | include |
174+ /// | false | true | *any* | include |
175+ /// | false | false | true | discard |
176+ /// | false | false | false | error |
168177 ///
169178 /// - At the end of the bundle execution, the bundle implementation will have
170179 /// a chance to validate any other platform-specific post-execution
171180 /// requirements. For example, the bundle may require that the state after
172181 /// the execution has a certain balance in some account, etc. If this check
173- /// fails, the bundle will be considered invalid and no execution result
182+ /// fails, the bundle will be considered invalid, and no execution result
174183 /// will be produced.
175184 fn execute_bundle < DB > (
176185 bundle : types:: Bundle < P > ,
@@ -196,65 +205,43 @@ impl<P: Platform> Executable<P> {
196205 let mut results = Vec :: with_capacity ( bundle. transactions ( ) . len ( ) ) ;
197206
198207 for transaction in bundle. transactions ( ) {
208+ let tx_hash = * transaction. tx_hash ( ) ;
209+ let optional = bundle. is_optional ( tx_hash) ;
210+ let allowed_to_fail = bundle. is_allowed_to_fail ( tx_hash) ;
211+
199212 let result = evm_config
200213 . evm_with_env ( & mut db, evm_env. clone ( ) )
201214 . transact ( transaction) ;
202215
203216 match result {
204- // valid transaction
205- Ok ( ExecResultAndState { result, state } ) => {
206- if !result. is_success ( ) {
207- match (
208- bundle. is_allowed_to_fail ( * transaction. tx_hash ( ) ) ,
209- bundle. is_optional ( * transaction. tx_hash ( ) ) ,
210- ) {
211- // transaction is not allowed to fail, but it is optional,
212- // we can remove it from the bundle and continue executing the
213- // bundle.
214- ( false , true ) => {
215- discarded. push ( * transaction. tx_hash ( ) ) ;
216- continue ;
217- }
218-
219- // transaction is not allowed to fail and is not optional,
220- // this invalidates the whole bundle and we cannot produce an
221- // execution result.
222- ( false , false ) => {
223- return Err ( ExecutionError :: BundleTransactionReverted (
224- * transaction. tx_hash ( ) ,
225- ) ) ;
226- }
227- // transaction is allowed to fail, include it
228- _ => { }
229- }
230- }
231-
232- // transaction will be included
217+ // Valid transaction or allowed to fail: include it in the bundle
218+ Ok ( ExecResultAndState { result, state } )
219+ if result. is_success ( ) || allowed_to_fail =>
220+ {
233221 results. push ( result) ;
234222 db. commit ( state) ;
235223 }
236- // Invalid transaction
224+ // Optional failing transaction, not allowed to fail
225+ // or optional invalid transaction: discard it
226+ Ok ( _) | Err ( _) if optional => {
227+ discarded. push ( tx_hash) ;
228+ }
229+ // Non-Optional failing transaction, not allowed to fail: invalidate the
230+ // bundle
231+ Ok ( _) => {
232+ return Err ( ExecutionError :: BundleTransactionReverted ( tx_hash) ) ;
233+ }
234+ // Non-Optional invalid transaction: invalidate the bundle
237235 Err ( err) => {
238- if bundle. is_optional ( * transaction. tx_hash ( ) ) {
239- // If the transaction is optional, we can skip it
240- // and continue with the next transaction.
241- discarded. push ( * transaction. tx_hash ( ) ) ;
242- continue ;
243- }
244-
245- return Err ( ExecutionError :: InvalidBundleTransaction (
246- * transaction. tx_hash ( ) ,
247- err,
248- ) ) ;
236+ return Err ( ExecutionError :: InvalidBundleTransaction ( tx_hash, err) ) ;
249237 }
250238 }
251239 }
252240
253- // produce a new bundle without the discarded transactions
254- let mut bundle = bundle;
255- for tx in discarded {
256- bundle = bundle. without_transaction ( tx) ;
257- }
241+ // reduce the bundle by removing discarded transactions
242+ let bundle = discarded
243+ . into_iter ( )
244+ . fold ( bundle, |b, tx| b. without_transaction ( tx) ) ;
258245
259246 // extract all the state changes that were made by executing
260247 // transactions in this bundle.
@@ -299,7 +286,7 @@ impl<P: Platform> Executable<P> {
299286 }
300287}
301288
302- /// Convinience trait that allows all types that can be executed to be used as a
289+ /// Convenience trait that allows all types that can be executed to be used as a
303290/// parameter to the `Checkpoint::apply` method.
304291pub trait IntoExecutable < P : Platform , S = ( ) > {
305292 fn try_into_executable ( self ) -> Result < Executable < P > , RecoveryError > ;
@@ -315,7 +302,7 @@ impl<P: Platform> IntoExecutable<P, Variant<0>> for types::Transaction<P> {
315302 }
316303}
317304
318- /// Transactions from the transaction pool can be converted infalliably into
305+ /// Transactions from the transaction pool can be converted infallibly into
319306/// an executable because the transaction pool discards transactions
320307/// that have invalid signatures.
321308impl < P : Platform > IntoExecutable < P , Variant < 1 > >
@@ -326,7 +313,7 @@ impl<P: Platform> IntoExecutable<P, Variant<1>>
326313 }
327314}
328315
329- /// Signature recovered individual transactions are always infalliably
316+ /// Signature- recovered individual transactions are always infallibly
330317/// convertable into an executable.
331318impl < P : Platform > IntoExecutable < P , Variant < 2 > >
332319 for Recovered < types:: Transaction < P > >
@@ -336,7 +323,7 @@ impl<P: Platform> IntoExecutable<P, Variant<2>>
336323 }
337324}
338325
339- /// Bundles are also convertible into an executable infalliably .
326+ /// Bundles are also convertible into an executable infallibly .
340327/// Signature recovery is part of the bundle assembly logic.
341328impl < P : Platform > IntoExecutable < P , Variant < 3 > > for types:: Bundle < P > {
342329 fn try_into_executable ( self ) -> Result < Executable < P > , RecoveryError > {
@@ -351,7 +338,7 @@ impl<P: Platform> IntoExecutable<P, Variant<4>> for Executable<P> {
351338 }
352339}
353340
354- /// Another checkpoints contents
341+ /// Another checkpoint content
355342impl < P : Platform > IntoExecutable < P , Variant < 5 > > for Checkpoint < P > {
356343 fn try_into_executable ( self ) -> Result < Executable < P > , RecoveryError > {
357344 ( & self ) . try_into_executable ( )
@@ -360,10 +347,10 @@ impl<P: Platform> IntoExecutable<P, Variant<5>> for Checkpoint<P> {
360347
361348impl < P : Platform > IntoExecutable < P , Variant < 6 > > for & Checkpoint < P > {
362349 fn try_into_executable ( self ) -> Result < Executable < P > , RecoveryError > {
363- if let Some ( bundle) = self . as_bundle ( ) {
364- Ok ( Executable :: Bundle ( bundle. clone ( ) ) )
365- } else if let Some ( tx) = self . as_transaction ( ) {
350+ if let Some ( tx) = self . as_transaction ( ) {
366351 Ok ( Executable :: Transaction ( tx. clone ( ) ) )
352+ } else if let Some ( bundle) = self . as_bundle ( ) {
353+ Ok ( Executable :: Bundle ( bundle. clone ( ) ) )
367354 } else {
368355 Err ( RecoveryError :: new ( ) )
369356 }
@@ -387,6 +374,7 @@ impl<P: PlatformWithRpcTypes> IntoExecutable<P, Variant<7>>
387374/// transaction executions that make up this overall result.
388375#[ derive( Debug , Clone ) ]
389376pub struct ExecutionResult < P : Platform > {
377+ /// The executable used to produce this result.
390378 source : Executable < P > ,
391379
392380 /// For transactions this is guaranteed to be a single-element vector,
@@ -399,13 +387,13 @@ pub struct ExecutionResult<P: Platform> {
399387}
400388
401389impl < P : Platform > ExecutionResult < P > {
402- /// Returns the executable that was executed to produce this result.
390+ /// Returns the executable used to produce this result.
403391 pub const fn source ( & self ) -> & Executable < P > {
404392 & self . source
405393 }
406394
407- /// Returns the aggregate state changes that were made by executing
408- /// the transactions in this execution unit.
395+ /// Returns the aggregate state changes made by executing the transactions in
396+ /// this execution unit.
409397 pub const fn state ( & self ) -> & BundleState {
410398 & self . state
411399 }
@@ -420,8 +408,7 @@ impl<P: Platform> ExecutionResult<P> {
420408 self . results . as_slice ( )
421409 }
422410
423- /// Returns individual transactions that were executed as part of this
424- /// execution unit.
411+ /// Returns individual transactions executed as part of this execution unit.
425412 pub fn transactions ( & self ) -> & [ Recovered < types:: Transaction < P > > ] {
426413 self . source ( ) . transactions ( )
427414 }
0 commit comments