@@ -12,7 +12,7 @@ mod tests {
12
12
use alloy_rpc_types_eth:: BlockTransactionsKind ;
13
13
use futures_util:: StreamExt ;
14
14
use op_alloy_consensus:: OpTypedTransaction ;
15
- use op_alloy_network:: Optimism ;
15
+ use op_alloy_network:: { Optimism , TransactionResponse } ;
16
16
use std:: {
17
17
cmp:: max,
18
18
path:: PathBuf ,
@@ -22,9 +22,14 @@ mod tests {
22
22
use tokio_tungstenite:: connect_async;
23
23
use uuid:: Uuid ;
24
24
25
+ /// Key used by builder
25
26
const BUILDER_PRIVATE_KEY : & str =
26
27
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" ;
27
28
29
+ /// Key used in tests, so builder txs won't affect them
30
+ const CUSTOM_PRIVATE_KEY : & str =
31
+ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" ;
32
+
28
33
#[ tokio:: test]
29
34
#[ cfg( not( feature = "flashblocks" ) ) ]
30
35
async fn integration_test_chain_produces_blocks ( ) -> eyre:: Result < ( ) > {
@@ -97,6 +102,7 @@ mod tests {
97
102
Ok ( ( ) )
98
103
}
99
104
105
+ /// Caution: this test could be brittle to change because of queued pool checks
100
106
#[ tokio:: test]
101
107
#[ cfg( not( feature = "flashblocks" ) ) ]
102
108
async fn integration_test_revert_protection ( ) -> eyre:: Result < ( ) > {
@@ -149,86 +155,128 @@ mod tests {
149
155
MIN_PROTOCOL_BASE_FEE ,
150
156
) ;
151
157
for _ in 0 ..10 {
158
+ // We check queued pool to see if it already has tx we sent earlier
159
+ // We do this in the beginning, because once we insert valid tx the queued tx would move
160
+ // to pending pool, because nonce gap would be fixed.
161
+ let mut queued_hash = None ;
162
+ let mut pool_content = provider. txpool_content ( ) . await ?;
163
+ if !pool_content. queued . is_empty ( ) {
164
+ // We are on the non-first, so we have 1 tx in queue pool
165
+ assert_eq ! (
166
+ pool_content. queued. len( ) ,
167
+ 1 ,
168
+ "Queued pool should contain only 1 transaction"
169
+ ) ;
170
+ queued_hash = Some (
171
+ pool_content
172
+ . queued
173
+ . pop_first ( )
174
+ . unwrap ( )
175
+ . 1
176
+ . pop_first ( )
177
+ . unwrap ( )
178
+ . 1
179
+ . tx_hash ( ) ,
180
+ ) ;
181
+ }
182
+
183
+ let known_wallet = Signer :: try_from_secret ( CUSTOM_PRIVATE_KEY . parse ( ) ?) ?;
152
184
// Get builder's address
153
- let known_wallet = Signer :: try_from_secret ( BUILDER_PRIVATE_KEY . parse ( ) ?) ?;
154
- let builder_address = known_wallet. address ;
185
+ let custom_address = known_wallet. address ;
155
186
// Get current nonce from chain
156
- let nonce = provider. get_transaction_count ( builder_address) . await ?;
157
- // Transaction from builder should succeed
158
- let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
159
- chain_id : 901 ,
160
- nonce,
161
- gas_limit : 210000 ,
162
- max_fee_per_gas : base_fee. into ( ) ,
163
- ..Default :: default ( )
164
- } ) ;
165
- let signed_tx = known_wallet. sign_tx ( tx_request) ?;
166
- let known_tx = provider
167
- . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
168
- . await ?;
169
-
170
- // Create a reverting transaction
171
- let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
172
- chain_id : 901 ,
173
- nonce : nonce + 1 ,
174
- gas_limit : 300000 ,
175
- max_fee_per_gas : base_fee. into ( ) ,
176
- input : hex ! ( "60006000fd" ) . into ( ) , // PUSH1 0x00 PUSH1 0x00 REVERT
177
- ..Default :: default ( )
178
- } ) ;
179
- let signed_tx = known_wallet. sign_tx ( tx_request) ?;
180
- let reverting_tx = provider
181
- . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
182
- . await ?;
183
-
184
- // Create a second reverting transaction
185
- let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
186
- chain_id : 901 ,
187
- nonce : nonce + 2 ,
188
- gas_limit : 300000 ,
189
- max_fee_per_gas : base_fee. into ( ) ,
190
- input : hex ! ( "60006000fd" ) . into ( ) , // PUSH1 0x00 PUSH1 0x00 REVERT
191
- ..Default :: default ( )
192
- } ) ;
193
- let signed_tx = known_wallet. sign_tx ( tx_request) ?;
194
- let reverting_tx_2 = provider
195
- . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
196
- . await ?;
197
-
198
- // Before block we should have 3 txs in pool, because they all valid
187
+ let nonce = provider. get_transaction_count ( custom_address) . await ?;
188
+
189
+ // Send valid tx that must be included in the block
190
+ let valid_tx = {
191
+ let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
192
+ chain_id : 901 ,
193
+ nonce,
194
+ gas_limit : 210000 ,
195
+ max_fee_per_gas : base_fee. into ( ) ,
196
+ ..Default :: default ( )
197
+ } ) ;
198
+ let signed_tx = known_wallet. sign_tx ( tx_request) ?;
199
+ let known_tx = provider
200
+ . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
201
+ . await ?;
202
+ known_tx. tx_hash ( ) . to_owned ( )
203
+ } ;
204
+
205
+ // If we don't have reverting tx in the queued pool we send one. This send occurs only
206
+ // on first cycle iteration
207
+ let revert_tx_1 = if queued_hash. is_none ( ) {
208
+ // Reverting tx that would be removed
209
+ let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
210
+ chain_id : 901 ,
211
+ nonce : nonce + 1 ,
212
+ gas_limit : 300000 ,
213
+ max_fee_per_gas : base_fee. into ( ) ,
214
+ input : hex ! ( "60006000fd" ) . into ( ) , // PUSH1 0x00 PUSH1 0x00 REVERT
215
+ ..Default :: default ( )
216
+ } ) ;
217
+ let signed_tx = known_wallet. sign_tx ( tx_request) ?;
218
+ let reverting_tx = provider
219
+ . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
220
+ . await ?;
221
+ reverting_tx. tx_hash ( ) . to_owned ( )
222
+ } else {
223
+ queued_hash. unwrap ( )
224
+ } ;
225
+
226
+ // We send second reverting tx
227
+ let revert_tx_2 = {
228
+ // Reverting tx that would be places in queue pool, then it would be placed in the
229
+ // pending pool in the next interation (after the nonce gap is fixed be sending
230
+ // valid tx) and removed
231
+ let tx_request = OpTypedTransaction :: Eip1559 ( TxEip1559 {
232
+ chain_id : 901 ,
233
+ nonce : nonce + 2 ,
234
+ gas_limit : 300000 ,
235
+ max_fee_per_gas : base_fee. into ( ) ,
236
+ input : hex ! ( "60006000fd" ) . into ( ) , // PUSH1 0x00 PUSH1 0x00 REVERT
237
+ ..Default :: default ( )
238
+ } ) ;
239
+ let signed_tx = known_wallet. sign_tx ( tx_request) ?;
240
+ let reverting_tx_2 = provider
241
+ . send_raw_transaction ( signed_tx. encoded_2718 ( ) . as_slice ( ) )
242
+ . await ?;
243
+ reverting_tx_2. tx_hash ( ) . to_owned ( )
244
+ } ;
245
+
246
+ // All txs should be in pending pool.
247
+ // 2 cases:
248
+ // - we sent all 3 txs
249
+ // - we sent 2 txs and one got promoted from queue pool
199
250
let pool = provider. txpool_status ( ) . await ?;
200
251
assert_eq ! ( pool. pending, 3 , "all txs should be in pending pool" ) ;
201
252
assert_eq ! ( pool. queued, 0 , "queued pool should be empty" ) ;
202
253
203
254
let block_hash = generator. generate_block ( ) . await ?;
204
255
205
- // TODO: uncomment once reth issue is addressed https://github.com/paradigmxyz/reth/issues/15973
206
256
// After block is produced we will remove one of the reverting txs and place another
207
257
// in queue pool because we have nonce gap
208
- // let pool = provider.txpool_status().await?;
209
- // assert_eq!(pool.pending, 0, "pending pool should be empty");
210
- // assert_eq!(pool.queued, 1, "queued pool should contain 1 tx");
258
+ let pool = provider. txpool_status ( ) . await ?;
259
+ assert_eq ! ( pool. pending, 0 , "pending pool should be empty" ) ;
260
+ assert_eq ! ( pool. queued, 1 , "queued pool should contain 1 tx" ) ;
211
261
212
262
// query the block and the transactions inside the block
213
263
let block = provider
214
264
. get_block_by_hash ( block_hash)
215
265
. await ?
216
266
. expect ( "block" ) ;
217
267
218
- // Verify known transaction is included
268
+ // Verify valid transaction is included
219
269
assert ! (
220
- block
221
- . transactions
222
- . hashes( )
223
- . any( |hash| hash == * known_tx. tx_hash( ) ) ,
270
+ block. transactions. hashes( ) . any( |hash| hash == * valid_tx) ,
224
271
"successful transaction missing from block"
225
272
) ;
226
273
227
- // Verify reverted transactions are NOT included
274
+ // Verify reverting transactions are NOT included
228
275
assert ! (
229
- !block. transactions. hashes( ) . any(
230
- |hash| hash == * reverting_tx. tx_hash( ) || hash == * reverting_tx_2. tx_hash( )
231
- ) ,
276
+ !block
277
+ . transactions
278
+ . hashes( )
279
+ . any( |hash| hash == * revert_tx_1 || hash == * revert_tx_2) ,
232
280
"reverted transaction unexpectedly included in block"
233
281
) ;
234
282
for hash in block. transactions . hashes ( ) {
0 commit comments