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